.net core遷移實(shí)踐:項(xiàng)目文件csproj的轉(zhuǎn)換
隨著net core的不斷更新和生產(chǎn)可用,越來(lái)越多的人把現(xiàn)有的應(yīng)用遷移和部署到net core平臺(tái)。本文將分享遷移過(guò)程中的一個(gè)環(huán)節(jié),給大家做一下參考。
背景說(shuō)明
先來(lái)介紹一下什么是SDK樣式的文件結(jié)構(gòu)。關(guān)注net core發(fā)展的同學(xué)應(yīng)該對(duì)早期的項(xiàng)目定義文件project.json還有點(diǎn)印象。
.net開(kāi)發(fā)組在net core 1.0版本時(shí)是準(zhǔn)備拋棄xml格式的csproj文件而改為json格式的project.json來(lái)定義和描述項(xiàng)目的。
但是后來(lái)主要由于MSBuild的兼容問(wèn)題,不得不放棄project.json轉(zhuǎn)移回xml格式的csproj(詳見(jiàn)https://devblogs.microsoft.com/dotnet/announcing-net-core-tools-msbuild-alpha/)。
然而由于傳統(tǒng).net framework的csproj文件內(nèi)容繁雜,可讀性和操作性較差,因此微軟重新定義了新樣式的csproj文件內(nèi)容樣式。
其最大化的保持了和原有xml屬性的兼容,并添加了一些新的特性。由于新格式的csproj文件總是以:
標(biāo)記來(lái)定義,所以稱(chēng)之為SDK樣式。
一個(gè)典型的.net framework平臺(tái)的csproj文件結(jié)構(gòu)如下圖所示:

其中包含了項(xiàng)目的編譯配置、調(diào)試生成配置、大量的nuget文件依賴(lài)、大量的cs源代碼文件路徑等,造成了此文件內(nèi)容的非常繁多,閱讀和分析都有比較大的困難。
而SDK樣式的csproj文件內(nèi)容就非常精簡(jiǎn)了,如下圖所示:

其中TargetFrameworks配置項(xiàng)目的多目標(biāo)平臺(tái),可選的值有netstandard2.1;net451;netcoreapp3.1,三種格式分別代表net standard、net framework、net core目標(biāo)平臺(tái)。
此處的配置根據(jù)各人的項(xiàng)目定位不同設(shè)置所需的值。我們公司的業(yè)務(wù)程序是跑在特定的運(yùn)行容器下的,遷移過(guò)程也是分階段展開(kāi),最終我們采取先同時(shí)編譯兩個(gè)目標(biāo)平臺(tái)dll的方案。
由以上對(duì)比可見(jiàn),SDK樣式的csproj內(nèi)容精簡(jiǎn)的一大原因就是文件的依賴(lài)(cs源碼和nuget包文件)不需要在csproj中明確寫(xiě)明。
針對(duì)cs源碼及目錄結(jié)構(gòu),VS自動(dòng)識(shí)別項(xiàng)目目錄內(nèi)的文件結(jié)構(gòu)作為項(xiàng)目結(jié)構(gòu),這一點(diǎn)改變真是點(diǎn)贊。
因?yàn)槲募澳夸洸恍枰猚sproj這個(gè)中間層再多一次描述,相當(dāng)于“所見(jiàn)即所得”,相信大家在平時(shí)肯定遇到磁盤(pán)的源文件存在而VS項(xiàng)目就是看不到等類(lèi)似問(wèn)題,以后不會(huì)再有困惑啦。
另外針對(duì)nuget包依賴(lài)的文件也是如此,csproj不再維護(hù)nuget包內(nèi)的文件明細(xì),而改為PackageReference以nuget包為單位來(lái)管理依賴(lài)。
這個(gè)改變對(duì)我們的轉(zhuǎn)換幾乎沒(méi)有影響,重新添加一次nuget引用即可;
同時(shí)再說(shuō)一句:net core是不支持packages.config文件管理nguet包,建議大家提前就將nuget包的管理改為PackageReference方式;
csproj文件轉(zhuǎn)換的操作步驟
有了以上的背景說(shuō)明,接下來(lái)的工作就是針對(duì)要遷移的項(xiàng)目csproj文件執(zhí)行改造,其實(shí)微軟也提供了一些輔助工具(dotnet try-convert等)來(lái)幫助遷移。
但是必須先保證遷移操作明確和無(wú)誤后,才能逐步使用特定的輔助工具來(lái)提高效率。具體操作如下:
1. 直接備份原csproj文件然后將其清空
2. 然后粘貼如下最小化的xml代碼:
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><TargetFrameworks>netstandard2.1;net451TargetFrameworks>PropertyGroup><ItemGroup><PackageReference Include="Newtonsoft.Json" Version="12.0.3" />ItemGroup>Project>
3. 調(diào)整的依賴(lài)nuget包和直接引用的程序集dll
這一步對(duì)nuget包有要求,需要其提供netstandard平臺(tái)的版本,如下圖:

如果是公司內(nèi)部的nuget包就需要提前做好支持,而如果是依賴(lài)的第三方nguet包那就需要確認(rèn)是否有net core支持了。
不過(guò)當(dāng)前net core已成趨勢(shì),絕大多數(shù)流行的nuget包都能夠支持了。
4. 檢查和處理源代碼中不兼容的部分
由于從.net framework遷移到.net core確實(shí)存在部分代碼不兼容的地方,因此需要一一識(shí)別并尋找替代方案。
微軟官方提供了不兼容說(shuō)明,大家可以參考本文末尾的鏈接【從 .NET Framework 遷移到 .NET Core 的中斷性變更】。
這里提供幾種常見(jiàn)的不兼容情況:
CallContext不兼容:可以通過(guò)Asynclocal
替換解決。 原System.ComponentModel.DataAnnotations不兼容:需要單獨(dú)安裝nuget包System.ComponentModel.Annotations即可解決。
System.Runtime.Remoting不兼容:暫無(wú)替換方案
5. 刪除:Properties文件夾的AssemblyInfo.cs
6. 刪除:packages.config文件。
7. 提醒:調(diào)整CI配置
輸出目錄Debug/Release目錄內(nèi)也分別生成了netstandard2.1和net451的2套程序集。因此假如應(yīng)用了CI的話,相關(guān)設(shè)置也需要更新。
完成以上操作后,項(xiàng)目的結(jié)構(gòu)就變成了下圖的樣子,這樣編譯通過(guò)后會(huì)生成針對(duì)特定目標(biāo)平臺(tái)的程序集。
總結(jié)
本文首先介紹了SDK樣式的csproj文件的背景,并詳細(xì)介紹了從傳統(tǒng).net framework項(xiàng)目轉(zhuǎn)換到net core項(xiàng)目的關(guān)鍵步驟。
根據(jù)各企業(yè)的項(xiàng)目規(guī)模和應(yīng)用場(chǎng)景的不同,還需要制訂合理的遷移計(jì)劃,配合嚴(yán)格的測(cè)試工作,這樣才能保證遷移工作的穩(wěn)定推進(jìn),尤其要避免的是因遷移net core而帶來(lái)服務(wù)異常甚至是生產(chǎn)事故。
祝大家遷移順利!
參考資料
.NET Core遷移前的準(zhǔn)備工作從 .NET Framework 遷移到 .NET Core 的中斷性變更從 packages.config 遷移到 PackageReference從 .NET Framework 移植到 .NET Core 的概述.NET可移植性分- 析器.NET API 分析器

