Asp.net Core Fundamentals_Part 1
The Startup class
The Startup
class is where:
- Services are registered in
ConfigureServices
and consumed across the app via dependency injection (DI) - Includes a Configure method to create the app’s request processing pipeline.
ConfigureServices
and Configure
are called by the ASP.NET Core runtime when the app starts:
The Startup
class is specified when the app's host is built.
webBuilder.UseStartup<Startup>();
Only the following service types can be injected into the Startup
constructor when using the Generic Host (IHostBuilder):
Multiple Startup
The class whose name suffix matches the current environment is prioritized. If the app is run in the Development environment and includes both a Startup
class and a StartupDevelopment
class, the StartupDevelopment
class is used.
The ConfigureServices method is:
- Called by the host before the
Configure
method to configure the app's services.
The Configure method
The Configure method is used to specify how the app responds to HTTP requests. The request pipeline is configured by adding middleware components to an IApplicationBuilder instance.Hosting creates an IApplicationBuilder
and passes it directly to Configure
.
Each middleware component in the request pipeline is responsible for invoking the next component in the pipeline or short-circuiting the chain, if appropriate.
Extend Startup with startup filters
Use IStartupFilter:
IStartupFilter
is used by ASP.NET Core to add defaults to the beginning of the pipeline without having to make the app author explicitly register the default middleware.
Dependency injection (services)
done
Middleware
The request handling pipeline is composed as a series of middleware components. Each component performs operations on an HttpContext
and either invokes the next middleware in the pipeline or terminates the request.
Host
On startup, an ASP.NET Core app builds a host. The host encapsulates all of the app’s resources, such as:
- An HTTP server implementation
- Middleware components
- Logging
- Dependency injection (DI) services
- Configuration
There are two different hosts:
- .NET Generic Host
- ASP.NET Core Web Host
The .NET Generic Host is recommended. The ASP.NET Core Web Host is available only for backwards compatibility.
The following example creates a .NET Generic Host:
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
The CreateDefaultBuilder
and ConfigureWebHostDefaults
methods configure a host with a set of default options, such as:
- Use Kestrel as the web server and enable IIS integration.
- Load configuration from appsettings.json, appsettings.{Environment Name}.json, environment variables, command line arguments, and other configuration sources.
Servers
ASP.NET Core provides the following server implementations:
- Kestrel is a cross-platform web server. Kestrel is often run in a reverse proxy configuration using IIS/Nginx/Apache(Linux OS). In ASP.NET Core 2.0 or later, Kestrel can be run as a public-facing edge server exposed directly to the Internet.
- IIS HTTP Server is a server for Windows that uses IIS. With this server, the ASP.NET Core app and IIS run in the same process.
- HTTP.sys is a server for Windows that isn’t used with IIS.
Dependency injection in ASP.NET Core
ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.
A dependency is an object that another object depends on.
Code dependencies should be avoided
- To replace
MyDependency
with a different implementation, theIndexModel
class must be modified. - This implementation is difficult to unit test. The app should use a mock or stub
MyDependency
class, which isn't possible with this approach.
Dependency injection addresses these problems through:
- The use of an interface
- The framework takes on the responsibility of creating an instance of the dependency and disposing of it when it’s no longer needed.
Register groups of services with extension methods
The ASP.NET Core framework uses a convention for registering a group of related services. The convention is to use a single Add{GROUP_NAME}
extension method to register all of the services required by a framework feature.
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString(“DefaultConnection”)));
Related groups of registrations can be moved to an extension method to register services.
Service lifetimes
- Transient objects are always different.
- Scoped objects are the same for each request but different across each request.
- Singleton objects are the same for every request.
Entity Framework contexts
By default, Entity Framework contexts are added to the service container using the scoped lifetime because web app database operations are normally scoped to the client request. To use a different lifetime, specify the lifetime by using an AddDbContext overload.
Design services for dependency injection
When designing services for dependency injection:
- Avoid stateful, static classes and members. Avoid creating global state by designing apps to use singleton services instead.
- Avoid direct instantiation of dependent classes within services.
If a class has a lot of injected dependencies, it might be a sign that the class has too many responsibilities and violates the Single Responsibility Principle (SRP). Attempt to refactor the class by moving some of its responsibilities into new classes.
Disposal of services
The container calls Dispose for the IDisposable types it creates. Services resolved from the container should never be disposed by the developer. If a type or factory is registered as a singleton, the container disposes the singleton automatically.
services.AddScoped<Service1>();
services.AddSingleton<Service2>();
Services not created by the service container
services.AddSingleton(new Service1());
- The framework doesn’t dispose of the services automatically.
- The developer is responsible for disposing the services.
Avoid using the service locator pattern. For example, don’t invoke GetService to obtain a service instance when you can use DI instead:
Recommended patterns for multi-tenancy in DI
Orchard Core is an application framework for building modular, multi-tenant applications on ASP.NET Core.
ASP.NET Core Middleware
Middleware is software that’s assembled into an app pipeline to handle requests and responses.
Request delegates handle each HTTP request. Request delegates are configured using Run, Map, and Use extension methods.
Create a middleware pipeline with IApplicationBuilder

