写在前面
ASP.NET Core是微软新推出的支持跨平台、高性能、开源的开发框架,它的优势不必多说,因为已经说得太多了。当然,现在依然有着数量庞大的系统运行于.NET Framework上,由于有大量的Break Changes,很多项目项目团队也不敢贸然升级,其中的考量也不全部是技术原因,更多的可能还是业务推进因素。
小编自年前开始考虑升级一套电商系统,原先是基于.NET Framework 4.5的,打算直接升级到.NET Core 3.1,由于系统规模比较庞大,所以一旦开工就是一个漫长的工程,我的博客也在很长时间没有再更新,有点对不起读者了。
年前第一次重构时,由于低估这套系统的复杂性再加上有些冒进,步子迈得有点大,出现了很多问题,不得不重新开始。这一次重构先易后难,步步为营,难题统一在后面解决,到现在已经完成了全部工程的百分之八十,后面的也没有太困难了,所以特地抽出时间小结一下。
详细内容
类库部分
类库部分的迁移应该是最简单的了,我是创建了一个新的类库,然后把代码copy过去,很少有地方需要修改,当然了有一些引用的第三方类库需要特殊对待,如Automapper、Autofac、FluentValidation等,这些也很简单,看看文档就行。
.NET Framework中,会有一些常用的封装库,如Session、Cookie和HttpRuntime等,这些变化比较大,所以自己在Startup中启用。
- Session: 
- Startup.Configure: - app.UseSession(new SessionOptions {Cookie = new CookieBuilder{},IdleTimeout = TimeSpan.FromSeconds(1),IOTimeout = Timeout.InfiniteTimeSpan });
- Startup.ConfigureServices: - services.AddSession();
- 使用Session,可以通过HttpContext调用: - HttpContext.Session.SetString("sessionId", sessionValue); HttpContext.Session.GetString("sessionId"); context.Session.Remove("sessionId");
 
