DEV Community

Siddharth
Siddharth

Posted on • Updated on

Dotting Your i's and Crossing Your t's: .NET Configuration

Configuring .NET apps can be confusing with all the different options available. We have different types of apps like Web APIs, Background Service and Console app etc. which can run on different .NET versions or environments and setting them up with correct configurations can be overwhelming.

I want to share what I learned that helped me understand how all the different configurations fit together and make sense of it all.

Before we start let's first understand .NET project SDK which is a base for different types of configuration.

.NET project SDK

.NET Core, .NET 5 and later projects are associated with a SDK (Software Development Kit). It is responsible for compiling, packing and publishing code. SDK contains .NET CLI, runtime and libraries.

We can target SDK in project file to configure different types of .NET apps.

<Project Sdk="Microsoft.NET.Sdk">
  ...
</Project>
Enter fullscreen mode Exit fullscreen mode

Available SDKs

ID Description
Microsoft.NET.Sdk The .NET SDK. This is the base SDK for all other SDKs
Microsoft.NET.Sdk.Web The .NET Web SDK
Microsoft.NET.Sdk.BlazorWebAssembly The .NET Blazor WebAssembly SDK
Microsoft.NET.Sdk.Razor The .NET Razor SDK
Microsoft.NET.Sdk.Worker The .NET Worker Service SDK
Microsoft.NET.Sdk.WindowsDesktop The .NET Desktop SDK

ASP.NET Core apps

ASP.NET Core is composed of several building blocks. These blocks can be configured to suit the requirements and shape the overall configuration of the application. We are going to look at some of the important blocks.

  • Host
  • Dependency Injection
  • Middlewares
  • Configuration
  • Environments
  • Logging
  • HttpContext
  • Routing
  • Server

Host

The Host, also referred as Host builder or builder, is the first step in configuration. It is used to configure app and its services. Host contains all resources of an app:

  • An HTTP server implementation
  • Middleware components
  • Logging
  • Dependency injection (DI) services
  • Configuration

There are three types of hosts you can choose which depends on the .NET project SDK:

  • ASP.NET Core Web Host
    • Exists only for backward compatibility
    • Supports .NET Core 2.x Apps
    • Uses WebHost.CreateDefaultBuilder() to create builder
  • .NET Generic Host
    • Available in all .NET SDKs
    • Supports ASP.NET Core 3.x and .NET 5 Apps
    • Uses Host.CreateDefaultBuilder() to create builder
    • More info: .NET Generic Host
  • .NET WebApplication Host also known as Minimal Host
    • Simplified version of .NET Generic Host
    • It provides some of the default configuration and hence significantly reduces the number of files and lines of code to configure an app.
    • Available only in Microsoft.NET.Sdk.Web
    • Supports .NET 6 Apps
    • Uses WebApplication.CreateBuilder() to create builder
    • More info: .NET Web Host

The following example configures apps and services using .NET Generic host:

public class Program
{
    public static void Main(string[] args)
    {
        // create host and run app
        CreateHostBuilder(args)
            .ConfigureHostConfiguration(config => config.SetBasePath(Directory.GetCurrentDirectory()))
            .Build()
            .Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

public class Startup
{
    public IConfiguration Configuration { get; }
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    // configure services
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped(typeof(IGitHubService), typeof(GitHubService));
        services.AddScoped(typeof(ISlackService), typeof(SlackService));
    }

    // configure app
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
    }
}
Enter fullscreen mode Exit fullscreen mode

Same configuration but with WebApplication host:

// create host
var builder = WebApplication.CreateBuilder(args);

// configure services
builder.Services.AddScoped(typeof(IGitHubService), typeof(GitHubService));
builder.Services.AddScoped(typeof(ISlackService), typeof(SlackService))

// configure app
var app = builder.Build();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();

// runs app after configuration
app.RunAsync();
Enter fullscreen mode Exit fullscreen mode

Dependency Injection (services)

