.Net 升级到 6 尝试

571 阅读4分钟

.Net 的新版本,LTS 的 6 发布了。手头的基于 LTS 3.2 或者 5 的项目可以考虑进行升级了。以下是将自己的项目升级的尝试的记录。

为什么要升级

随着 .Net 6 一起发布的还有 VS 2022、PowerShell 7.2 和 C# 10,它们带来了一些列新的功能和更好的性能,要想享受这些,当然是需要一起升级的。以下是它们一些吸引我的点。

  1. 64 位 IDE。64 位意味着 VS 不在受限于仅仅 4G 的可用内存,大大提高打开、编辑、运行和调试项目的时间成本。
  2. 性能提升。.Net 6 在性能上得到了很大的提升,特别是在文件 IO 和 Json 解析上,对于我正在学习的规则引擎项目,需要大量的处理这两种情况。
  3. 热重载。现在开发中不再需要修改代码、停止应用、启动运行了,在开启热重载后保存代码之后应用会自动重载。

当然,它们的升级远不止这些,有兴趣的可以查看微软官方的升级文档。

升级项目版本

这一步很简单,在项目文件中将 TargetFramework 改为 net6.0 就可以了

<TargetFramework>net6.0</TargetFramework>

这么写的话会访问所有的跨平台的 API,但如果你要正对特定系统就行开发的话可以特化你的平台,比如可以写成

  • net6.0-android
  • net6.0-ios
  • net6.0-maccatalyst
  • net6.0-tvos
  • net6.0-windows

等等平台

全局引用

TargetFramework 同一级引入一个参数 ImplicitUsings 并将它设为 enable,开启全局引用。

<ImplicitUsings>enable</ImplicitUsings>

回到我们的代码中

image.png

我们发现不少 using 直接灰了,不再需要我们手动引入了。这是因为 .Net 在编译时会自动生成一个 [你的项目名字].GlobalUsings.g.cs 的文件,里面使用最新的 global using 特性为我们全局引入了常用的系统包,我们不再需要自己手动来引入它们了,它长这样。

// <auto-generated/>
global using global::Microsoft.AspNetCore.Builder;
global using global::Microsoft.AspNetCore.Hosting;
global using global::Microsoft.AspNetCore.Http;
global using global::Microsoft.AspNetCore.Routing;
global using global::Microsoft.Extensions.Configuration;
global using global::Microsoft.Extensions.DependencyInjection;
global using global::Microsoft.Extensions.Hosting;
global using global::Microsoft.Extensions.Logging;
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Net.Http.Json;
global using global::System.Threading;
global using global::System.Threading.Tasks;

当然,我们也可以使用自定义的文件替换它,也可以引入自己的全局引入文件引入我们项目中常用的库。

可空类型

TargetFramework 同一级再引入一个参数 Nullable 并将它设为 enable,开启可空类型。

<Nullable>enable</Nullable>

虽然 C# 早就引入了可空类型,但是在引入该类型之前已经存在的数据类型的可空会逃过编译器的检查,在运行时才会触发异常,打开这个功能,强制为它们添加可控添加。虽然这个参数不是 .Net 6 引入的,但是之前对于可空的误报过多,在 C# 10 升级了可空分析后,官方建议将其设为开启。

程序入口

6 之前的 .Net 网络应用程序的入口是由 Program.csStartup.cs 两个文件配合完成的。其中 Program.cs 是一个有一个 public static void Main(string[] args) 方法的类,在其中由引入 Startup.cs 启动应用,而 Startup.cs 是由 IConfiguration 类型构造,提供 ConfigureServicesConfigure 两个返回 void 的方法构成的,典型的约定大约配置。 .Net 6 升级后统一使用 Program.cs 一个文件作为入口参数,而它也不再是一个类,而是一个脚本文件,是的,脚本文件,没有类也没有函数。看起来类似于 express.js 项目的入口文件的感觉。

改造前

Program.cs

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

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.AddControllers();
        services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo {Title = "RuleService", Version = "v1"});
        });
        services.AddSingleton<Rules.RuleService>();
    }

    // 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();
            app.UseSwagger();
            app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "RuleService v1"));
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseAuthorization();

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

改造后 Program.cs

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddSingleton<Rules.RuleService>();

builder.Services.AddHttpClient();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

可以看到 Swagger 不再需要配置地址,而服务的注入管理需要的 Services 改为从 builder 中取出。整体风格也变得更加的简洁易读。

命名空间

文件的命名空间不再需要将代码全部包起来,而是直接放在文件头部了。 下面是一个文件的改造前后的对比

namespace RuleService.Rules
{
    public class RuleResponse
    {
        public string Message { get; init; }
    }
}
namespace RuleService.Rules;

public class RuleResponse
{
    public string Message { get; init; }
}

后记

对于我的规则引擎项目至此就升级结束了。可以享受起最新的平台给我带来的便利了。