Each delegate can perform operations before and after the next delegate. Exception-handling delegates should be called early in the pipeline, so they can catch exceptions that occur in later stages of the pipeline.
Chain multiple request delegates together with Use. The next
parameter represents the next delegate in the pipeline.
Run delegates don’t receive a next
parameter. The first Run
delegate is always terminal and terminates the pipeline.

Middleware order

The Endpoint middleware in the preceding diagram executes the filter pipeline for the corresponding app type — MVC or Razor Pages.

The order is critical for security, performance, and functionality.

Exception/error handling
When the app runs in the Development environment:
- Developer Exception Page Middleware (UseDeveloperExceptionPage) reports app runtime errors.
- Database Error Page Middleware reports database runtime errors.
When the app runs in the Production environment:
- Exception Handler Middleware (UseExceptionHandler) catches exceptions thrown in the following middlewares.
- HTTP Strict Transport Security Protocol (HSTS) Middleware (UseHsts) adds the
Strict-Transport-Security
header.
Endpoint Routing Middleware (UseEndpoints with MapRazorPages) to add Razor Pages endpoints to the request pipeline.
Branch the middleware pipeline
Map
branches the request pipeline based on matches of the given request path.

ASP.NET Core Web Host
ASP.NET Core apps configure and launch a host. The host is responsible for app startup and lifetime management. At a minimum, the host configures a server and a request processing pipeline. The host can also set up logging, dependency injection, and configuration, and IHostedService
implementations.
In a web app, one of the IHostedService
implementations is a web service that starts an HTTP server implementation.
Default builder settings
The CreateDefaultBuilder method:
- Loads host configuration from:
- Environment variables prefixed with
DOTNET_
.
2. Loads app configuration from:
- appsettings.json.
- appsettings.{Environment}.json.
3. Adds the following logging providers:
- Console
- Debug
The ConfigureWebHostDefaults method:
- Loads host configuration from environment variables prefixed with
ASPNETCORE_
. - Sets Kestrel server as the web server.
- Adds Host Filtering middleware.
- Enables IIS integration.
Framework-provided services
The following services are registered automatically:
IHostApplicationLifetime
Inject the IHostApplicationLifetime (formerly IApplicationLifetime
) service into any class to handle post-startup and graceful shutdown tasks.
IHostLifetime
The IHostLifetime implementation controls when the host starts and when it stops.
IHostEnvironment
Inject the IHostEnvironment service into a class to get information about the following settings:
Web server implementations in ASP.NET Core
Kestrel
Kestrel is the default web server specified by the ASP.NET Core project templates.
Use Kestrel:
- By itself as an edge server processing requests directly from a network, including the Internet.
- With a reverse proxy server, such as Internet Information Services (IIS), Nginx, or Apache. A reverse proxy server receives HTTP requests from the Internet and forwards them to Kestrel.
Either hosting configuration — with or without a reverse proxy server — is supported.
ASP.NET Core ships with the following:
- Kestrel server is the default, cross-platform HTTP server implementation.
- IIS HTTP Server is an in-process server for IIS.
- HTTP.sys server - If ASP.NET Core apps are run on Windows, HTTP.sys is an alternative to Kestrel. Kestrel is generally recommended for best performance.
When using IIS or IIS Express, the app either runs:
- In the same process as the IIS worker process (the in-process hosting model) with the IIS HTTP Server. In-process is the recommended configuration.
- In a process separate from the IIS worker process (the out-of-process hosting model) with the Kestrel server.
The ASP.NET Core Module is a native IIS module that handles native IIS requests between IIS and the in-process IIS HTTP Server or Kestrel.
Hosting models
Using in-process hosting, an ASP.NET Core app runs in the same process as its IIS worker process. In-process hosting provides improved performance over out-of-process hosting because requests aren’t proxied over the loopback adapter
Open Web Interface for .NET (OWIN) with ASP.NET Core
ASP.NET Core supports the Open Web Interface for .NET (OWIN). OWIN allows web apps to be decoupled from web servers. It defines a standard way for middleware to be used in a pipeline to handle requests and associated responses.
Server startup
The server is launched when the Integrated Development Environment (IDE) or editor starts the app:
- Visual Studio: Launch profiles can be used to start the app and server with either IIS Express/ASP.NET Core Module or the console.
- Visual Studio Code: The app and server are started by Omnisharp, which activates the CoreCLR debugger.
- Visual Studio for Mac: The app and server are started by the Mono Soft-Mode Debugger.
- When launching the app from a command prompt in the project’s folder, dotnet run launches the app and server (Kestrel and HTTP.sys only)
Configuration in ASP.NET Core
CreateDefaultBuilder provides default configuration for the app in the following order:
Sets the content root to the path returned by GetCurrentDirectory.
Loads host configuration from:
- Environment variables prefixed with
DOTNET_
. - Command-line arguments.
Loads app configuration from:
- appsettings.json.
- appsettings.{Environment}.json.
- User secrets when the app runs in the
Development
environment. - Environment variables.
- Command-line arguments.
Bind hierarchical configuration data using the options pattern
The preferred way to read related configuration values is using the options pattern.
“Position”: {
“Title”: “Editor”,
“Name”: “Joe Smith”
}
public class PositionOptions
{
public const string Position = “Position”;
public string Title { get; set; }
public string Name { get; set; }
}
public void ConfigureServices(IServiceCollection services)
{
services.Configure<PositionOptions>(Configuration.GetSection(
PositionOptions.Position));
services.AddRazorPages();
}
public class Test2Model : PageModel
{
private readonly PositionOptions _options;
public Test2Model(IOptions<PositionOptions> options)
{
_options = options.Value;
}
public ContentResult OnGet()
{
return Content($”Title: {_options.Title} \n” +
$”Name: {_options.Name}”);
}
}
In the preceding code, changes to the JSON configuration file after the app has started are not read. To read changes after the app has started, use IOptionsSnapshot.
Using the default configuration, the appsettings.json and appsettings.Environment
.json files are enabled with reloadOnChange: true.
Related groups of registrations can be moved to an extension method to register services.
Security and user secrets
Configuration data guidelines:
- Never store passwords or other sensitive data in configuration provider code or in plain text configuration files. The Secret Manager tool can be used to store secrets in development.
- Don’t use production secrets in development or test environments.
- Specify secrets outside of the project so that they can’t be accidentally committed to a source code repository.
By default, the user secrets configuration source is registered after the JSON configuration sources.
Environment variables
Using the default configuration, the EnvironmentVariablesConfigurationProvider loads configuration from environment variable key-value pairs after reading appsettings.json, appsettings.Environment
.json, and user secrets.
The :
separator doesn't work with environment variable hierarchical keys on all platforms. __
, the double underscore, is:
Call AddEnvironmentVariables with a string to specify a prefix for environment variables:

