Migrate from ASP.NET Core 5.0 to 6.0
This article explains how to update an existing ASP.NET Core 5.0 project to ASP.NET Core 6.0. For instructions on how to migrate from ASP.NET Core 3.1 to ASP.NET Core 6.0, see Migrate from ASP.NET Core 3.1 to 6.0.
Prerequisites
- Visual Studio 2022 with the ASP.NET and web development workload.
- .NET 6.0 SDK
Update .NET SDK version in global.json
If you rely upon a global.json
file to target a specific .NET SDK version, update the version
property to the .NET 6.0 SDK version that's installed. For example:
{
"sdk": {
- "version": "5.0.100"
+ "version": "6.0.100"
}
}
Update the target framework
Update the project file's Target Framework Moniker (TFM) to net6.0
:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
- <TargetFramework>net5.0</TargetFramework>
+ <TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>
Update package references
In the project file, update each Microsoft.AspNetCore.*
and Microsoft.Extensions.*
package reference's Version
attribute to 6.0.0 or later. For example:
<ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="5.0.3" />
- <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="5.0.0" />
+ <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="6.0.0" />
+ <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="6.0.0" />
</ItemGroup>
New hosting model
The new .NET 6 minimal hosting model for ASP.NET Core apps requires only one file and a few lines of code. Apps migrating to 6.0 don't need to use the new minimal hosting model. For more information, see Apps migrating to 6.0 don't need to use the new minimal hosting model in the following section.
The following code from the ASP.NET Core empty template creates an app using the new minimal hosting model:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
The minimal hosting model:
- Significantly reduces the number of files and lines of code required to create an app. Only one file is needed with four lines of code.
- Unifies
Startup.cs
andProgram.cs
into a singleProgram.cs
file. - Uses top-level statements to minimize the code required for an app.
- Uses global
using
directives to eliminate or minimize the number ofusing
statement lines required.
The following code displays the Startup.cs
and Program.cs
files from an ASP.NET Core 5 Web App template (Razor Pages) with unused using
statements removed:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
// Unused usings removed.
namespace WebAppRPv5
{
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.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
}
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
// Unused usings removed.
namespace WebAppRPv5
{
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>();
});
}
}
In ASP.NET Core 6, the preceding code is replaced by the following:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
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 preceding ASP.NET Core 6 sample shows how:
- ConfigureServices is replaced with
WebApplication.Services
. builder.Build()
returns a configured WebApplication to the variableapp
. Configure is replaced with configuration calls to same services usingapp
.
Detailed examples of migrating ASP.NET Core 5 Startup
code to ASP.NET Core 6 using the minimal hosting model are provided later in this document.
There are a few changes to the other files generated for the Web App template:
Index.cshtml
andPrivacy.cshtml
have the unusedusing
statements removed.RequestId
inError.cshtml
is declared as a nullable reference type (NRT):
- public string RequestId { get; set; }
+ public string? RequestId { get; set; }
- Log level defaults have changed in
appsettings.json
andappsettings.Development.json
:
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
+ "Microsoft.AspNetCore": "Warning"
In the preceding ASP.NET Core template code, "Microsoft": "Warning"
has been changed to "Microsoft.AspNetCore": "Warning"
. This change results in logging all informational messages from the Microsoft
namespace except Microsoft.AspNetCore
. For example, Microsoft.EntityFrameworkCore
is now logged at the informational level.
For more details on the new hosting model, see the Frequently asked questions section. For more information on the adoption of NRTs and .NET compiler null-state analysis, see the Nullable reference types (NRTs) and .NET compiler null-state static analysis section.
Apps migrating to or using 6.0 and later don't need to use the new minimal hosting model
Using Startup
and the Generic Host used by the ASP.NET Core 3.1 and 5.0 templates is fully supported.
Use Startup with the new minimal hosting model
ASP.NET Core 3.1 and 5.0 apps can use their Startup
code with the new minimal hosting model. Using Startup
with the minimal hosting model has the following advantages:
- No hidden reflection is used to call the
Startup
class. - Asynchronous code can be written because the developer controls the call to
Startup
. - Code can be written that interleaves
ConfigureServices
andConfigure
.
One minor limitation in using Startup
code with the new minimal hosting model is that to inject a dependency into Configure
, the service in Program.cs
must be manually resolved.
Consider the following code generated by the ASP.NET Core 3.1 or 5.0 Razor Pages template:
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>();
});
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
The preceding code migrated to the new minimal hosting model:
using Microsoft.AspNetCore.Builder;
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);
var app = builder.Build();
startup.Configure(app, app.Environment);
app.Run();
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (!env.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
In the preceding code, the if (env.IsDevelopment())
block is removed because in development mode, the developer exception page middleware is enabled by default. For more information, see Differences between the ASP.NET Core 5 and 6 hosting models in the next section.
When using a custom dependency injection (DI) container, add the following highlighted code:
using Autofac;
using Autofac.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);
// 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();
using Autofac;
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
// Using a custom DI container
public void ConfigureContainer(ContainerBuilder builder)
{
// Configure custom container.
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (!env.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
});
}
}
When using the minimal hosting model, the endpoint routing middleware wraps the entire middleware pipeline, therefore there's no need to have explicit calls to UseRouting
or UseEndpoints
to register routes. UseRouting
can still be used to specify where route matching happens, but UseRouting
doesn't need to be explicitly called if routes should be matched at the beginning of the middleware pipeline.
In the following code, the calls to UseRouting
and UseEndpoints
are removed from Startup
. MapRazorPages
is called in Program.cs
:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (!env.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
//app.UseRouting();
//app.UseEndpoints(endpoints =>
//{
// endpoints.MapRazorPages();
//});
}
}
using Microsoft.AspNetCore.Builder;
var builder = WebApplication.CreateBuilder(args);
var startup = new Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);
var app = builder.Build();
startup.Configure(app, app.Environment);
app.MapRazorPages();
app.Run();
When using Startup
with the new minimal hosting model, keep in mind the following difference:
Program.cs
controls the instantiation and lifetime of theStartup
class.- Any additional services injected into the
Configure
method need to be manually resolved by theProgram
class.
Differences between the ASP.NET Core 5 and 6 hosting models
- In development mode, the developer exception page middleware is enabled by default.
- The app name defaults to the entry point assembly's name:
Assembly.GetEntryAssembly().GetName().FullName
. When using the WebApplicationBuilder in a library, explicitly change the app name to the library's assembly to allow MVC's application part discovery to work. See Change the content root, app name, and environment in this document for detailed instructions. - The endpoint routing middleware wraps the entire middleware pipeline, therefore there's no need to have explicit calls to
UseRouting
orUseEndpoints
to register routes.UseRouting
can still be used to specify where route matching happens, butUseRouting
doesn't need to be explicitly called if routes should be matched at the beginning of the middleware pipeline. - The pipeline is created before any IStartupFilter runs, therefore exceptions caused while building the pipeline aren't visible to the
IStartupFilter
call chain. - Some tools, such as EF migrations, use
Program.CreateHostBuilder
to access the app'sIServiceProvider
to execute custom logic in the context of the app. These tools have been updated to use a new technique to execute custom logic in the context of the app. Entity Framework Migrations is an example of a tool that usesProgram.CreateHostBuilder
in this way. We're working to make sure tools are updated to use the new model. - Unlike the
Startup
class, the minimal host doesn't automatically configure a DI scope when instantiating the service provider. For contexts where a scope is required, it is necessary to invoke IServiceScope with IServiceScopeFactory.CreateScope to instantiate a new scope. For more information, see how to resolve a service at app startup. - It's not possible to change any host settings such as app name, environment, or the content root after the creation of the WebApplicationBuilder. For detailed instructions on changing host settings, see Customize
IHostBuilder
orIWebHostBuilder
. The following highlighted APIs throw an exception:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
// WebHost
try
{
builder.WebHost.UseContentRoot(Directory.GetCurrentDirectory());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
builder.WebHost.UseEnvironment(Environments.Staging);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
builder.WebHost.UseSetting(WebHostDefaults.ApplicationKey, "ApplicationName2");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
builder.WebHost.UseSetting(WebHostDefaults.ContentRootKey, Directory.GetCurrentDirectory());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
builder.WebHost.UseSetting(WebHostDefaults.EnvironmentKey, Environments.Staging);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
// Host
try
{
builder.Host.UseEnvironment(Environments.Staging);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
// TODO: This does not throw
builder.Host.UseContentRoot(Directory.GetCurrentDirectory());
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
The
Startup
class can't be used fromWebApplicationBuilder.Host
orWebApplicationBuilder.WebHost
. The following highlighted code throws an exception:var builder = WebApplication.CreateBuilder(args); try { builder.Host.ConfigureWebHostDefaults(webHostBuilder => { webHostBuilder.UseStartup<Startup>(); }); } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } builder.Services.AddRazorPages(); var app = builder.Build();
var builder = WebApplication.CreateBuilder(args); try { builder.WebHost.UseStartup<Startup>(); } catch (Exception ex) { Console.WriteLine(ex.Message); throw; } builder.Services.AddRazorPages(); var app = builder.Build();
The IHostBuilder implementation on WebApplicationBuilder (
WebApplicationBuilder.Host
), doesn't defer execution of the ConfigureServices, ConfigureAppConfiguration, or ConfigureHostConfiguration methods. Not deferring execution allows code using WebApplicationBuilder to observe changes made to theIServiceCollection
andIConfiguration
. The following example only addsService1
as anIService
: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(); // Displays Service1 only. Console.WriteLine(app.Services.GetRequiredService<IService>()); app.Run(); class Service1 : IService { } class Service2 : IService { } interface IService { }
In the preceding code, the builder.Host.ConfigureServices
callback gets called inline rather than being deferred until builder.Build
is called. This means that Service1
gets added to the IServiceCollection
before Service2
and results in Service1
being resolved for IService
.
Building libraries for ASP.NET Core 6
The existing .NET ecosystem built extensibility around IServiceCollection, IHostBuilder, and IWebHostBuilder. These properties are available on WebApplicationBuilder as Services
, Host
, and WebHost
.
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 ensures that your middleware, route handler, or other extensibility points continue to work across different hosting models.
Frequently asked questions (FAQ)
Is the new minimal hosting model less capable?
No. The new hosting model is functionally equivalent for 98% of scenarios supported by
IHostBuilder
and theIWebHostBuilder
. There are some advanced scenarios that require specific workarounds onIHostBuilder
, but we expect those to be extremely rare.Is the generic hosting model deprecated?
No. The generic hosting model is an alternative model that is supported indefinitely. The generic host 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. The new hosting model is the preferred way to host new apps using .NET 6 and later, but you aren't forced to change the project layout in existing apps. This means apps can upgrade from .NET 5 to .NET 6 by changing the target framework in the project file from
net5.0
tonet6.0
. For more information, see the Update the target framework section in this article. However, we recommend apps migrate to the new hosting model to take advantage of new features only available to the new hosting model.Do I have to use top-level statements?
No. The new project templates all use top-level statements, but the new hosting APIs can be used in any .NET 6 app to host a webserver or web app.
Where do I put state that was stored as fields in my
Program
orStartup
class?We strongly recommend using dependency injection (DI) to flow state in ASP.NET Core apps.
There are two approaches to storing state outside of DI:
Store the state in another class. Storing in a class assumes a static state that can be accessed from anywhere in the app.
Use the
Program
class generated by top level statements to store state. UsingProgram
to store state is the semantic approach:var builder = WebApplication.CreateBuilder(args); ConfigurationValue = builder.Configuration["SomeKey"] ?? "Hello"; var app = builder.Build(); app.MapGet("/", () => ConfigurationValue); app.Run(); partial class Program { public static string? ConfigurationValue { get; private set; } }
What if I was using a custom dependency injection container?
Custom DI containers are supported. For an example, see Custom dependency injection (DI) container.
Do
WebApplicationFactory
andTestServer
still work?Yes.
WebApplicationFactory<TEntryPoint>
is the way to test the new hosting model. For an example, see Test withWebApplicationFactory
orTestServer
.
Blazor
After following the guidance earlier in this article to update an app to 6.0, adopt specific features by following the links in What's new in ASP.NET Core 6.0.
To adopt all of the new 6.0 features for Blazor apps, we recommend the following process:
- Create a new 6.0 Blazor project from one of the Blazor project templates. For more information, see Tooling for ASP.NET Core Blazor.
- Move the app's components and code to the 6.0 app making modifications to adopt the new 6.0 features.
Migrating SPA projects
Migrating Angular apps from SPA extensions
Migrating React apps from SPA extensions
See Migrating React applications from Spa Extensions in this GitHub issue
Update Docker images
For apps using Docker, update your Dockerfile FROM
statements and scripts. Use a base image that includes the ASP.NET Core 6.0 runtime. Consider the following docker pull
command difference between ASP.NET Core 5.0 and 6.0:
- docker pull mcr.microsoft.com/dotnet/aspnet:5.0
+ docker pull mcr.microsoft.com/dotnet/aspnet:6.0
See GitHub issue Breaking Change: Default console logger format set to JSON.
Changes to the ASP.NET Core Razor SDK
The Razor compiler now leverages the new source generators feature to generate compiled C# files from the Razor views and pages in a project. In previous versions:
- The compilation relied on the
RazorGenerate
andRazorCompile
targets to produce the generated code. These targets are no longer valid. In .NET 6, both code generation and compilation are supported by a single call to the compiler.RazorComponentGenerateDependsOn
is still supported to specify dependencies that are required before the build runs. - A separate Razor assembly,
AppName.Views.dll
, was generated that contained the compiled view types in an application. This behavior has been deprecated and a single assemblyAppName.dll
is produced that contains both the app types and the generated views. - The app types in
AppName.Views.dll
were public. In .NET 6, the app types are inAppName.dll
but areinternal sealed
. Apps doing type discover onAppName.Views.dll
won't be able to do type discover onAppName.dll
. The following shows the API change:
- public class Views_Home_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
+ internal sealed class Views_Home_Index : global::Microsoft.AspNetCore.Mvc.Razor.RazorPage<dynamic>
Make the following changes:
- The following properties are no longer applicable with the single-step compilation model.
RazorTargetAssemblyAttribute
RazorTargetName
EnableDefaultRazorTargetAssemblyInfoAttributes
UseRazorBuildServer
GenerateRazorTargetAssemblyInfo
GenerateMvcApplicationPartsAssemblyAttributes
For more information, see Razor compiler no longer produces a Views assembly.
Project templates use Duende Identity Server
Project templates now use Duende Identity Server.
Important
Duende Identity Server is an open source product with a reciprocal license agreement. If you plan to use Duende Identity Server in production, you might be required to obtain a commercial licence from Duende Software and pay a license fee. For more information, see Duende Software: Licenses.
To learn how to use Microsoft Azure Active Directory for ASP.NET Core Identity, see Identity (dotnet/aspnetcore GitHub repository).
Add a DbSet<Key>
property named Keys
to every IdentityDbContext
to satisfy a new requirement from the updated version of IPersistedGrantDbContext
. The keys are required as part of the contract with Duende Identity Server's stores.
public DbSet<Key> Keys { get; set; }
Note
Existing migrations must be recreated for Duende Identity Server.
Code samples migrated to ASP.NET Core 6.0
Code samples migrated to the new minimal hosting model in 6.0
Review breaking changes
See the following resources:
- Identity: Default Bootstrap version of UI changed
- Breaking changes for migration from version 5.0 to 6.0: Includes ASP.NET Core and Entity Framework Core.
- Announcements GitHub repository (aspnet/Announcements,
6.0.0
label): Includes breaking and non-breaking information.
Nullable reference types (NRTs) and .NET compiler null-state static analysis
ASP.NET Core project templates use nullable reference types (NRTs), and the .NET compiler performs null-state static analysis. These features were released with C# 8 and are enabled by default for apps generated using ASP.NET Core 6.0 (C# 10) or later.
The .NET compiler's null-state static analysis warnings can either serve as a guide for updating a documentation example or sample app locally or be ignored. Null-state static analysis can be disabled by setting Nullable
to disable
in the app's project file, which we only recommend for documentation examples and sample apps if the compiler warnings are distracting while learning about .NET. We don't recommend disabling null-state checking in production projects.
For more information on NRTs, the MSBuild Nullable
property, and updating apps (including #pragma
guidance), see the following resources in the C# documentation:
- Nullable reference types
- Nullable reference types (C# reference)
- Learn techniques to resolve nullable warnings
- Update a codebase with nullable reference types to improve null diagnostic warnings
- Attributes for null-state static analysis
- ! (null-forgiving) operator (C# reference)
ASP.NET Core Module (ANCM)
If the ASP.NET Core Module (ANCM) wasn't a selected component when Visual Studio was installed or if a prior version of the ANCM was installed on the system, download the latest .NET Core Hosting Bundle Installer (direct download) and run the installer. For more information, see Hosting Bundle.
Application name change
In .NET 6, WebApplicationBuilder normalizes the content root path to end with a DirectorySeparatorChar. Most apps migrating from HostBuilder or WebHostBuilder won't have the same app name because they aren't normalized. For more information, see SetApplicationName
Additional resources
ASP.NET Core