从 ASP.NET Core 5.0 迁移到6。0

本文介绍如何将现有 ASP.NET Core 5.0 项目更新为 ASP.NET Core 6.0。

先决条件

更新 global.asax 中的 .NET SDK 版本

如果依赖于 global.json 特定 .NET SDK 版本的文件,请将 version 属性更新为安装的 .NET 6.0 sdk 版本。 例如:

{
  "sdk": {
-    "version": "5.0.100"
+    "version": "6.0.100"
  }
}

更新目标框架

将 TFM) (的项目文件的 目标框架名字对象 更新为 net6.0

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

  <PropertyGroup>
-    <TargetFramework>net5.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>

</Project>

更新包引用

在项目文件中,将每个 Microsoft.AspNetCore.*Microsoft.Extensions.* 包引用的 Version 属性更新为6.0.0 或更高版本。 例如:

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

新的宿主模型

新的 .net 6 ASP.NET Core 应用程序的最小宿主模型只需要一个文件和几行代码。 以下来自 ASP.NET Core 空模板的代码将创建一个应用:

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

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

app.Run();

最小宿主模型:

  • 大大减少了创建应用程序所需的文件和代码行数。 只有一个文件需要四行代码。
  • Startup.cs将和 Program.cs 合并到单个 Program.cs 文件中。
  • 使用 顶级语句 来最大程度地减少应用程序所需的代码。
  • 使用全局 using 指令消除或最大程度地减少所需的 using 语句行数。

下面的代码将显示 Startup.cs Program.cs ASP.NET Core 5 Web 应用模板中的和文件 (Razor 已删除未使用的语句) 页 using

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

在 ASP.NET Core 6 中,前面的代码替换为以下代码:

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

前面的 ASP.NET Core 6 示例显示了如何:

Startup本文档后面提供了使用最小宿主模型将 ASP.NET Core 5 代码迁移到 ASP.NET Core 6 的详细示例。

对 Web 应用程序模板生成的其他文件有一些更改:

- public string RequestId { get; set; }
+ public string? RequestId { get; set; }
  • "Microsoft.Hosting.Lifetime": "Information" 已从和中 appsettings.json 删除 appsettings.Development.json
- "Microsoft": "Warning",
- "Microsoft.Hosting.Lifetime": "Information"
+ "Microsoft.AspNetCore": "Warning"

有关新的托管模型的更多详细信息,请参阅 常见问题解答 部分。 有关采用 NRTs 和 .NET 编译器 null 状态分析的详细信息,请参阅 可以为 null 的引用类型 (NRTs) 和 .net 编译器 null 状态静态分析 部分。

ASP.NET Core 5 和6个托管模型之间的差异

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();
  • Startup不能从或使用类 WebApplicationBuilder.Host WebApplicationBuilder.WebHost 。 以下突出显示的代码将引发异常:

    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();
    
  • IHostBuilder () 上的实现 WebApplicationBuilder WebApplicationBuilder.Host 不会延迟 ConfigureServicesConfigureAppConfiguration 或方法的执行 ConfigureHostConfiguration 。 不延迟执行会允许使用代码 WebApplicationBuilder 来观察对和所做的更改 IServiceCollection IConfiguration 。 下面的示例仅添加 Service1 一个 IService

    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
    {
    }
    

在前面的代码中, builder.Host.ConfigureServices 回调在调用之前被调用,而不是被延迟 builder.Build 。 这意味着 Service1 将添加到之前, IServiceCollection Service2 并将 Service1 为解析 IService

为 ASP.NET Core 6 构建库

现有的 .NET 生态系统围绕、和构建了扩展性 IServiceCollection IHostBuilder IWebHostBuilder 。 这些属性在上 WebApplicationBuilder 作为 Services 、和提供 Host WebHost

WebApplication 同时实现 Microsoft.AspNetCore.Builder.IApplicationBuilderMicrosoft.AspNetCore.Routing.IEndpointRouteBuilder

IHostBuilder IWebHostBuilder IApplicationBuilder IEndpointRouteBuilder 在构建 ASP.NET Core 特定组件时,我们希望库作者继续以、、和为目标。 这可确保中间件、路线处理程序或其他扩展点继续在不同的托管模型上工作。

常见问题解答 (FAQ)

  • 新的最小宿主模型是否不能支持?

    不是。 新的托管模型在功能上等效于和支持的98% 的方案 IHostBuilder IWebHostBuilder 。 某些高级方案需要特定的解决方法 IHostBuilder ,但我们希望这些方案非常罕见。

  • 泛型托管模型是否已弃用?

    不是。 一般托管模型是一种无限支持的替代模型。 泛型宿主支持新的托管模型,并且仍是托管基于工作进程的应用程序的主要方式。

  • 是否必须迁移到新的托管模型?

    不是。 新的托管模型是使用 .NET 6 和更高版本托管新应用程序的首选方法,但不强制更改现有应用程序中的项目布局。 这意味着应用可以通过将项目文件中的目标框架从更改为来从 .NET 5 升级到 .NET 6 net5.0 net6.0 。 有关详细信息,请参阅本文中的 更新目标框架 部分。 但是,我们建议将应用迁移到新的托管模型,以利用仅适用于新的托管模型的新功能。

  • 我是否需要使用顶级语句?

    不是。 新的项目模板均使用 顶级语句,但可以在任何 .net 6 应用中使用新的托管 api 来承载 web 服务器或 web 应用。

  • 如何将存储为字段的状态置于 ProgramStartup 类中?

    强烈建议在 ASP.NET Core 应用中使用依赖关系注入 (DI) 流式传输状态。

    有两种方法可以在 DI 之外存储状态:

    • 将状态存储在另一个类中。 在类中存储时假定可以从应用程序中的任何位置访问静态状态。

    • 使用 Program 由顶级语句生成的类来存储状态。 使用 Program 存储状态是语义方法:

      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; }
      }
      
  • 如果我使用的是自定义依赖关系注入容器怎么办?

    支持自定义 DI 容器。 有关示例,请参阅 自定义依赖项注入 (DI) 容器

  • 是否 WebApplicationFactory TestServer 仍能正常工作?

    是的。 WebApplicationFactory<TEntryPoint> 测试新的宿主模型的方法。 有关示例,请参阅使用 WebApplicationFactoryTestServer 进行测试

