asp.net core 3.0 更新简记

asp.net core 3.0 更新简记

Intro

最近把活动室预约项目从 asp.net core 2.2 更新到了 asp.net core 3.0,记录一下,升级踩过的坑以及经验总结,包括但不限于

  • TargetFramework (netcoreapp2.2 需要更新为 netcoreapp3.0)
  • Dependency
  • Host/Environment
  • Mvc
  • Routing
  • Swagger
  • Dockerfile
  • EF(不推荐更新)

TargetFramework 更新

原来项目里的 netcoreapp2.x 版本需要更新为 netcoreapp3.0,原来有些基于 netstandard2.0 的项目的包如果有包更新之后依赖 netstandard2.1 可能需要更新为 netstandard2.1(非必须,看项目依赖)

Dependency

原来在 dotnetcore 2.x 版本时大部分的包以 nuget 的形式提供,可以直接通过 nuget 引用,从 dotnetcore 3.0 开始很多包不再发布 nuget 包,需要通过框架引用来引用包(FrameworkReference)

比如在一个类库项目 <Project Sdk="Microsoft.NET.Sdk"> 里有这么一个引用 <PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.2" />,在 dotnetcore 3.0 并没有发布对应的 nuget 包,需要使用框架引用,示例如下:

<FrameworkReference Include="Microsoft.AspNetCore.App" />

如果是 Web 项目 <Project Sdk="Microsoft.NET.Sdk.Web">,Sdk 是 Web 的话直接把 targetFramework 更新的 netcoreapp3.0 就可以了,不需要再添加上面的引用了,项目里 <PackageReference Include="Microsoft.AspNetCore.App"/> 的引用也可以直接移除了

Host && Environment

原来的 IHostingEnvironment 改为了 IWebHostEnvironment,原来注入 IHostingEnvironment 的地方需要修改为注入 IWebHostEnvironment

原来 Program 里使用的 WebHostBuilder 改为 HostBuilder 并配置 ``ConfigureWebHostDefaults,变更如下:

Program.cs

MVC

服务注册

3.0 里 MVC 对 ControllerRazorPages 以及 RazorViews 整理,注入服务的时候我们可以只注入自己需要的服务,如果是 WebAPI 项目可以只添加 Controller 需要的服务即可,对应的添加方式:

services.AddControllers(); // WebAPI
services.AddControllersWithViews(); // MVC
services.AddRazorPages(); // RazorPage

JSON 配置

asp.net core 3.0 默认使用微软新的 JSON,但是推荐还是用 Newtonsoft.Json,比较成熟而且对很多比较特殊的情况都有处理,已然成为了.NET 里 JSON 序列化的事实标准,使用方式如下:

  1. 引用 nuget 包 Microsoft.AspNetCore.Mvc.NewtonsoftJson

  2. 配置使用 Newtonsoft.Json

services.AddControllersWithViews()
    .AddNewtonsoftJson(options =>
    {
        options.SerializerSettings.ContractResolver = new DefaultContractResolver();
        options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc; // 设置时区为 UTC
    });

Rounting

asp.net core 3.0 推荐使用 endpoint rounting

配置方式如下:

           app.UseStaticFiles();

            app.UseSwagger()
                .UseSwaggerUI(c =>
                {
                    // c.RoutePrefix = string.Empty; //
                    c.SwaggerEndpoint($"/swagger/{ApplicationHelper.ApplicationName}/swagger.json", "活动室预约系统 API");
                    c.DocumentTitle = "活动室预约系统 API";
                });
                
            app.UseRouting(); // 放在 UseStaticFiles 之后

            app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());

            app.UseRequestLog();
            app.UsePerformanceLog();

            app.UseAuthentication();
            app.UseAuthorization(); // 放在 UseAuthentication 之后

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers(); // 属性路由
                endpoints.MapControllerRoute("Notice", "/Notice/{path}.html", new
                {
                    controller = "Home",
                    action = "NoticeDetails"
                }); // 自定义路由
                endpoints.MapControllerRoute(name: "areaRoute", "{area:exists}/{controller=Home}/{action=Index}"); // 区域路由
                endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}"); // 默认路由
            });

Swagger

更新 swagger 依赖到最新的 5.0.0-rc-x 版本(还没发稳定版,需要显示预览版本才能看到)