ASP.NET Core includes dependency injection (DI) that makes configured services available throughout an app. Services are added to the DI container using WebApplicationBuilder.Services (builder.Services). When the builder is instantiated, many framework-provided services are added by default.

Example

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddScoped(typeof(IContactRepository), typeof(ContactRepository));

var app = builder.Build();
Enter fullscreen mode Exit fullscreen mode

As services are add to DI container, we can resolve these services using constructor.

public class Contact
{
    public readonly IContactRepository _contactRepository;

    public Contact(IContactRepository contactRepository)
    {
        _contactRepository = contactRepository;
    }

    public Contact Get(int id)
        => _contactRepository.Get(id);
}
Enter fullscreen mode Exit fullscreen mode

Middleware

In simple terms, middleware is a set of tools that connects different parts of an application and allows them to work together. When the application gets a request, the middleware receives it first and directs it to the right place in the application.

In the configuration, we can add series of middleware in request pipeline. These middleware are chained together. In even of an incoming request, each middleware in sequence -

  • Chooses whether to pass the request to the next middleware.
  • It can perform its operation before and after the next middleware.

How middleware works

By convention, a middleware is invoked by an extension method starts with "Use" keyword. Sequence is the order middleware are defined. "Run" or "RunAsync" method in the last terminates the request.

// create host
var builder = WebApplication.CreateBuilder(args);

// configure services
builder.Services.AddScoped(typeof(IContactRepository), typeof(ContactRepository));

// configure app
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
}

app.UseHttpsRedirection();
app.UseAuthorization();

// custom middleware
app.Use(async (context, next) =>
{
    // Do work that can write to the Response.
    await next.Invoke();
    // Do logging or other work that doesn't write to the Response.
});

// terminates the request
app.Run();
Enter fullscreen mode Exit fullscreen mode

From here on things are easy if you understand SDKs, Hosts, DI & Middleware. Let's check rest of the building blocks quickly -

Configuration

ASP.NET Core apps provides configuration framework. It get settings in key-value pairs. It has many built-in configuration providers which gets settings/configurations from different sources:

  • Settings files, such as appsettings.json
  • Environment variables
  • Azure Key Vault
  • Azure App Configuration
  • Command-line arguments
  • Custom providers, installed or created
  • Directory files
  • In-memory .NET objects

You can use host/builder to configuration from multiple sources.

var builder = WebApplication.CreateBuilder(args);

// Services
var settings = builder.Configuration.GetSection("Settings");
builder.Services.Configure<Settings>(settings);

var app = builder.Build();

// Configure
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();


// Terminates request
await app.RunAsync();
Enter fullscreen mode Exit fullscreen mode

Environments

Set the ASPNETCORE_ENVIRONMENT environment variable to your environment name and access it using Host Builder. By default Development, Staging and Production values are supported .NET configuration.

ASP.NET Core reads that environment variable at app startup and stores the value in an IWebHostEnvironment implementation. This implementation is available anywhere in an app via dependency injection (DI).

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.Run();
Enter fullscreen mode Exit fullscreen mode

Logging

ASP.NET Core supports variety of built-in and third-party logging providers:

  • Console
  • Debug
  • Event Tracing on Windows
  • Windows Event Log
  • TraceSource
  • Azure App Service
  • Azure Application Insights

When you create a building using WebApplication.CreateBuilder it adds Console, Debug, EventSource and EventLog by default. You can reconfigure logging providers using Host builder.

var builder = WebApplication.CreateBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddConsole();

var app = builder.Build();
app.Run();
Enter fullscreen mode Exit fullscreen mode

Final thoughts

If we rethink the structure of the ASP.NET core building blocks it looks something like this -

- .NET project SDK
  - Host (builder)
    - Dependency Injection (builder.Services)
    - Configuration (builder.Configuration)
    - Logging (builder.Logging)
    - App (builder.Build())
      - Middlewares (app.Use{ExtensionMethod})
      - Environments (app.Environment)
Enter fullscreen mode Exit fullscreen mode

I hope this helps to clear confusion. Thanks for reading!


References

Top comments (0)