- Cookie: 
- Response.Cookies.Append("User", "1", new CookieOptions() {Expires = DateTime.Now.AddMinutes(10) });//新增操作 Response.Cookies.Delete("User");//删除操作
- HttpRuntime的使用,可以通过IMemoryCache替换,具体的使用方法可参考MSDN(链接:https://docs.microsoft.com/en-us/aspnet/core/performance/caching/memory?view=aspnetcore-3.1) 
- System.Drawing已经不存在了,我使用的是ZKWeb.System.Drawing,基本上类名、枚举名没变化,只是命名空间Drawing变成了DrawingCore 
- 依赖注入部分全部迁移到Startup.ConfigureServices 
- Controller部分- 顺便说一下,静态资源部分,如JS、CSS、Image、Font这些复制到wwwroot目录上,另外app.UseStaticFiles();会在模板中出现。 - 1、获取Controller及Action信息,可以通过RouteData.Values["controller"].ToString(),RouteData.Values["action"].ToString() - 2、很多的信息都放到了Request.Header[“”]中,如果之前可以用过Request直接点出来的,但是现在点不出来了,可以尝试使用这种方式,说不准会有意外惊喜。另外有一个相关的常量在这里出示一下,使用方式即Request.Header[HeaderNames.Authority],当然Request.HttpMethod 改为了 Request.Method。 - public static class HeaderNames {public static readonly string Accept;public static readonly string AcceptCharset;public static readonly string AcceptEncoding;public static readonly string AcceptLanguage;public static readonly string AcceptRanges;public static readonly string AccessControlAllowCredentials;public static readonly string AccessControlAllowHeaders;public static readonly string AccessControlAllowMethods;public static readonly string AccessControlAllowOrigin;public static readonly string AccessControlExposeHeaders;public static readonly string AccessControlMaxAge;public static readonly string AccessControlRequestHeaders;public static readonly string AccessControlRequestMethod;public static readonly string Age;public static readonly string Allow;public static readonly string Authority;public static readonly string Authorization;public static readonly string CacheControl;public static readonly string Connection;public static readonly string ContentDisposition;public static readonly string ContentEncoding;public static readonly string ContentLanguage;public static readonly string ContentLength;public static readonly string ContentLocation;public static readonly string ContentMD5;public static readonly string ContentRange;public static readonly string ContentSecurityPolicy;public static readonly string ContentSecurityPolicyReportOnly;public static readonly string ContentType;public static readonly string Cookie;public static readonly string CorrelationContext;public static readonly string Date;public static readonly string DNT;public static readonly string ETag;public static readonly string Expect;public static readonly string Expires;public static readonly string From;public static readonly string Host;public static readonly string IfMatch;public static readonly string IfModifiedSince;public static readonly string IfNoneMatch;public static readonly string IfRange;public static readonly string IfUnmodifiedSince;public static readonly string KeepAlive;public static readonly string LastModified;public static readonly string Location;public static readonly string MaxForwards;public static readonly string Method;public static readonly string Origin;public static readonly string Path;public static readonly string Pragma;public static readonly string ProxyAuthenticate;public static readonly string ProxyAuthorization;public static readonly string Range;public static readonly string Referer;public static readonly string RequestId;public static readonly string RetryAfter;public static readonly string Scheme;public static readonly string SecWebSocketAccept;public static readonly string SecWebSocketKey;public static readonly string SecWebSocketProtocol;public static readonly string SecWebSocketVersion;public static readonly string Server;public static readonly string SetCookie;public static readonly string Status;public static readonly string StrictTransportSecurity;public static readonly string TE;public static readonly string TraceParent;public static readonly string TraceState;public static readonly string Trailer;public static readonly string TransferEncoding;public static readonly string Translate;public static readonly string Upgrade;public static readonly string UpgradeInsecureRequests;public static readonly string UserAgent;public static readonly string Vary;public static readonly string Via;public static readonly string Warning;public static readonly string WebSocketSubProtocols;public static readonly string WWWAuthenticate;public static readonly string XFrameOptions; }- 3、Request.IsAjaxRequest - 这个已经不存在了,可以自行实现。 - public static bool IsAjaxRequest(this HttpRequest request) {if (request == null)throw new ArgumentNullException("request");if (request.Headers != null)return request.Headers["X-Requested-With"] == "XMLHttpRequest";return false; }- 4、Area注册 - 之前的AreaRegistration已经不存在,如果需要设置Area,可以在每个Controller上设置[Area(“Admin”)],路由处的注册可以考虑如下方式, - app.UseEndpoints(endpoints => {endpoints.MapControllerRoute(name: "default",pattern: "{controller=Home}/{action=Index}/{id?}");endpoints.MapControllerRoute(name: "areas",pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}"); });- 5、AbsoluteUri也已经不存在了,但是可以通过如下三个方法取代: - /// <summary>/// Returns the combined components of the request URL in a fully un-escaped form (except for the QueryString)/// suitable only for display. This format should not be used in HTTP headers or other HTTP operations./// </summary>/// <param name="request">The request to assemble the uri pieces from.</param>/// <returns>The combined components of the request URL in a fully un-escaped form (except for the QueryString)/// suitable only for display.</returns>public static string GetDisplayUrl(this HttpRequest request);/// <summary>Returns the relative URI.</summary>/// <param name="request">The request to assemble the uri pieces from.</param>/// <returns>The path and query off of <paramref name="request" />.</returns>public static string GetEncodedPathAndQuery(this HttpRequest request);/// <summary>/// Returns the combined components of the request URL in a fully escaped form suitable for use in HTTP headers/// and other HTTP operations./// </summary>/// <param name="request">The request to assemble the uri pieces from.</param>/// <returns>The encoded string version of the URL from <paramref name="request" />.</returns>public static string GetEncodedUrl(this HttpRequest request);- 6、过滤器 - 之前继承ActionFilterAttribute,现在实现IActionFilter,注册方式为services.AddMvc(o=>o.Filters.Add(new XX())),当然之前的很多过滤器或者Controller基类方法已经不存在了,如Controller OnAuthentication。 - IResultFilter中的OnResultExecuting(ResultExecutingContext filterContext)需要通过filterContext.Controller as Controller来获取默认的Controller。 - 最后有一个比较重要的类ActionDescriptor,ControllerDescriptor继承自ActionDescriptor,这里可以通过类型转换获取相关信息。 - 之前有很多的FilterAttribute也可以通过中间件来取代。 - 7、Action上被去掉的Attribute,如[ValidateInput(false)],[ChildActionOnly] - View部分- 1、页面基类型及扩展 - 之前我们创建页面基类型,是通过继承System.Web.Mvc.WebViewPage<TModel>来实现,现在我们可以通过RazorPage<TModel>来取代。 - 扩展HtmlHelper也换成了IHtmlHelper接口。HtmlString也替换了MvcHtmlString,更上层也以接口方式来取代IHtmlContent。 - 2、Ajax.BeginForm换成了<form asp-controller="DistributorGrade" asp-action="Save" id="addform" data-ajax="true" data-ajax-method="post" data-ajax-begin="begin" data-ajax-success="success">。当前.NET Core 依然支持Html.BeginForm,不过我建议大家有时间的时候都替换一下,具体请参考下一条。 - 3、第2条出现的asp-action等是通过Razor Tag Helpers来实现的,很多的自定义需要加入到_ViewImports.cshtml,当然一些引用也可以统一放到这里,如@using Microsoft.AspNetCore.Routing,这样就可以在当前的Area中作为全局引用了。 - Razor Tag Help是一个十分重要的功能,它使得.NET Core MVC的开发更像是在写Html语言,更加的清晰,更加具有生产力。 - 如@Html.TextBoxFor()可以用通过<input asp-for=””/>替换,以下图片摘自MSDN: - Framework MVC的写法 - Core MVC的写法 - 一些Tag Help集锦:(引用链接:https://docs.microsoft.com/en-US/aspnet/core/mvc/views/tag-helpers/intro?view=aspnetcore-3.1) - Built-in ASP.NET Core Tag Helpers- Anchor Tag Helper - Cache Tag Helper - Component Tag Helper - Distributed Cache Tag Helper - Environment Tag Helper - Form Tag Helper - Form Action Tag Helper - Image Tag Helper - Input Tag Helper - Label Tag Helper - Link Tag Helper - Partial Tag Helper - Script Tag Helper - Select Tag Helper - Textarea Tag Helper - Validation Message Tag Helper - Validation Summary Tag Helper - 4、@Html.Action和@Html.RenderAction可以通过ViewComponents来取代 - public class XXXXViewComponent : ViewComponent {public IViewComponentResult Invoke(){return this.View("");} }- 调用方式是await Component.InvokeAsync(“XXXXViewComponent”),详情请点击链接 - 5、@MvcHtmlString.Create()可以使用new Microsoft.AspNetCore.Html.HtmlString()取代 - 6、IP地址可以通过HttpRequest.HttpContext.Connection.RemoteIpAddress获取 - 7、之前通过@helper 定义页面的函数,这个已经被去掉了,现在可以通过@functions来取代 - 小结- 限于篇幅,先总结这么多,系统尚未完全结束,不过升级到.NET Core是一个非常棒的过程,可以更好地体验.NET Core的强大。如果小伙伴在升级过程中也遇到了很多问题,希望这篇文章可以给大家一些帮助,另外我没有写到的,大家可以留个言,我统一收集一下。