Migrate from ASP.NET Core in .NET 8 to ASP.NET Core in .NET 9

This article explains how to update an ASP.NET Core in .NET 8 to ASP.NET Core in .NET 9.

Prerequisites

Update the .NET SDK version in global.json

If you rely on a global.json file to target a specific .NET Core SDK version, update the version property to the .NET 9.0 SDK version that's installed. For example:

{
  "sdk": {
-    "version": "8.0.100"
+    "version": "9.0.100"
  }
}

Update the target framework

Update the project file's Target Framework Moniker (TFM) to net9.0:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
  </PropertyGroup>

</Project>

Update package references

In the project file, update each Microsoft.AspNetCore.*, Microsoft.EntityFrameworkCore.*, Microsoft.Extensions.*, and System.Net.Http.Json package reference's Version attribute to 9.0.0 or later. For example:

<ItemGroup>
-   <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="8.0.2" />
-   <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.2" />
-   <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="8.0.0" />
-   <PackageReference Include="System.Net.Http.Json" Version="8.0.0" />
+   <PackageReference Include="Microsoft.AspNetCore.JsonPatch" Version="9.0.0" />
+   <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.0" />
+   <PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="9.0.0" />
+   <PackageReference Include="System.Net.Http.Json" Version="9.0.0" />
</ItemGroup>

Replace UseStaticFiles with MapStaticAssets

Optimize the handling of static files in your web apps by replacing UseStaticFiles with MapStaticAssets in the app's Program file:

- app.UseStaticFiles();
+ app.MapStaticAssets();

In MVC & Razor Pages apps you additionally need to chain a call to .WithStaticAssets after MapRazorPages or MapControllerRoute in Program.cs. For an example, see the Static files in ASP.NET Core.

ASP.NET Core automatically fingerprints and precompresses your static files at build and publish time, and then MapStaticAssets surfaces the optimized files as endpoints using endpoint routing with appropriate caching headers.

To resolve the fingerprinted file names from your app:

  • In Blazor apps, use the ComponentBase.Assets property. Update explicit references to static assets in Razor component files (.razor) to use @Assets["{ASSET PATH}"], where the {ASSET PATH} placeholder is the path to the asset. Note that this should NOT be done for the Blazor framework scripts (blazor.*.js). In the following example, Bootstrap, the Blazor project template app stylesheet (app.css), and the CSS isolation stylesheet (based on an app's namespace of BlazorSample) are linked in a root component, typically the App component (Components/App.razor):

    <link rel="stylesheet" href="@Assets["bootstrap/bootstrap.min.css"]" />
    <link rel="stylesheet" href="@Assets["app.css"]" />
    <link rel="stylesheet" href="@Assets["BlazorSample.styles.css"]" />
    
  • In MVC & Razor Pages apps, the script and link tag helpers will automatically resolve the fingerprinted file names.

To resolve the fingerprinted file names when importing JavaScript modules, add a generated import map:

  • In Blazor apps, add the (ImportMap) component to the <head> content of the app's root component, typically in the App component (App.razor):

    <ImportMap />
    
  • In MVC & Razor pages apps, add <script type="importmap"></script> to the head of the main layout file, which is updated by the Import Map Tag Helper.

For more information, see the following resources:

Blazor

Adopt simplified authentication state serialization for Blazor Web Apps

Blazor Web Apps can optionally adopt simplified authentication state serialization.

In the server project:

  • Remove the Persisting Authentication State Provider (PersistingAuthenticationStateProvider.cs).

  • Remove the service registration from the Program file. Instead, chain a call to AddAuthenticationStateSerialization on AddRazorComponents:

    - builder.Services.AddScoped<AuthenticationStateProvider, PersistingAuthenticationStateProvider>();
    
      builder.Services.AddRazorComponents()
          .AddInteractiveServerComponents()
          .AddInteractiveWebAssemblyComponents()
    +     .AddAuthenticationStateSerialization();
    

The API only serializes the server-side name and role claims for access in the browser. To include all claims, set SerializeAllClaims to true:

.AddAuthenticationStateSerialization(options => options.SerializeAllClaims = true);

In the client project (.Client):

  • Remove the Persistent Authentication State Provider (PersistentAuthenticationStateProvider.cs).

  • Remove the service registration from the Program file. Instead, call AddAuthenticationStateDeserialization on the service collection:

    - builder.Services.AddSingleton<AuthenticationStateProvider, PersistentAuthenticationStateProvider>();
    + builder.Services.AddAuthenticationStateDeserialization();
    

For more information, see What's new in ASP.NET Core 9.0.

Streaming rendering attribute no longer requires the true parameter

In .NET 8, streaming rendering required you to pass true for the enabled parameter:

@attribute [StreamRendering(true)]

In .NET 9 or later, true can optionally be removed, as true is now the default for the enabled parameter:

@attribute [StreamRendering]

Additional resources