- Environment variables set with the
MyCustomPrefix_
prefix override the default configuration providers. This includes environment variables without the prefix. - The prefix is stripped off when the configuration key-value pairs are read.
Naming of environment variables
Environment variable names reflect the structure of an appsettings.json file.


Environment variables set in launchSettings.json
Environment variables set in launchSettings.json override those set in the system environment.
GetSection and GetChildren methods are available to isolate sections and children of a section in the configuration data.
launch.json/launchSettings.json are tooling configuration files for the Development environment,
Options pattern in ASP.NET Core
The options pattern uses classes to provide strongly typed access to groups of related settings. When configuration settings are isolated by scenario into separate classes, the app adheres to two important software engineering principles:
- The Interface Segregation Principle (ISP) or Encapsulation: Scenarios (classes) that depend on configuration settings depend only on the configuration settings that they use.
- Separation of Concerns: Settings for different parts of the app aren’t dependent or coupled to one another.
Options interfaces
- Does not support:
- Reading of configuration data after the app has started.
- Named options
- Is registered as a Singleton and can be injected into any service lifetime.
- Is useful in scenarios where options should be recomputed on every request.
Named options support using IConfigureNamedOptions
Named options:
- Are useful when multiple configuration sections bind to the same properties.
- Are case sensitive.


Options validation
Options validation enables option values to be validated.


