如何用一份 source code 建立出不同 target framework 所使用的 library

因應容器化,許多人早已將 .net framework 的 library 轉至 .net core 使用,這邊記錄的是遇到 .net frameowrk 在轉換至 .net core 時遇到的問題,以及如何將新的 library 打包轉至內部 nuget server

.netFramework 版本在過渡時期需要同時支援 .netFramework 與 .net standard 2.0, .net core 3.1 的版本

在轉移的情況,大多不太需要修改程式碼

但如果需要修改,可以使用 .Net Portability 的工具來做轉移報告的評估

相關使用可以參考這裡
https://dotblogs.com.tw/stanley14/2017/06/22/DotNetPortabilityAnalyzer

以這份程式碼為例,如果你的程式碼上有使用到 EncryptedXml,相關程式碼可參照微網軟站
https://docs.microsoft.com/zh-tw/dotnet/api/system.security.cryptography.xml.encryptedxml?view=netframework-4.7.2

可以發現 EncryptedXml 該類別在 .net standard 2.0 是無法轉移過去的

 

且另一個問題是該元件在 .net core 3.1 已經被移至 System.Security.Cryptography.Xml.dll
https://docs.microsoft.com/zh-tw/dotnet/api/system.security.cryptography.xml.encryptedxml?view=netcore-3.1

然而在 .net framework 4.7.2 則是在 System.Security.dll,雖然命名空間都沒有改動
https://docs.microsoft.com/zh-tw/dotnet/api/system.security.cryptography.xml.encryptedxml?view=netframework-4.7.2

這樣的改動則會對轉移程式碼造成影響,因為現在是需要用同一份程式碼佈署出支援三種環境的組件

後來在 nuget 上則找到了微軟有針對 EncryptedXml 出了各種環境的 package(版本為 5.0.0),基本上安裝 nuget 上的就可以用同一份程式碼來做佈署即可

但如果你很不放心,還是想針對 .net frameowrk 4.7.2的版本去引入 System.Security (版本為4.0.0.0)的版本的話該怎麼進行;同時間 .net core .net standard 則是引用 System.Security.Cryptography.Xml (版本 5.0.0),你可以使用條件式編譯

調整輸出的 target framework 為多重輸出
手動調整 csproj 的內容

 <PropertyGroup>
   <TargetFrameworks>net472;netstandard20;netcoreapp3.1</TargetFrameworks>
 </PropertyGroup>

此時建置則會發現 bin\debug 底下會出現三種 target framework 的輸出


 

接著把 EncryptedXml 的程式碼置入,在 .net framework 4.7.2 的版本則會發現少引用了  system.security

在 .net frameowrk 4.7.2 加上缺少的 system.security 的組件後,會發現會同時 apply 到每個版本去,然而在 .net core/.net standard 並沒有這個組件

條件式引入 dll

針對不同的 target framework 所要引用的東西則有所不同 (當然這邊可以直接引入System.Security.Cryptography.Xml 5.0.0的版本即可,這樣就不用條件式引用了)

但我們在這邊做點小實驗測試在不同 target framework 怎麼引入所需的 dll

修改 csproj 為如下

<Project Sdk="Microsoft.NET.Sdk">

 <PropertyGroup>
   <TargetFrameworks>net472;netstandard20;netcoreapp3.1</TargetFrameworks>
 </PropertyGroup>

 
 <ItemGroup Condition=" '$(TargetFramework)' == 'net472' ">
   <Reference Include="System.Security" />
 </ItemGroup>
 
 <ItemGroup Condition=" '$(TargetFramework)' == 'netcoreapp3.1'">
   <Reference Include="System.Security.Cryptography.Xml" />
   <PackageReference Include="System.Security.Cryptography.Xml" Version="5.0.0" />
 </ItemGroup>
 
 <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard20'">
   <Reference Include="System.Security.Cryptography.Xml" />
   <PackageReference Include="System.Security.Cryptography.Xml" Version="5.0.0" />
 </ItemGroup>
 
</Project>

在這邊就可以看到不同的組件引入的 dll 是不同的


微調程式碼

假設今天很不幸的,你的程式碼寫法是完全不同的,那你該怎麼微調

由於這個組件 EncryptedXml 的差異只在於 dll 引入的不同,所以在寫法上不需要調整即可使用

這邊稍微簡介如果你要調整的話該怎麼做

#if NET472
       public static string Test()
       {
           return "net 472";
       }
#endif
#if NETCOREAPP3_1 || NETSTANDARD2_0

       public static string Test()
       {
           return "net core or net standard";
       }
#endif

於是我們可以在 .net framework 4.7.2 的環境測試使用這個 lib 會拿到 net 472

在 .net core 3.1 的環境拿到的則是不同的資料

這樣就能證明我們所建置出來的 dll 是可以針對不同環境客製化(雖然一般上不太建議這麼做,但通常在轉換舊程式的過程總是會遇到這種奇異現象)

建置成 nuget package

可以使用 vs2019 命令提示字元將完成的 lib 建置成 nuget package, 並放置到內部 nuget server,相關內容可參考這邊

cd /d [your csproject folder]
dotnet pack CommonLib.csproj


參考資料
https://dotblogs.com.tw/stanley14/2017/06/22/DotNetPortabilityAnalyzer
https://markheath.net/post/csproj-conditional-references
https://docs.microsoft.com/zh-tw/dotnet/standard/frameworks
https://dotblogs.com.tw/AceLee/2020/05/11/200723