# Migration to ASP.NET Core in .NET 6

- [WebApplication and WebApplicationBuilder](#webapplication-and-webapplicationbuilder)
- [Differences in the hosting model](#differences-in-the-hosting-model)
- [Building libraries for ASP.NET Core](#building-libraries-for-aspnet-core)
- [FAQ](#faq)
- [Cheatsheet](#cheatsheet)

## WebApplication and WebApplicationBuilder

.NET 6 introduces a new hosting model for ASP.NET Core applications. This model is streamlined and reduces the amount of 
boilerplate code required to get a basic ASP.NET Core application up and running. 

```C#
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World");

app.Run();
```

This model unifies `Startup.cs` and `Program.cs` into a single file experience that takes advantage of top level statements to remove any boilerplate. There should be a mostly mechanical translation from .NET 5 projects using a `Startup` class to the new hosting model:

**Program.cs (.NET 5)**

```C#
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>();
            });
}
```

**Startup.cs (.NET 5)**

```C#
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.MapRazorPages();
    }
}
```

**Program.cs (.NET 6)**

```C#
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthorization();

app.MapRazorPages();

app.Run();
```

The above shows that `ConfigureServices(IServiceCollection)` can be configured using `WebApplicationBuilder.Services` and `Configure(IApplicationBuilder...)` can be configured by using `WebApplication`.

## Differences in the hosting model

- The developer exception page middleware is enabled when the environment is `Development`.
- The application name always defaults to the entry point assembly's name `Assembly.GetEntryAssembly().GetName().FullName`.
When using the `WebApplicationBuilder` in a library, you will need to explicitly change the application name to the library's
assembly to allow MVC's application part discovery to work (finding controllers, views etc) (see the [Cheatsheet](#changing-the-content-root-application-name-and-environment) for instructions on how to do this).
- The endpoint routing middleware wraps the entire middleware pipeline. This means there's no need to have explicit calls to `UseEndpoints` to register routes. `UseRouting` can still be used to move where route matching happens.
- The final pipeline is created before any `IStartupFilter` runs. This means that exceptions caused while building the
main pipeline won't be visible to the `IStartupFilter` call chain.
- Some tools (like EF migrations) use `Program.CreateHostBuilder` to access the application's `IServiceProvider` to execute custom logic in the context of the application, these tools have been updated to use a new technique to achieve the same thing. We will work with the ecosystem to make sure tools are all updated to use the new model.
- It is not possible to change any host settings (application name, environment or the content root) after the creation
of the `WebApplicationBuilder` (see the [Cheatsheet](#changing-the-content-root-application-name-and-environment) for instructions on how to do this). The following APIs will throw an exception:

    **WebHost**

    ```csharp
    builder.WebHost.UseContentRoot(Directory.GetCurrentDirectory());
    builder.WebHost.UseEnvironment(Environments.Staging);

    builder.WebHost.UseSetting(WebHostDefaults.ApplicationKey, "ApplicationName2");
    builder.WebHost.UseSetting(WebHostDefaults.ContentRootKey, Directory.GetCurrentDirectory());
    builder.WebHost.UseSetting(WebHostDefaults.EnvironmentKey, Environments.Staging);
    ```

    **Host**

    ```csharp
    builder.Host.UseEnvironment(Environments.Staging);
    builder.Host.UseContentRoot(Directory.GetCurrentDirectory());
    ```

- It is not possible to use the `Startup` class via the `WebApplicationBuilder.Host` or `WebApplicationBuilder.WebHost`. The following will throw an exception:
    ```csharp
    var builder = WebApplication.CreateBuilder(args);
    builder.Host.ConfigureWebHostDefaults(webHostBuilder =>
    {
        webHostBuilder.UseStartup<Startup>();
    });
    ```

    OR

    ```csharp
    var builder = WebApplication.CreateBuilder(args);
    builder.WebHost.UseStartup<Startup>();
    ```
- The `IHostBuilder` implementation on `WebApplicationBuilder` (`WebApplicationBuilder.Host`), does not defer execution of `ConfigureServices`, `ConfigureAppConfiguration` or `ConfigureHostConfiguration` methods. This allows code using `WebApplicationBuilder` to observe changes made to the `IServiceCollection` and `IConfiguration`. The below example will only add `Service1` as an `IService`.

    ```csharp
    using Microsoft.Extensions.DependencyInjection.Extensions;

    var builder = WebApplication.CreateBuilder(args);

    builder.Host.ConfigureServices(services =>
    {
        services.TryAddSingleton<IService, Service1>();
    });

    builder.Services.TryAddSingleton<IService, Service2>();

    var app = builder.Build();

    // This will print Service1
    Console.WriteLine(app.Services.GetRequiredService<IService>());

    app.Run();

    class Service1 : IService
    {

    }

    class Service2 : IService
    {

    }

    interface IService
    {

    }
    ```

## Building libraries for ASP.NET Core

The existing .NET ecosystem has built extensibility around `IServiceCollection`, `IHostBuilder` and `IWebHostBuilder`. These properties are available on the `WebApplicationBuilder` as `Services`, `Host` and `WebHost`. 

The `WebApplication` implements both `Microsoft.AspNetCore.Builder.IApplicationBuilder` and `Microsoft.AspNetCore.Routing.IEndpointRouteBuilder`.

We expect library authors to continue targeting `IHostBuilder`, `IWebHostBuilder`, `IApplicationBuilder` and `IEndpointRouteBuilder` when building ASP.NET Core specific components. This will ensure that your middleware, route handler, or other extensibility points continue to work across different hosting models.

## FAQ

### Is the new hosting model less capable

No, it should be functionally equivalent for 98% to what you can do with the `IHostBuilder` and the `IWebHostBuilder`. There are
more advanced scenarios (the 2%) that will require specific knobs on `IHostBuilder` but we expect those to be extremely rare.

### Is the generic hosting model dead/deprecated?

No, it's not. It's an alternative model that will keep working forever. The generic host still underpins the new hosting model
and is still the primary way to host worker-based applications.

### Do I have to migrate to the new hosting model

No, you don't have to. It's the preferred way to host ASP.NET Core applications from .NET 6 and onwards but you aren't forced to change your project layout. This means you can upgrade from .NET 5 to .NET 6.0 by changing the target framework in your project file from `net5.0` to `net6.0`.

### Do I have to use top-level statements?

The new project templates all use top-level statements, but these new hosting APIs can be used in any .NET 6 application to host
a webserver/web application.

### Where do I put state that was stored as fields in my Program/Startup class?

There are 2 solutions to this problem:
1. You can store the state on another class. Assuming this was static state that you were accessing from anywhere in the application.
2. There's a `Program` class generated by top level statements that you can put this state on if you wish to keep that
semantic.

This is an example of #2:

**.NET 5**

```C#
public class Startup
{
    public static string ConfigurationValue { get; private set; }

    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;

        ConfigurationValue = Configuration["SomeValue"];
    }

    public IConfiguration Configuration { get; }

    // More configuration here
}
```

**.NET 6**

```C#
var builder = WebApplication.CreateBuilder(args);

ConfigurationValue = builder.Configuration["SomeValue"];

var app = builder.Build();

app.Run();

partial class Program
{
    public static string ConfigurationValue { get; private set; }
}
```

This would make it possible to use `Program.ConfigurationValue` in your .NET 6 application.

**NOTE: We recommend using dependency injection to flow state in your ASP.NET Core applications.**

### Does WebApplicationFactory/TestServer still work?

`WebApplicationFactory<TEntryPoint>` is the way to test the new hosting model. See the [Cheatsheet](#testing-with-webapplicationfactorytestserver) for an example.


### What if I was using a custom dependency injection container?

This is still supported, see the [Cheatsheet](#custom-dependency-injection-container) for an example.

### I like the Startup class; can I keep it?

Yes, you can. Here's a shim you can use to keep it working as is with the new hosting model:

**Program.cs**

```C#
var builder = WebApplication.CreateBuilder(args);

var startup = new Startup(builder.Configuration);

startup.ConfigureServices(builder.Services);

// Uncomment if using a custom DI container
// builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());
// builder.Host.ConfigureContainer<ContainerBuilder>(startup.ConfigureContainer);

var app = builder.Build();

startup.Configure(app, app.Environment);

app.Run();
```

**Startup.cs**

```C#
class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {

    }

//  Uncomment if using a custom DI container
//  public void ConfigureContainer(ContainerBuilder builder)
//  {
//  }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
    {

    }
}
```

There are a few differences here:

- You control the instantiation and lifetime of the `Startup` class.
- Any additional services injected into the `Configure` method need to be manually resolved by your `Program` class.

## Cheatsheet

### Adding middleware

**.NET 5**

```csharp
public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseStaticFiles();
    }
}
```

**.NET 6**

```csharp
var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.UseStaticFiles();

app.Run();
```

### Adding routes

**.NET 5**

```csharp
public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/", () => "Hello World");
        });
    }
}
```

**.NET 6**

In .NET 6, routes can be added directly to the `WebApplication` without an explicit call to `UseEndpoints`.

```csharp
var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/", () => "Hello World");

app.Run();
```

**NOTE: Routes added directly to the `WebApplication` will execute at the end of the pipeline.**

### Changing the content root, application name and environment

**.NET 5**

```csharp
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseEnvironment(Environments.Staging)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>()
                      .UseSetting(WebHostDefaults.ApplicationKey, typeof(Program).Assembly.FullName);
        });
```

**.NET 6**

```csharp
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
    ApplicationName = typeof(Program).Assembly.FullName,
    ContentRootPath = Directory.GetCurrentDirectory(),
    EnvironmentName = Environments.Staging
});

Console.WriteLine($"Application Name: {builder.Environment.ApplicationName}");
Console.WriteLine($"Environment Name: {builder.Environment.EnvironmentName}");
Console.WriteLine($"ContentRoot Path: {builder.Environment.ContentRootPath}");

var app = builder.Build();
```

### Adding configuration providers

**.NET 5**

```csharp
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureAppConfiguration(config =>
        {
            config.AddIniFile("appsettings.ini");
        })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });
```

**.NET 6**

```csharp
var builder = WebApplication.CreateBuilder(args);

builder.Configuration.AddIniFile("appsettings.ini");

var app = builder.Build();
```

### Adding logging providers

**.NET 5**

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

**.NET 6**

```csharp
var builder = WebApplication.CreateBuilder(args);

// Configure JSON logging to the console
builder.Logging.AddJsonConsole();

var app = builder.Build();
```

### Adding services

**.NET 5**

```csharp
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Add the memory cache services
        services.AddMemoryCache();

        // Add a custom scoped service
        services.AddScoped<ITodoRepository, TodoRepository>();
    }
}
```

**.NET 6**

```csharp
var builder = WebApplication.CreateBuilder(args);

// Add the memory cache services
builder.Services.AddMemoryCache();

// Add a custom scoped service
builder.Services.AddScoped<ITodoRepository, TodoRepository>();

var app = builder.Build();
```

### Customizing the IHostBuilder

**.NET 5**

```csharp
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureHostOptions(o => o.ShutdownTimeout = TimeSpan.FromSeconds(30));
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });
```

Existing extension methods on `IHostBuilder` can be accessed using the `Host` property.

**.NET 6**

```csharp
var builder = WebApplication.CreateBuilder(args);

// Wait 30 seconds for graceful shutdown
builder.Host.ConfigureHostOptions(o => o.ShutdownTimeout = TimeSpan.FromSeconds(30));

var app = builder.Build();
```

### Customizing the IWebHostBuilder

**.NET 5**

```csharp
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            // Change the HTTP server implemenation to be HTTP.sys based
            webBuilder.UseHttpSys()
                      .UseStartup<Startup>();
        });
```

**.NET 6**

Existing extension methods on `IWebHostBuilder` can be accessed using the `WebHost` property.

```csharp
var builder = WebApplication.CreateBuilder(args);

// Change the HTTP server implemenation to be HTTP.sys based
builder.WebHost.UseHttpSys();

var app = builder.Build();
```

### Changing the web root

By default, the web root is relative to the content root in the `wwwroot` folder. This is where the static files
middleware expects to find static files. You can change this by using the `UseWebRoot` method on the `WebHost` property:

**.NET 5**

```csharp
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            // Look for static files in webroot
            webBuilder.UseWebRoot("webroot")
                      .UseStartup<Startup>();
        });
```

**.NET 6**

```csharp
var builder = WebApplication.CreateBuilder(args);

// Look for static files in webroot
builder.WebHost.UseWebRoot("webroot");

var app = builder.Build();
```

### Custom dependency injection container

This example uses [Autofac](https://autofac.readthedocs.io/en/latest/integration/aspnetcore.html)

**.NET 5**

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

```csharp
public class Startup
{
    public void ConfigureContainer(ContainerBuilder containerBuilder)
    {
    }
}
```

**.NET 6**

```csharp
var builder = WebApplication.CreateBuilder(args);

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());

// Register your own things directly with Autofac here. Don't
// call builder.Populate(), that happens in AutofacServiceProviderFactory
// for you.
builder.Host.ConfigureContainer<ContainerBuilder>(builder => builder.RegisterModule(new MyApplicationModule()));

var app = builder.Build();
```

### Accessing additional services

**.NET 5**

In `Startup.Configure` you can inject any service added via the `IServiceCollection`.

```csharp
public class Startup
{
    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IService, Service>();
    }

    // Anything added to the service collection can be injected into Configure
    public void Configure(IApplicationBuilder app, 
                          IWebHostEnvironment env,
                          IHostApplicationLifetime lifetime,
                          IService service,
                          ILogger<Startup> logger)
    {
        lifetime.ApplicationStarted.Register(() => 
            logger.LogInformation($"The application {env.ApplicationName} started in we injected {service}"));
    }
}
```

**.NET 6**

In .NET 6, there are a few common services available as top level properties on `WebApplication` and additional services
need to be manually resolved from the `IServiceProvider` via `WebApplication.Services`.

```csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IService, Service>();

var app = builder.Build();

IService service = app.Services.GetRequiredService<IService>();
ILogger logger = app.Logger;
IHostApplicationLifetime lifetime = app.Lifetime;
IWebHostEnvironment env = app.Environment;

lifetime.ApplicationStarted.Register(() =>
    logger.LogInformation($"The application {env.ApplicationName} started in we injected {service}"));

app.Run();
```

### Testing with WebApplicationFactory/TestServer

In the below samples, the test project uses `TestServer` and `WebApplicationFactory`. These ship as separate packages that need to explicit referenced:

**WebApplicationFactory**

```xml
<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="{Version}" />
</ItemGroup>
```

**TestServer**

```xml
<ItemGroup>
  <PackageReference Include="Microsoft.AspNetCore.TestHost" Version="{Version}" />
</ItemGroup>
```

This sample is using xUnit and `IHelloService` will be shared between both examples:

```csharp
public interface IHelloService
{
    string HelloMessage { get; }
}

public class HelloService : IHelloService
{
    public string HelloMessage => "Hello World";
}
```

**.NET 5**

```csharp
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<IHelloService, HelloService>();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IHelloService helloService)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseRouting();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/", async context =>
            {
                await context.Response.WriteAsync(helloService.HelloMessage);
            });
        });
    }
}
```

**With TestServer**

```csharp
[Fact]
public async Task HelloWorld()
{
    using var host = Host.CreateDefaultBuilder()
        .ConfigureWebHostDefaults(builder =>
        {
            // Use the test server and point to the application's startup
            builder.UseTestServer()
                    .UseStartup<WebApplication1.Startup>();
        })
        .ConfigureServices(services =>
        {
            // Replace the service
            services.AddSingleton<IHelloService, MockHelloService>();
        })
        .Build();

    await host.StartAsync();

    var client = host.GetTestClient();

    var response = await client.GetStringAsync("/");

    Assert.Equal("Test Hello", response);
}

class MockHelloService : IHelloService
{
    public string HelloMessage => "Test Hello";
}
```

**With WebApplicationFactory**

```csharp
[Fact]
public async Task HelloWorld()
{
    var application = new WebApplicationFactory<Program>()
        .WithWebHostBuilder(builder =>
        {
            builder.ConfigureServices(services =>
            {
                services.AddSingleton<IHelloService, MockHelloService>();
            });
        });

    var client = application.CreateClient();

    var response = await client.GetStringAsync("/");

    Assert.Equal("Test Hello", response);
}

class MockHelloService : IHelloService
{
    public string HelloMessage => "Test Hello";
}
```

**.NET 6**

```csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<IHelloService, HelloService>();

var app = builder.Build();

var helloService = app.Services.GetRequiredService<IHelloService>();

app.MapGet("/", async context =>
{
    await context.Response.WriteAsync(helloService.HelloMessage);
});

app.Run();
```

In .NET 6, `WebApplicationFactory<TEntryPoint>` is used to test application using new hosting model. The compiler produces an `internal Program` class applications that use top level statements. We need to make this available to the test project by using `InternalsVisibleTo`. This can be done using the project file or in any other .cs file:

**MyProject.csproj**

```xml
<ItemGroup>
    <InternalsVisibleTo Include="MyTestProject" />
</ItemGroup>
```

OR

```C#
[assembly: InternalsVisibleTo("MyTestProject")]
```

The other solution is to make the `Program` class public. You can do this with top level statements by defining a `public partial Program` class anywhere in the project (or in `Program.cs`):

**Program.cs**

```csharp
var builder = WebApplication.CreateBuilder(args);

// ... Wire up services and routes etc

app.Run();

public partial class Program { }
```

```csharp
[Fact]
public async Task HelloWorld()
{
    var application = new WebApplicationFactory<Program>()
        .WithWebHostBuilder(builder =>
        {
            builder.ConfigureServices(services =>
            {
                services.AddSingleton<IHelloService, MockHelloService>();
            });
        });

    var client = application.CreateClient();

    var response = await client.GetStringAsync("/");

    Assert.Equal("Test Hello", response);
}

class MockHelloService : IHelloService
{
    public string HelloMessage => "Test Hello";
}
```

The .NET 5 version and .NET 6 version with the WebApplicationFactory are identical. This is by design.