services.AddSwaggerGen(options =>
{
    options.SwaggerDoc(ApplicationHelper.ApplicationName, new Microsoft.OpenApi.Models.OpenApiInfo { Title = "活动室预约系统 API", Version = "1.0" });

    options.IncludeXmlComments(System.IO.Path.Combine(AppContext.BaseDirectory, $"{typeof(Models.Notice).Assembly.GetName().Name}.xml"));
    options.IncludeXmlComments(System.IO.Path.Combine(AppContext.BaseDirectory, $"{typeof(API.NoticeController).Assembly.GetName().Name}.xml"), true);
});

这里没有用到 OperationFilter,如果用到了 OperationFilter,可能需要引入 Swashbuckle.AspNetCore.Filters 这个包,详细参考:https://www.cnblogs.com/laozhang-is-phi/p/11520048.html#autoid-6-0-0

Docker

Dockerfile 里基础镜像需要更新到 3.0

示例 dockerfile:

FROM mcr.microsoft.com/dotnet/core/sdk:3.0-alpine AS build-env
WORKDIR /src

# Copy csproj and restore as distinct layers
COPY ActivityReservation.Common/*.csproj ActivityReservation.Common/
COPY ActivityReservation.Models/*.csproj ActivityReservation.Models/
COPY ActivityReservation.DataAccess/*.csproj ActivityReservation.DataAccess/
COPY ActivityReservation.Business/*.csproj ActivityReservation.Business/
COPY ActivityReservation.Helper/*.csproj ActivityReservation.Helper/
COPY ActivityReservation.WechatAPI/*.csproj ActivityReservation.WechatAPI/
COPY ActivityReservation.AdminLogic/*.csproj ActivityReservation.AdminLogic/
COPY ActivityReservation.API/*.csproj ActivityReservation.API/
COPY ActivityReservation/ActivityReservation.csproj ActivityReservation/

# RUN dotnet restore ActivityReservation/ActivityReservation.csproj
## diff between netcore2.2 and netcore3.0
WORKDIR /src/ActivityReservation
RUN dotnet restore

# copy everything and build
COPY . .
RUN dotnet publish -c Release -o out ActivityReservation/ActivityReservation.csproj

# build runtime image
FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-alpine

LABEL Maintainer="WeihanLi"
WORKDIR /app
COPY --from=build-env /src/ActivityReservation/out .
EXPOSE 80
ENTRYPOINT ["dotnet", "ActivityReservation.dll"]

修改基础镜像一般不会有什么问题,需要注意的是如果 dockerfile 里有用到 dotnet publish 且publish 的项目不在当前目录下,可能会遇到这样的问题,最后找不到要发布的文件。

dotnet core 3.0 cli 有个 breaking change,如果要发布的项目不在当前目录下,在 2.x 版本是会发布到项目文件所在目录下的,但是 3.0 版本会发布在当前目录下,比如说执行dotnet publish -c Release ./src/ActivityReservation.csproj -o out命令:

2.x版本会在 src目录下生成一个 out 文件夹

3.0 版本会在当前目录下生成一个 out 文件夹,out文件夹和 src 同级

详细问题可以参考 https://github.com/dotnet/cli/issues/12696

EF

EF Core 3.0 和 asp.net core 3.0 完全独立,可以在 asp.net core 3.0 的项目里使用 2.x 的 EF Core

EF Core 3.0 有个 breaking change,不再隐式支持客户端渲染数据,这可以让你更清晰的知道哪些条件和在数据库执行的哪些条件是在本地执行的,但是实际试用下来,还是有很多问题的,在 EF 的基础上封装了一层,试用表达式树来拼接查询条件,但是最后执行的时候会有问题,但是简化后的条件实际上并不会在客户端执行任何过滤操作,所以暂时不推荐试用 ef core 3.0,而且更新之后可能会遇到其他的问题,详见issue https://github.com/aspnet/EntityFrameworkCore/issues/18025

现在的项目使用 ef core2.1 + asp.net core3.0 运行

More

其他的地方应该也有需要修改的地方,欢迎补充

Reference

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,408评论 4 371
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,690评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,036评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,726评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,123评论 3 296
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,037评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,178评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,964评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,703评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,863评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,333评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,658评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,374评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,195评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,988评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,167评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,970评论 2 279

推荐阅读更多精彩内容