Introduction to Startup.cs in .NET Core

Introduction to Startup.cs in .NET Core

In this post, we take a look at the Startup class, its importance in .NET Core project configuration.

kndb's photo
kndb

Published on Jul 28, 2021

3 min read

TL;DR

In Startup.cs we configure classes(a.k.a. services), middleware and routes for our .NET Core application.

Quick Glance

Startup.cs is configured using the WebHostBuilder.UseStartup extension method inside Program.cs. Since this extension method uses generics, we could always create our own class and use that instead.

public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }

        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .ConfigureWebHostDefaults(webBuilder =>
                {
                    // we map our startup class here
                    webBuilder.UseStartup<Startup>(); 
                });
    }

A typical Startup.cs for a WebAPI project would look like so

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

We can see that it houses 2 methods

  1. ConfigureServices
  2. Configure

ConfigureServices

The ConfigureServices method is where we configure our classes, define their lifetimes. Once configured, the classes can be used throughout the application. This method is optional, meaning if there are no extra classes to be configured we could entirely remove it.

We can use the built-in IoC container provided by the framework or use other popular options like Autofac to help us with the DI.

Autofac is loaded with features required for complex DI setup and resolution, for most applications the built-in IoC container should do the job.

The DI framework's job is to manage and dispense instances in the application.

In this post we will take a glimpse of how to use the built-in IoC container (full implementation is omitted for brevity)

Lets assume we have a Multiplication class which exposes a Calculate method

public class Multiplication
{
    public long Calculate(int x, int y)
    {
          // return
    }
}

A MathController class which takes Multiplication class in its constructor(constructor injection)

[Route("math")]
public class MathController : ControllerBase
{
    private readonly Multiplication _multiply;

    public MathController(Multiplication multiply)
    {
        _multiply= multiply;
    }

    [HttpGet("multiply")]
    public async Task GetMultiplicationResult(int x, int y)
    {
        var result = _multiply.Calculate(x,y);
        Console.WriteLine(result);
    }
}

And we setup our Multiplication class inside ConfigureServices method like so

public void ConfigureServices(IServiceCollection services)
{
        services.AddScoped<Multiplication>();
 }

As soon as we receive a GET call to ~/math/multiply with x and y as query parameters. The DI framework would create an instance of Multiplication class and serve the same instance for the whole API request.

What does "the whole API request" mean?
Lets assume, to process the request the control had to go through multiple classes, and all those classes had a dependency on Multiplication.

HashNode-Page-2 (1).png The DI framework would make sure to use the same Multiplication instance(here obj 1) throughout the API request.

A scope could span across whole API requests, it could even be a code block, the user(client) can define what the scope is.

Configure

The Configure method is where we configure the request pipeline for our application. The request pipeline contains a series of components called middleware, where each middleware performs some operation on the HttpContext and either invokes the next middleware in the pipeline or terminates the request.

The Configure method is invoked after all services are setup.

In our Startup.cs code snippet above, request flows sequentially

  1. UseHttpsRedirection() -> adds a middleware for redirecting HTTP requests to HTTPS
  2. UseRouting() -> adds a middleware that matches request to an endpoint
  3. UseAuthorization() -> adds a middleware that enables endpoint authorization
  4. UseEndpoints() -> adds a middleware that executes the matched endpoint

After a request is processed data flows in the reverse direction of how middlewares were setup.

HashNode-Page-3 (7).png

Thank you for going through this post. I hope you found it useful. Cheers!

Additional resources

  1. Book recommendation on dependency injection 👌👌
  2. Official documentation on .NET Core DI framework https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-3.0