IValidateOptions for complex validation
Environments
To determine the runtime environment, ASP.NET Core reads from the following environment variables:
- DOTNET_ENVIRONMENT
ASPNETCORE_ENVIRONMENT
when ConfigureWebHostDefaults is called. TheASPNETCORE_ENVIRONMENT
value overridesDOTNET_ENVIRONMENT
.
The following code:
- Calls UseDeveloperExceptionPage when
ASPNETCORE_ENVIRONMENT
is set toDevelopment
. - Calls UseExceptionHandler when the value of
ASPNETCORE_ENVIRONMENT
is set toStaging
,Production
, orStaging_2
.
The launchSettings.json file:
- Is only used on the local development machine.
- Is not deployed.
- contains profile settings.
The value of commandName
can specify the web server to launch.
IISExpress
: Launches IIS Express.IIS
: No web server launched. IIS is expected to be available.Project
: Launches Kestrel.
Logging in .NET Core and ASP.NET Core
- .NET Core supports a logging API that works with a variety of built-in and third-party logging providers..NET Core supports a logging API that works with a variety of built-in and third-party logging providers.
Call CreateDefaultBuilder, which adds the following logging providers:
- Console
- Debug
- EventSource
- EventLog: Windows only
To override the default set of logging providers added by Host.CreateDefaultBuilder
, call ClearProviders
and add the required logging providers.


Create logs
To create logs, use an ILogger<TCategoryName> object from dependency injection (DI).

Log category
When an ILogger
object is created, a category is specified. That category is included with each log message created by that instance of ILogger
.
Log event ID
Each log can specify an event ID. The sample app uses the MyLogEvents
class to define event IDs:
An event ID associates a set of events. For example, all logs related to displaying a list of items on a page might be 1001.
Log message template
Each log API uses a message template. The message template can contain placeholders for which arguments are provided.
Default log level
If the default log level is not set, the default log level value is Information
.
Log scopes
A scope can group a set of logical operations. This grouping can be used to attach the same data to each log that’s created as part of a set. For example, every log created as part of processing a transaction can include the transaction ID.
A scope:
- Is an IDisposable type that’s returned by the BeginScope method.
- Lasts until it’s disposed.

“IncludeScopes”: true, // Required to use Scopes.
Built-in logging providers
ASP.NET Core includes the following logging providers as part of the shared framework:
Event Source
The EventSource
provider writes to a cross-platform event source with the name Microsoft-Extensions-Logging
. On Windows, the provider uses ETW.
dotnet trace tooling
Use the dotnet trace tooling to collect a trace from an app:
Trace for performance analysis utility
Perfview
Use the PerfView utility to collect and view logs. There are other tools for viewing ETW logs, but PerfView provides the best experience for working with the ETW events emitted by ASP.NET Core.
PerfView is a free performance-analysis tool that helps isolate CPU and memory-related performance issues. It is a Windows tool, but it also has some support for analyzing data collected on Linux machines.
Azure Application Insights
The Microsoft.Extensions.Logging.ApplicationInsights provider package writes logs to Azure Application Insights. Application Insights is a service that monitors a web app and provides tools for querying and analyzing the telemetry data. If you use this provider, you can query and analyze your logs by using the Application Insights tools.
The logging provider is included as a dependency of Microsoft.ApplicationInsights.AspNetCore, which is the package that provides all available telemetry for ASP.NET Core. If you use this package, you don’t have to install the provider package.
Using a third-party framework (NLog) is similar to using one of the built-in providers:
- Add a NuGet package to your project.
- Call an
ILoggerFactory
extension method provided by the logging framework.
No asynchronous logger methods
Logging should be so fast that it isn’t worth the performance cost of asynchronous code. If a logging data store is slow, don’t write to it directly. Consider writing the log messages to a fast store initially, then moving them to the slow store later. For example, when logging to SQL Server, don’t do so directly in a Log
method, since the Log
methods are synchronous. Instead, synchronously add log messages to an in-memory queue and have a background worker pull the messages out of the queue to do the asynchronous work of pushing data to SQL Server.
Apply log filter rules in code
The preferred approach for setting log filter rules is by using Configuration.

Routing in ASP.NET Core
Routing is registered in the middleware pipeline in Startup.Configure
.
Routing uses a pair of middleware, registered by UseRouting and UseEndpoints (The MapGet
method is used to define an endpoint)

The following example shows routing with health checks and authorization:

- Middleware can run before
UseRouting
to modify the data that routing operates upon eg: UseRewriter, UseHttpMethodOverride, or UsePathBase.
2. Middleware can run between UseRouting
and UseEndpoints
- Usually inspects metadata to understand the endpoints and often makes security decisions, as done by
UseAuthorization
andUseCors
. - The combination of middleware and metadata allows configuring policies per-endpoint.