今天嘗試將自己的小項目從.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