有时候,版本更新太快并不是一件好事。虽然,两周一个迭代的“敏捷”开发依然被客户嫌弃交付缓慢,可一边是前端领域“求不要再更新了,学不动了”的声音,一边则是.NET Core从1.x到2.x再到3.x的高歌猛进。版本更新太快,带来的是API的频繁变动,无法形成有效的知识沉淀,就像转眼到了2020年,Python 2.x
和Windows 7
都引来了“寿终正寝”,可能你都还没有认真地学习过这些知识,突然就被告知这些知识要过期了,想想还是觉得挺疯狂啊。最近一直在捣鼓,如何让.NET Core
应用跑在Heroku
平台上,因为Docker
镜像里使用最新的.NET Core 3.1运行时,所以,痛定思痛之余,决定把手头项目升级到3.1。上一次痛苦还是在2.1升级2.2,这还真没过多长时间。所以呢,这篇博客主要梳理下从2.2升级到3.1过程中遇到的问题。
更新项目文件
- 调整目标框架为
netcoreapp3.1
- 删除引用项:
Microsoft.AspNetCore.App
、Microsoft.AspNetCore.Razor.Design
- 删除
AspNetCoreHostingModel
,如果项目文件中的值为InProcess
(因为ASP.NET Core 3.0 或更高版本项目默认为进程内承载模型)
更新程序入口
- CreateWebHostBuilder()方法的返回值类型由
IWebHostBuilder
调整为IHostBuilder
- 增加引用项:
Microsoft.Extensions.Hosting
Kestrel
配置变更至ConfigureWebHostDefaults()
方法
public static IHostBuilder CreateWebHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(serverOptions =>
{
// Set properties and call methods on options
})
.UseStartup<Startup>();
});
如果通过 HostBuilder
手动创建宿主,则需要在 ConfigureWebHostDefaults()
方法中显式调用·UseKestrel()
:
public static void Main (string[] args)
{
var host = new HostBuilder ()
.UseContentRoot (Directory.GetCurrentDirectory ())
.ConfigureWebHostDefaults (webBuilder =>
{
webBuilder.UseKestrel (serverOptions =>
{
// Set properties and call methods on options
})
.UseIISIntegration ()
.UseStartup<Startup> ();
})
.Build ();
host.Run ();
}
更新Startup
Configure()
方法第二个参数由``IHostingEnvironment调整为
IWebHostEnvironment(需要引用
Microsoft.Extensions.Hosting`)- 从管道中删除
UseMvc()
扩展方法,相应地,删除AddMvc()
及其链式调用相关方法 AddMvc()
等价于AddRazorPages()
+AddControllersWithViews()
AddControllers()
对应WebApi
模板,AddControllersWithViews()
对应MVC
模板,AddRazorPages()
对应SPA
模板- 路由注册由
传统路由
调整为终结点路由
:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
//SignalR路由
endpoints.MapHub<ChatHub>("/chat");
//RazorPages路由
endpoints.MapRazorPages()
//特性路由(WebApi)
endpoints.MapControllers();
//控制器路由(MVC)
endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
});
}
如果希望继续使用传统路由
,则可以使用下列方法任一:
services.AddMvc(options => options.EnableEndpointRouting = false);
services.AddControllers(options => options.EnableEndpointRouting = false);
services.AddControllersWithViews(options => options.EnableEndpointRouting = false);
services.AddRazorPages().AddMvcOptions(options => options.EnableEndpointRouting = false);
序列化/反序列化
- 从
.NET Core 3.0
开始,System.Text.Json
默认作为替代Newtonsoft.json
的新一代JSON API - 直接从
.NET Core 3.0
创建的SignalR项目,服务端返回的JSON数据存在大小写的问题,这是由System.Text.Json
引起的。解决方案是:
services.AddSignalR()
.AddJsonProtocol(options => options.PayloadSerializerOptions.PropertyNamingPolicy = null);
同理,对于该方案对于services.AddControllers()
一样有效,前提是项目中使用了System.Text.Json
。同理,对于SignalR的客户端项目,我们有:
new HubConnectionBuilder()
.WithUrl("/chatHub")
.AddJsonProtocol(options =>
{
//TODO
})
.Build();
- SignalR的JavaScript客户端由
@aspnet/signalr
调整为为@microsoft/signalr
:
const signalR = require("@microsoft/signalr");
let connection = new signalR.HubConnectionBuilder().withUrl(url).build();
- 如果希望继续使用
Newtonsoft.json
,则需要安装AspNetCore NewtonsoftJson
。相应地,需要显式调用AddNewtonsoftJson()
扩展方法:
services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
同样地,AddNewtonsoftJson()
支持AddControllers()
, AddControllersWithViews()
, AddRazorPages()
所有方法
疑难杂症
- 升级后提示无法加载类型:
Microsoft.AspNetCore.Mvc.MvcJsonOptions
,解决方案是: 升级Swashbuckle.AspNetCore
至最新版本(5.0+),调整Swagger中间件配置代码:
services.AddSwaggerGen(swagger =>
{
//这里发生了变化,需要引用:Microsoft.OpenApi.Models
swagger.SwaggerDoc("v1", new OpenApiInfo { Title = "ynamic WebApi", Version = "v1.0" });
});
- 安装完
.NET Core 3.x
,使用dotnet build
编译项目提示找不到Microsoft.NET.Sdk.Web
。解决方案是: 升级2.2的时候,调整项目文件中的Microsoft.NET.Sdk.Web
为Microsoft.NET.Sdk
可以解决,而这个方法在3.x以后失效。 此时,可以检查环境变量MSBuildSDKsPath
中的SDK版本和实际版本是否一致,尤其是像博主这样从2.0一路升级到3.x的朋友,应该都会遇到这个问题。