Blazor

若要为应用采用所有 新的 6.0 Blazor 功能,建议执行以下过程:

  • Blazor从一个项目模板创建一个新的6.0 项目 Blazor 。 有关详细信息,请参阅 用于 ASP.NET Core Blazor 的工具
  • 将应用的组件和代码移动到进行修改的 6.0 应用,以采用新的 6.0 功能。

更新 Docker 映像

对于使用 Docker 的应用,请更新 Dockerfile FROM 语句和脚本。 使用包含 6.0 ASP.NET Core的基础映像。 请考虑 docker pull 5.0 ASP.NET Core 6.0 之间的以下命令差异:

- docker pull mcr.microsoft.com/dotnet/aspnet:5.0
+ docker pull mcr.microsoft.com/dotnet/aspnet:6.0

对 ASP.NET Core Razor SDK 的更改

Razor编译器现在利用新的源生成器功能从项目中的视图和页面生成已编译的 Razor C# 文件。 在以前的版本中:

  • 编译依赖于 和 RazorGenerate RazorCompile 目标来生成生成的代码。 这些目标不再有效。 在 .NET 6 中,对编译器的单个调用支持代码生成和编译。 RazorComponentGenerateDependsOn 仍支持指定生成运行前所需的依赖项。
  • 生成 Razor 了一个单独的 AppName.Views.dll 程序集 ,其中包含应用程序中的已编译视图类型。 此行为已弃用,并生成一个包含应用类型和生成的 AppName.dll 视图的程序集。
  • 中的应用类型 AppName.Views.dll 是公共的。 在 .NET 6 中,应用类型在 中 AppName.dll ,但 为 internal sealed 。 对 执行类型发现的应用将无法对 AppName.Views.dll 执行类型发现 AppName.dll 。 下面显示了 API 更改:
- 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>

进行以下更改:

  • 以下属性不再适用于单步编译模型。
    • RazorTargetAssemblyAttribute
    • RazorTargetName
    • EnableDefaultRazorTargetAssemblyInfoAttributes
    • UseRazorBuildServer
    • GenerateRazorTargetAssemblyInfo
    • GenerateMvcApplicationPartsAssemblyAttributes

有关详细信息,请参阅 Razor 编译器不再生成视图程序集

Project模板使用 Duende Identity Server

Project模板现在使用Duende Identity 服务器。 有关迁移指南,请参阅 Identity Server4 v4.1 到 Duende Identity Server v5。

重要

Duende Identity Server 是一种具有相互许可协议的开源产品。 如果计划在生产环境中使用 Duende Server,可能需要从 Identity Duende Software 获取商业许可证并支付许可证费用。 有关详细信息,请参阅 Duende Software: Licenses

若要了解如何将Microsoft Azure Active Directory用于 ASP.NET Core Identity ,请参阅 ( Identity .GitHub/aspnetcore) 。

将名为 DbSet<Key> 的属性 Keys 添加到每个 ,以满足更新版本的 IdentityDbContext 中的新要求 IPersistedGrantDbContext 。 密钥是与 Duende Server 存储的协定 Identity 的一部分。

public DbSet<Key> Keys { get; set; }

备注

必须为 Duende 服务器重新创建现有 Identity 迁移。

迁移到 6.0 ASP.NET Core代码示例

从 ASP.NET Core 5.0 迁移到6.0 中的新的最小托管模型的代码示例

查看中断性变更

请参阅以下资源:

空引用类型 (NRT) 和 .NET 编译器 Null 状态静态分析

ASP.NET Core模板使用可为空引用类型 (NRT) ,并且 .NET 编译器执行 null 状态静态分析。 这些功能随 C# 8 一起发布,默认已为使用 ASP.NET Core 6.0 (C# 10) 或更高版本生成的应用启用。

.NET 编译器的 null 状态静态分析警告可以充当在本地更新文档示例或示例应用的指南,也可以忽略。 可以通过在应用的项目文件中Nullable 设置为 disable 来禁用 Null 状态静态分析,建议仅将其用于文档示例和示例应用(如果编译器警告在你了解 .NET 时会分散你的注意力)。 不建议在生产项目中禁用 Null 状态检查。

有关 NRT、MSBuild Nullable 属性和更新应用(包括 #pragma 指南)的详细信息,请参阅 C# 文档中的以下资源:

其他资源