.NET Core 2.0 迁移实战:从 1.x 到 2.0
2017.10.30
C#
3 min
1.3k 字
// 目录 · contents
2017 年 8 月 .NET Core 2.0
发布,这次升级的改动比预期大,但都是往好的方向走。我们趁这次机会把公司内部的一个
API 服务从 1.1 迁移到了 2.0,这篇文章记录整个过程和踩到的坑。
1. 项目文件格式变化
1.x 用 project.json,2.0 换回了 .csproj
格式(但是简化版):
1 2 3 4 5 6 7 8 9 10
| <Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>netcoreapp2.0</TargetFramework> </PropertyGroup>
<ItemGroup> <PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" /> </ItemGroup> </Project>
|
注意 Microsoft.AspNetCore.All 这个元包,它包含了 ASP.NET
Core 生态的所有包,不需要像 1.x 那样一个个添加。这让 csproj
文件干净了很多。
迁移工具:
大多数情况下自动迁移可以工作,但有些自定义构建步骤需要手动处理。
2. Program.cs 简化
1.x 的 WebHostBuilder 需要手动配置很多选项,2.0 引入了
WebHost.CreateDefaultBuilder:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseContentRoot(Directory.GetCurrentDirectory()) .ConfigureAppConfiguration((context, config) => { config.AddJsonFile("appsettings.json"); config.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true); config.AddEnvironmentVariables(); }) .ConfigureLogging(logging => { logging.AddConsole(); logging.AddDebug(); }) .UseStartup<Startup>() .Build();
host.Run(); }
public static void Main(string[] args) { WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .Build() .Run(); }
|
CreateDefaultBuilder 默认已经配置了:Kestrel、IIS
集成、从 appsettings.json 和环境变量读取配置、Console/Debug
日志。
3. Startup 构造函数简化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| public class Startup { public Startup(IHostingEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) .AddJsonFile("appsettings.json"); Configuration = builder.Build(); }
public IConfiguration Configuration { get; } }
public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; }
public IConfiguration Configuration { get; } }
|
4. Authentication 中间件变化
这是迁移里改动最大的地方,1.x 的
app.UseJwtBearerAuthentication() 等方法废弃了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| app.UseJwtBearerAuthentication(new JwtBearerOptions { Authority = "https://auth.example.com", Audience = "my-api" });
public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.Authority = "https://auth.example.com"; options.Audience = "my-api"; }); }
public void Configure(IApplicationBuilder app) { app.UseAuthentication(); app.UseMvc(); }
|
这个改动破坏性比较大,迁移时需要逐一检查所有认证配置。
5. Entity Framework Core 2.0
改进
EF Core 2.0 补上了一些 1.x 里缺失的关键功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| modelBuilder.Entity<User>().HasQueryFilter(u => !u.IsDeleted);
db.Users.IgnoreQueryFilters().Where(u => u.Name == "Alice");
modelBuilder.Entity<Order>() .OwnsOne(o => o.ShippingAddress);
var orders = db.Orders .Include(o => o.Items) .ThenInclude(i => i.Product) .ThenInclude(p => p.Category) .ToList();
|
踩坑记录
迁移过程中最耗时的问题是 IHttpContextAccessor
线程安全问题。
我们有一个中间件,从 HttpContext
读取当前用户信息并存到一个 Singleton 服务里,供后续业务逻辑使用。1.x
时没问题,迁移到 2.0 后偶发出现 A 请求读到了 B 请求的用户信息。
原因:2.0 改进了异步执行模型,某些情况下
AsyncLocal(HttpContext
内部依赖的机制)的传播行为有变化。
解决方案:把这个 Singleton 服务改成 Scoped,通过构造函数注入
IHttpContextAccessor,而不是手动存储用户信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class CurrentUserService { private static AsyncLocal<UserInfo> _current = new AsyncLocal<UserInfo>(); public UserInfo Current => _current.Value; public void Set(UserInfo user) => _current.Value = user; }
public class CurrentUserService { private readonly IHttpContextAccessor _accessor;
public CurrentUserService(IHttpContextAccessor accessor) { _accessor = accessor; }
public string UserId => _accessor.HttpContext?.User?.FindFirst(ClaimTypes.NameIdentifier)?.Value; }
|
总结
.NET Core 2.0 是个显著的成熟版本:
- API 覆盖率大幅提升:.NET Standard 2.0 兼容了约
32000 个 API,大量 .NET Framework 包可以直接使用
- 项目文件更简洁:
csproj +
Microsoft.AspNetCore.All 元包
CreateDefaultBuilder
减少了大量样板代码
- Authentication 中间件统一化,配置更清晰但有
breaking change
如果你在 1.x 上犹豫要不要升级,2.0
是一个值得迁移的版本。我们迁移完成后,新功能开发速度明显提升,运维反映部署也更稳定了。