今天尝试将自己的小项目从.net core 2.2 升级到 3.1,发现并不是简单的 一键升级 这么简单(惭愧)!!记录下升级的步骤以及过程中遇到的问题。
所有项目目标框架选择为.net core 3.1
发现项目依赖项的包中出现黄色感叹号,编译成功,但是项目启动后显示警告。
原因警告已经说的很清楚了,移除Microsoft.AspNetCore.App和Razor.Design引用。
项目启动更改
原启动方式
public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureLogging((hostingContext,logging)=> { logging.ClearProviders(); logging.AddConsole(); logging.AddNLog(); }) .UseStartup<Startup>();
现启动方式,注意,采用了更通用的Host替代了原来的WebHost
public static void Main(string[] args) { CreateHostBuilder(args).Build().Run(); } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>() .ConfigureLogging((hostingContext, logging) => { logging.ClearProviders(); logging.AddConsole(); logging.AddNLog(); }); });
更新所有Nuget包至最新
mvc项目异常来了!
Application startup exception: System.InvalidOperationException: Endpoint Routing does not support 'IApplicationBuilder.UseMvc(...)'. To use 'IApplicationBuilder.UseMvc' set 'MvcOptions.EnableEndpointRouting = false' inside 'ConfigureServices(...).
原本MVC注册方式发生变化,不再支持 app.UserMcv();
修改成以下方式
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseRouting(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
当然网上也提供了支持原类似的注册方式,可参考 https://www.cnblogs.com/tianma3798/p/11909293.html
部分依赖组件注册方式也发生了变化。例如Autofac,原因是ConfigureServices不再支持返回System.IServiceProvider。
原本的注册方式
public IContainer ApplicationContainer { get; private set; } private IServiceProvider ConfigureInjectionsWithAutofac(IServiceCollection services) { var builder = new ContainerBuilder(); //very import //正常返回IServiceProvider 并不能替代原IServicePrivder。还需要自定义ServiceScopeFactory保证RequestServices返回的也是你自定义的ServiceProvider。 //详细见https://www.cnblogs.com/artech/p/3rd-party-di-integration.html //此处autofac做了特殊处理 builder.Populate(services); //TODO 如何支持多个数据库链接DbContext? //具体的 Repository,可以依赖具体的 DbContext builder.RegisterType<MyDataContext>().As<DbContext>().InstancePerLifetimeScope(); #region repository builder.RegisterType<UserRepository>().As<IUserRepository>().InstancePerLifetimeScope(); #endregion #region domainService builder.RegisterType<UserDomainService>().As<IUserDomainService>().InstancePerLifetimeScope(); #endregion #region appService builder.RegisterType<UserAppService>().As<IUserAppService>().InstancePerLifetimeScope(); #endregion #region middlerware builder.RegisterType<GlobalExceptionMiddleware>().AsSelf().InstancePerLifetimeScope(); builder.RegisterType<GlobalApiLoggingMiddleware>().AsSelf().InstancePerLifetimeScope(); #endregion ApplicationContainer = builder.Build(); return new AutofacServiceProvider(ApplicationContainer); }
现在的注册方式,在program.cs类中 UseServiceProviderFactory
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseServiceProviderFactory(new AutofacServiceProviderFactory()) .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>() .ConfigureLogging((hostingContext, logging) => { logging.ClearProviders(); logging.AddConsole(); logging.AddNLog(); }); });
在Startup.cs类中新增 ConfigureContainer
public void ConfigureContainer(ContainerBuilder builder) { #region repository builder.RegisterType<UserRepository>().As<IUserRepository>().InstancePerLifetimeScope(); #endregion #region domainService builder.RegisterType<UserDomainService>().As<IUserDomainService>().InstancePerLifetimeScope(); #endregion #region appService builder.RegisterType<UserAppService>().As<IUserAppService>().InstancePerLifetimeScope(); #endregion #region middlerware builder.RegisterType<GlobalExceptionMiddleware>().AsSelf().InstancePerLifetimeScope(); builder.RegisterType<GlobalApiLoggingMiddleware>().AsSelf().InstancePerLifetimeScope(); #endregion }
webapi返回值问题。我正好有个接口返回的是JObject。升级到3.1后报 System.NotSupportedException: The collection type 'Newtonsoft.Json.Linq.JObject' is not supported。原代码如下
[Route("headclaims")] [HttpGet] public IActionResult HeadClaims() { var claimTypes = new List<string> { "name", "phone", "userId", "introduce","client_id" }; var claimHeads = Request.Headers.Where(x => claimTypes.Contains(x.Key)); var user = Request.HttpContext.User; if (claimHeads == null) return Ok(); var returnObj = new JObject(); foreach (var ch in claimHeads) { returnObj.Add(ch.Key, ch.Value.ToString()); } return Ok(returnObj); }
最后返回要修改成这样,才能正确返回。
return Ok(returnObj.ToString());
Entityframework 问题。下面代码在2.2是可以运行的,但是3.0会报linq错误。
public async Task<UserEntity> GetUserAsync(string name, string password) { return await _dbSet.FirstOrDefaultAsync(x => string.Equals(x.Name, name, StringComparison.CurrentCultureIgnoreCase) && string.Equals(x.Password, password, StringComparison.CurrentCultureIgnoreCase)); }
修正为
public async Task<UserEntity> GetUserAsync(string name, string password) { return await _dbSet.FirstOrDefaultAsync(x => x.Name == name && x.Password == password); }
原因应该在这里能解释:https://docs.microsoft.com/zh-cn/ef/core/querying/client-eval。
至此,我的小项目正确运行起来!!
最后贴下官方的迁移指引,我承认之前没看~~~。一定要看一下!!!
https://docs.microsoft.com/zh-cn/aspnet/core/migration/22-to-30?view=aspnetcore-3.1&tabs=visual-studio