目录
前言
Microsoft.AspNetCore.Mvc.ViewFeatures.Internal 消失了
升级到 ASP.NET Core 3.1
项目文件(.csproj)
Program.cs
Startup.cs
ViewBag 与 Razor Pages 第一次接触
ViewBag 与 Razor Pages 第二次接触
小结(文件更改对比图)
ASP.NET Core 3.1 的确很棒, 肉眼可见的快, 快, 快!
前言
2019 年的最后一个月, 微软终于发布了. Net Core 3.1, 这是 .Net Core 有史以来的第二个长期支持版本(至少 3 年的支持期限).
作为一个大版本更新,.NET Core 3.0 引入了大量改进和新特性, 例如新增加的 Windows Forms 和 WPF, 新的 JSON API, 对 ARM64 架构的支持, 以及全面提升的性能.
所以升级是势在必行的, 那么很多开发人员就面临一个问题:
如果从上一个长期支持版本 ASP.NET Core 2.1 升级到最新的 ASP.NET Core 3.1 ?
.Net Core 版本列表:
From: https://dotnet.microsoft.com/download/dotnet-core
如果是之前的 .Net Framework, 这个升级是非常平滑的, 甚至不需要做任何改动(比如:.Net Framework 2.0 -> .Net Framework 4.5), 但是 .Net Core 的升级却有点麻烦, 微软给出了一系列的升级指南, 不过都是从上一个版本到下一个紧邻版本:
- ASP.NET Core 2.1 -> 2.2:
- ASP.NET Core 2.2 -> 3.0:
- ASP.NET Core 3.0 -> 3.1:
当然这个过程不仅仅是改个配置文件的问题,.Net Core 的版本升级中居然有很多 Breaking changes, 也就是说如果你用的类在 .Net Core 3.1 中突然删除了, 对不起, 你只有调整自己代码的份了.
Microsoft.AspNetCore.Mvc.ViewFeatures.Internal 消失了
而且这个 Breaking changes 还不少, 比如 FineUICore 之前的版本 (支持 ASP.NET Core 2.1) 用到的 Microsoft.AspNetCore.Mvc.ViewFeatures.Internal 在 .Net Core 3.1 就被无情的删掉了:
那位说了, 你为啥要用 .Internal 里面的类?
这是很正常的, FineUICore 作为一个支持 ASP.NET Core 的控件库, 需要一些底层操作, 为了支持快速数据绑定, 类似如下的形式:
支持 TagHelpers 的页面代码:<f:DatePicker For="TheModel.StartDate"></f:DatePicker>
支持 htmlHelpers 的视图代码: F.DatePickerFor(m => m.TheModel.StartDate)
可以参考示例: https://pages.fineui.com/#/DataModel/UICompare
为了支持这个特性, 我们就需要计算表达式的文本值, 以及表达式所绑定的数据, 因此就用到了如下两个静态类方法:
- ExpressionHelper.GetExpressionText
- ExpressionMetadataProvider.FromLambdaExpression
在 .Net Core 2.2 之前的版本, 这两个方法是逃不过的. 可惜不巧的是, 微软却认为这些方法没有公开的价值, 因此就武断的隐藏掉了(你让之前使用这些类的程序情何以堪!).
网络上有很多类似的提问, 但是阻挡不了微软隐藏几乎所有 .Internal 命名空间类的做法.
- https://github.com/aspnet/AspNetCore/issues/8678
- https://github.com/aspnet/Mvc/issues/8724
在 ASP.NET Core 3.1 虽然有替代的方法可以实现上述被删掉的功能, 但是就做不到向后兼容了. FineUICore 近期已经升级到 FineUICore v6.2.0, 以便适用这个改动, 升级之后支持 .Net Core 3.1+, 不再对老版本提供支持了.
升级到 ASP.NET Core 3.1
下面我们会以 FineUICore.Examples 为例, 讲解如果将 ASP.NET Core 2.1 升级到 ASP.NET Core 3.1, 这里面的坑还真不少.
项目文件(.csproj)
这里面有几点需要注意:
1. TargetFramework 改为 netcoreapp3.1
2. 删除 Microsoft.AspNetCore.App 的引用, 这个会默认包含在框架 Sdk=Microsoft.NET.Sdk.web 中
3. 添加 Microsoft.AspNetCore.Mvc.NewtonsoftJson 的引用
有一点需要特别注意, .Net Core 3.0 已经从共享框架中删除了 Newtonsoft.JSON, 并引入新的 System.Text.JSON 类库.
但是我们的 FineUICore 示例代码很多地方都依赖了 Newtonsoft.JSON 的特性, 因此需要引入 Microsoft.AspNetCore.Mvc.NewtonsoftJson, 这个引用会自动包含 Newtonsoft.JSON 库, 可以求证于编译时的 Debug 文件夹, 位于项目的 \bin\Debug\netcoreapp3.1:
完整的项目工程文件:
- <Project Sdk="Microsoft.NET.Sdk.Web">
- <PropertyGroup>
- <TargetFramework>netcoreapp3.1</TargetFramework>
- <MvcRazorCompileOnPublish>false</MvcRazorCompileOnPublish>
- </PropertyGroup>
- <ItemGroup>
- <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.0" />
- </ItemGroup>
- <ItemGroup>
- <ProjectReference Include="..\FineUICore\FineUICore.csproj" />
- </ItemGroup>
- </Project>
Program.cs
需要注意的几点:
1. 将 IWebHostBuilder 改为 IHostBuilder
2. 将 Web 服务器的启动代码放到 ConfigureWebHostDefaults 中
当然, 这些改动我们不需要特别关心, 简单的照做就行了.
完整的 Progam.cs 代码:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Threading.Tasks;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.Extensions.Configuration;
- using Microsoft.Extensions.Hosting;
- using Microsoft.Extensions.Logging;
- namespace FineUICore.Examples
- {
- public class Program
- {
- public static void Main(string[] args)
- {
- CreateHostBuilder(args).Build().Run();
- }
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseStartup<Startup>();
- });
- }
- }
Startup.cs
这里的改动有点多, 我们分两个截图分别说明 ConfigureServices 和 Configure 的改动:
这里有几点需要调整:
1. 用 AddControllersWithViews 替代 AddMvc
2. 对参数 ModelBinderProviders 更改放到 AddMvcOptions 中
3. 添加 AddNewtonsoftJson 用来使用老的 Newtonsoft.JSON 来序列化 JSON 数据
ASP.NET Core 3.1 添加了三个新的服务注册方法来代替之前的 AddMvc:
AddRazorPages: 添加对 Razor Pages 的支持
AddControllersWithViews: 添加对控制器和视图的支持
AddControllers: 添加对控制器的支持
这些方法可以组合, 比如如下代码等效于之前版本的 AddMvc:
- services.AddControllersWithViews();
- services.AddRazorPages();
这个方法的完整代码:
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddDistributedMemoryCache();
- services.AddSession();
- // 配置请求参数限制
- services.Configure<FormOptions>(x =>
- {
- x.ValueCountLimit = 1024; // 请求参数的个数限制(默认值: 1024)
- x.ValueLengthLimit = 4194304; // 单个请求参数值的长度限制(默认值: 4194304 = 1024 * 1024 * 4)
- });
- // FineUI 服务
- services.AddFineUI(Configuration);
- services.AddControllersWithViews().AddMvcOptions(options =>
- {
- // 自定义模型绑定(Newtonsoft.JSON)
- options.ModelBinderProviders.Insert(0, new JsonModelBinderProvider());
- }).AddNewtonsoftJson();
- }
下面来看下 Configure 方法:
这里面有几点重要改动:
1. 将 IHostingEnvironment 改为 IWebHostEnvironment
2. 添加 App.UseRouting();App.UseAuthorization();, 注意添加的位置, 要放到 UseStaticFiles 的后面
3. UseMvc 改为 UseEndpoints
完整的函数代码:
- public void Configure(IApplicationBuilder App, IWebHostEnvironment env)
- {
- if (env.IsDevelopment())
- {
- App.UseDeveloperExceptionPage();
- }
- else
- {
- App.UseExceptionHandler("/Home/Error");
- }
- App.UseStaticFiles();
- App.UseSession();
- App.UseRouting();
- App.UseAuthorization();
- // FineUI 中间件(确保 UseFineUI 位于 UseEndpoints 的前面)
- App.UseFineUI();
- App.UseEndpoints(endpoints =>
- {
- endpoints.MapControllerRoute(
- name: "area",
- pattern: "{area:exists}/{controller=Home}/{action=Index}/{id?}");
- endpoints.MapControllerRoute(
- name: "default",
- pattern: "{controller=Home}/{action=Index}/{id?}");
- });
- }
对于支持 Razor Pages 的项目, 这里的 UseEndpoints 代码要改为:
- App.UseEndpoints(endpoints =>
- {
- endpoints.MapRazorPages();
- });
ViewBag 与 Razor Pages 第一次接触
在传统的 Controller/Model/View 的 MVC 架构中, 我们可以在控制器中使用 ViewBag 将数据传递到 视图中, 虽然 ViewBag 只是字典类 ViewData 的一个动态封装, 但是写法更简单:
然而在 Razor Pages 的页面模型类中, 却少了对 ViewBag 的支持, 因此在 ASP.NET Core 2.2 之前的版本里, 我们通过在基类中新增一个 ViewBag 属性来解决:
可以看出, 这里的 ViewBag 其实是一个 dynamic 类型, 内部存储单元还是 ViewData, 这段代码来自:
这么一个看似很技巧的东西其实来自微软某位程序员的自 (zi) 信(da), 如果你留意观察, 就会发现一个奇怪的逻辑:
Controller 基类 和 View 视图中支持 ViewBag
Razor Pages 的后台模型类中不支持 ViewBag, 然后 Razor Pages 的页面中支持 ViewBag
说白了, 微软的逻辑是: 你可以在 Razor Pages 中使用 ViewBag(尽管也可以设置) , 但是别在模型类中设置 ViewBag.
这位大虾 (DamianEdwards) 在一个 GitHub 的 issue 中提到这样一个观点:
We purposefully didn't add ViewBag because I wanted to discourage its use. As @pranavkm points out you can add it back easily enough if you wish but I don't have plans to add it back to the built-in base class.
- public class DynamicViewData : DynamicObject
- {
- private ViewDataDictionary ViewData;
- public DynamicViewData(ViewDataDictionary viewData)
- {
- ViewData = viewData;
- }
- /// <summary>
- /// 获取所有 key
- /// </summary>
- public override IEnumerable<string> GetDynamicMemberNames()
- {
- return ViewData.Keys;
- }
- /// <summary>
- /// 调用 ViewBag.key; 时执行
- /// </summary>
- public override bool TryGetMember(GetMemberBinder binder, out object result)
- {
- result = ViewData[binder.Name];
- return true;
- }
- /// <summary>
- /// 调用 ViewBag.key = "value"; 时执行
- /// </summary>
- public override bool TrySetMember(SetMemberBinder binder, object value)
- {
- ViewData[binder.Name] = value;
- return true;
- }
- }
- private DynamicViewData _viewBag;
- public dynamic ViewBag
- {
- get
- {
- if (_viewBag == null)
- {
- _viewBag = new DynamicViewData(ViewData);
- }
- return _viewBag;
- }
- }
来源: https://www.cnblogs.com/sanshi/p/11640318.html