Skip to content

Instantly share code, notes, and snippets.

@javafun
Forked from davidfowl/.NET6Migration.md
Created April 11, 2024 02:02
Show Gist options
  • Save javafun/56e9f0ae91ae40906dc4111f61eee940 to your computer and use it in GitHub Desktop.
Save javafun/56e9f0ae91ae40906dc4111f61eee940 to your computer and use it in GitHub Desktop.

Revisions

  1. @davidfowl davidfowl revised this gist Sep 25, 2021. 1 changed file with 36 additions and 0 deletions.
    36 changes: 36 additions & 0 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -161,6 +161,42 @@ of the `WebApplicationBuilder` (see the [Cheatsheet](#changing-the-content-root-
    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

  2. @davidfowl davidfowl revised this gist Sep 22, 2021. 1 changed file with 6 additions and 6 deletions.
    12 changes: 6 additions & 6 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -180,20 +180,20 @@ more advanced scenarios (the 2%) that will require specific knobs on `IHostBuild
    ### 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.
    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?
    ### 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
    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's 2 solutions to this problem:
    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.
    @@ -250,9 +250,9 @@ This would make it possible to use `Program.ConfigurationValue` in your .NET 6 a

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

    ### I like the Startup class, can I keep it?
    ### 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:
    Yes, you can. Here's a shim you can use to keep it working as is with the new hosting model:

    **Program.cs**

  3. @davidfowl davidfowl revised this gist Sep 22, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -189,7 +189,7 @@ No, you don't have to. It's the preferred way to host ASP.NET Core applications
    ### 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 applcation.
    a webserver/web application.

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

  4. @davidfowl davidfowl revised this gist Sep 22, 2021. 1 changed file with 55 additions and 0 deletions.
    55 changes: 55 additions & 0 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -186,6 +186,61 @@ and is still the primary way to host worker based applications.

    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 applcation.

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

    There's 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.
  5. @davidfowl davidfowl revised this gist Sep 21, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -20,7 +20,7 @@ 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 mechanial translation from .NET 5 projects using a `Startup` class to the new hosting model:
    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)**

  6. @davidfowl davidfowl revised this gist Sep 21, 2021. 1 changed file with 1 addition and 5 deletions.
    6 changes: 1 addition & 5 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -94,11 +94,7 @@ builder.Services.AddRazorPages();

    var app = builder.Build();

    if (app.Environment.IsDevelopment())
    {
    app.UseDeveloperExceptionPage();
    }
    else
    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.
  7. @davidfowl davidfowl revised this gist Sep 20, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -197,7 +197,7 @@ No, you don't have to. It's the preferred way to host ASP.NET Core applications

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

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

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

  8. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 18 additions and 0 deletions.
    18 changes: 18 additions & 0 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -616,6 +616,24 @@ 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
  9. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -201,7 +201,7 @@ This is still supported works, see the [Cheatsheet](#custom-dependency-injection

    ### 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:
    Yes you can. Here's a shim you can use to keep it working as is with the new hosting model:

    **Program.cs**

  10. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -195,7 +195,7 @@ No, you don't have to. It's the preferred way to host ASP.NET Core applications
    `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 DI container?
    ### What if I was using a custom dependency injection container?

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

  11. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 11 additions and 24 deletions.
    35 changes: 11 additions & 24 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -176,43 +176,30 @@ We expect library authors to continue targeting `IHostBuilder`, `IWebHostBuilder

    ## FAQ

    **Is the new hosting model less capable**
    ### 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?**
    ### 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**
    ### 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 if you have built up features around the `Startup` class.
    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`.

    **Does WebApplicationFactory/TestServer still work?**
    ### Does WebApplicationFactory/TestServer still work?

    `WebApplicationFactory<TEntryPoint>` is the way to test the new hosting model with the test server.
    `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 DI container?**
    ### What if I was using a custom DI container?

    That still works, here's an example that uses Autofac
    This is still supported works, see the [Cheatsheet](#custom-dependency-injection-container) for an example.

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

    **I like the Startup class, can I keep it?**
    ### 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:

    @@ -267,8 +254,8 @@ class Startup

    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.cs`.
    - 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

  12. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 4 additions and 1 deletion.
    5 changes: 4 additions & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -122,12 +122,15 @@ The above shows that `ConfigureServices(IServiceCollection)` can be configured u
    ## 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`. The following APIs will throw an exception:
    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**

  13. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 20 additions and 1 deletion.
    21 changes: 20 additions & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -127,7 +127,26 @@ The above shows that `ConfigureServices(IServiceCollection)` can be configured u
    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`.
    of the `WebApplicationBuilder`. 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);
  14. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -789,4 +789,4 @@ class MockHelloService : IHelloService
    }
    ```

    The .NET 5 version and .NET 6 version with the WebApplicationFactory is identical. This is by design.
    The .NET 5 version and .NET 6 version with the WebApplicationFactory are identical. This is by design.
  15. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 34 additions and 2 deletions.
    36 changes: 34 additions & 2 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -605,7 +605,7 @@ lifetime.ApplicationStarted.Register(() =>
    app.Run();
    ```

    ### Testing with TestServer
    ### Testing with WebApplicationFactory/TestServer

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

    @@ -651,6 +651,8 @@ public class Startup
    }
    ```

    **With TestServer**

    ```csharp
    [Fact]
    public async Task HelloWorld()
    @@ -684,6 +686,34 @@ class MockHelloService : IHelloService
    }
    ```

    **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
    @@ -757,4 +787,6 @@ class MockHelloService : IHelloService
    {
    public string HelloMessage => "Test Hello";
    }
    ```
    ```

    The .NET 5 version and .NET 6 version with the WebApplicationFactory is identical. This is by design.
  16. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -125,7 +125,7 @@ The above shows that `ConfigureServices(IServiceCollection)` can be configured u
    - 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 grab 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.
    - 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`.
    - It is not possible to use the `Startup` class via the `WebApplicationBuilder.Host` or `WebApplicationBuilder.WebHost`. The following will throw an exception:
  17. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 5 additions and 5 deletions.
    10 changes: 5 additions & 5 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -594,12 +594,12 @@ builder.Services.AddSingleton<IService, Service>();

    var app = builder.Build();

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

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

    app.Run();
  18. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -159,7 +159,7 @@ We expect library authors to continue targeting `IHostBuilder`, `IWebHostBuilder
    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?**
    **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.
  19. @davidfowl davidfowl revised this gist Sep 19, 2021. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -730,7 +730,6 @@ var builder = WebApplication.CreateBuilder(args);
    app.Run();

    // This will expose the Program class
    public partial class Program { }
    ```

  20. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 3 additions and 4 deletions.
    7 changes: 3 additions & 4 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -146,7 +146,7 @@ of the `WebApplicationBuilder`.

    ## Building libraries for ASP.NET Core

    The existing .NET ecosystem has built extensibility around `IServiceCollection`, `IHostBuilder` and `IWebHostBuilder`. These properties are exposed on the `WebApplicationBuilder` as `Services`, `Host` and `WebHost`.
    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`.

    @@ -703,8 +703,7 @@ app.MapGet("/", async context =>
    app.Run();
    ```

    In .NET 6, `WebApplicationFactory<TEntryPoint>` is used to test application using new hosting model. Top level programs
    expose an `internal` `Program` class and we need to expose this to the test project by using `InternalsVisibleTo`. This can be done using the project file or in any other .cs file:
    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**

    @@ -720,7 +719,7 @@ OR
    [assembly: InternalsVisibleTo("MyTestProject")]
    ```

    The other technique for exposing the `Program` class is by making it public. You can do this with top level statements by defining a `public partial Program` class anywhere in the project (or in `Program.cs`):
    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**

  21. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,7 @@
    # Migration to ASP.NET Core in .NET 6

    - [WebApplication and WebApplicationBuilder](#webapplication-and-webapplicationbuilder)
    - [Behavior differences in the hosting model](#behavior-differences-in-the-hosting-model)
    - [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)
  22. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 15 additions and 0 deletions.
    15 changes: 15 additions & 0 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -720,6 +720,21 @@ OR
    [assembly: InternalsVisibleTo("MyTestProject")]
    ```

    The other technique for exposing the `Program` class is by making it 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();

    // This will expose the Program class
    public partial class Program { }
    ```

    ```csharp
    [Fact]
    public async Task HelloWorld()
  23. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -408,10 +408,10 @@ public class Startup
    public void ConfigureServices(IServiceCollection services)
    {
    // Add the memory cache services
    builder.Services.AddMemoryCache();
    services.AddMemoryCache();

    // Add a custom scoped service
    builder.Services.AddScoped<ITodoRepository, TodoRepository>();
    services.AddScoped<ITodoRepository, TodoRepository>();
    }
    }
    ```
  24. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 145 additions and 8 deletions.
    153 changes: 145 additions & 8 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -80,10 +80,7 @@ public class Startup

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
    endpoints.MapRazorPages();
    });
    app.MapRazorPages();
    }
    }
    ```
    @@ -115,10 +112,7 @@ app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints =>
    {
    endpoints.MapRazorPages();
    });
    app.MapRazorPages();

    app.Run();
    ```
    @@ -314,6 +308,8 @@ 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**
    @@ -608,3 +604,144 @@ lifetime.ApplicationStarted.Register(() =>

    app.Run();
    ```

    ### Testing with TestServer

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

    ```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";
    }
    ```

    **.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. Top level programs
    expose an `internal` `Program` class and we need to expose this 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")]
    ```

    ```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";
    }
    ```
  25. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 20 additions and 2 deletions.
    22 changes: 20 additions & 2 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -125,12 +125,30 @@ app.Run();

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

    ## Behavior differences in the hosting model
    ## Differences in the hosting model

    - The developer exception page middleware is enabled when the environment is `Development`.
    - 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 grab 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`.
    - 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>();
    ```

    ## Building libraries for ASP.NET Core

    @@ -589,4 +607,4 @@ lifetime.ApplicationStarted.Register(() =>
    logger.LogInformation($"The application {env.ApplicationName} started in we injected {service}"));

    app.Run();
    ```
    ```
  26. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 52 additions and 1 deletion.
    53 changes: 52 additions & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -233,7 +233,7 @@ class Startup

    There are a few differences here:

    - No more reflection magic to call the Startup class. You control the instantiation and lifetime.
    - 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.cs`.

    ## Cheatsheet
    @@ -539,3 +539,54 @@ builder.Host.ConfigureContainer<ContainerBuilder>(builder => builder.RegisterMod

    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();

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

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

    app.Run();
    ```
  27. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 58 additions and 58 deletions.
    116 changes: 58 additions & 58 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -238,6 +238,64 @@ There are a few differences here:

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

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

    **.NET 5**
    @@ -481,61 +539,3 @@ builder.Host.ConfigureContainer<ContainerBuilder>(builder => builder.RegisterMod

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

    ## 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();
    ```
  28. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 58 additions and 0 deletions.
    58 changes: 58 additions & 0 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -481,3 +481,61 @@ builder.Host.ConfigureContainer<ContainerBuilder>(builder => builder.RegisterMod

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

    ## 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();
    ```
  29. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    # Migration to ASP.NET Core 6
    # Migration to ASP.NET Core in .NET 6

    - [WebApplication and WebApplicationBuilder](#webapplication-and-webapplicationbuilder)
    - [Behavior differences in the hosting model](#behavior-differences-in-the-hosting-model)
  30. @davidfowl davidfowl revised this gist Sep 18, 2021. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions .NET6Migration.md
    Original file line number Diff line number Diff line change
    @@ -2,6 +2,7 @@

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