公司的項目一直採用.NET框架來開發Web項目。目前基礎類庫均爲.NET Framework 4.6.2版本。Caching, Logging,DependencyInjection,Configuration等基礎設施相關的依賴庫一直和官方保持同步,目前是1.1版本。.NET Core愈來愈趨於穩定,新的開發工具也在三月份發佈。所以,計劃將.NET Framework移植至.NET Core/Strandard。目的是使基於.NET開發的Web應用能夠跨平臺運行。html
按應用場景將公司的項目分爲基礎類庫,基礎服務和應用項目。基礎類庫以包的形式提供各種基礎功能。基礎服務經過Wcf項目搭建或者經過Web API項目搭建。應用項目則是Web Mvc項目爲主。基礎類庫和基礎服務是以一個一個解決方案的形式存在。每一個解決方案的結構,包含一個或者多個類庫項目,一個或多個控制檯項目,它們分別用於功能實現、單元測試、功能演示。若是所有要移植,那麼優先級應該是基礎類庫 -> 基礎服務 -> 應用項目。這次移植的對象是基礎類庫。linux
基礎類庫最終會以包的形式經過NuGet發佈出去,目前只面向.NET Framework框架。移植的目標之一,是讓包也能被面向.NET Core、.NET Standard框架的項目引用。結合官方資料,我選擇了直接遷移的方案。即直接將項目文件轉換爲新的基於.NET Core的項目文件。下面詳細說明移植的細節。git
1. 新建基於.NET Core的項目。github
首先重命名現有項目文件*.csproj爲*.Net46.csproj。而後使用VS2017新建一個新的基於.NET Core的項目,項目類型能夠是「類庫(.Net Core)」或者「類庫(.Net Standard)」。注意,VS2017會提示存在同名目錄,因此建立時能夠輸入一個不一樣的名稱,而後手工調整回來。web
2. 編輯項目文件,使之支持面向多個目標框架。windows
經過VS2017 RC新建的項目,「類庫(.Net Core)」或者「類庫(.Net Standard)」,默認只有一個目標框架。咱們能夠編輯項目文件,使之支持面向多個目標框架。如支持目標框架爲.NET Standard 1.四、.Net Core App 1.0和.NET Framework 4.5,則這樣來修改。api
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>netstandard1.4;netcoreapp1.0;net45</TargetFrameworks> </PropertyGroup> </Project>
注意,官方文檔中提供了.NET支持的目標框架列表,你能夠查詢更多其餘的目標框架。若是要兼容較低版本的框架,則目標框架版本不宜設置太高。如「net45」可用於.NET Framework 4.5 ~ 4.6.2等版本。如「net46」則只能用於.NET Framework 4.6 ~ 4.6.2等版本。服務器
3. 修改應用程序代碼相關API,使之支持多個目標框架。app
a. 因目標框架提供的API不相同。故必要時可添加條件編譯符號以便支持不一樣的運行時版本。框架
如下是常見的條件編譯符號列表。
.NET Framework 2.0 --> NET20
.NET Framework 3.5 --> NET35
.NET Framework 4.0 --> NET40
.NET Framework 4.5 --> NET45
.NET Framework 4.5.1 --> NET451
.NET Framework 4.5.2 --> NET452
.NET Framework 4.6 --> NET46
.NET Framework 4.6.1 --> NET461
.NET Framework 4.6.2 --> NET462
.NET Standard 1.0 --> NETSTANDARD1_0
.NET Standard 1.1 --> NETSTANDARD1_1
.NET Standard 1.2 --> NETSTANDARD1_2
.NET Standard 1.3 --> NETSTANDARD1_3
.NET Standard 1.4 --> NETSTANDARD1_4
.NET Standard 1.5 --> NETSTANDARD1_5
.NET Standard 1.6 --> NETSTANDARD1_6
關於條件編譯符號的應用,如如下代碼:
using System; using System.IO; namespace Baza.NetStandardTester { public class PathHelper { public string BaseDirectory { get; set; } public PathHelper() { #if NET45 BaseDirectory = AppDomain.CurrentDomain.BaseDirectory; #endif } public string GetRootedPath(string path) { string rootedPath = path ?? string.Empty; if (!Path.IsPathRooted(rootedPath)) { if (string.IsNullOrEmpty(BaseDirectory)) throw new ArgumentNullException("請先設置BaseDirectory屬性"); rootedPath = Path.Combine(BaseDirectory, rootedPath); } string directory = Path.GetDirectoryName(rootedPath); if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } return rootedPath; } } }
代碼說明,PathHelper提供GetRootedPath方法用於根據相對路徑計算出絕對路徑。當運行時爲.NET Core時,BaseDirectory屬性須要手動設置。當運行時爲.NET Framework 4.5時,由構造器對BaseDirectory屬性進行賦值。注意System.AppDomain.CurrentDomain.BaseDirectory用於獲取託管程序執行路徑,類AppDomain只存在於.NET Framework中。
b. .NET Standard是個基於包的框架。當你須要某個API時,如IDataReader,你須要安裝System.Data.Common包。若是是使用.NET Framework,則在命名空間System.Data下能夠找到IDataReader而無需按照包。藉助https://packagesearch.azurewebsites.net/工具,你能夠快速定位某個API在哪一個包中。
c. 基於.NET Core的項目,包版本號和其餘元數據,都存儲在*.csproj中,不會使用AssemblyInfo.cs文件,即移植時,這個文件能夠刪除。可是.NET Framework項目仍是會繼續使用該文件。
4. 同一解決方案,類庫間的引用策略。
在引用類庫時,要注意目標框架的兼容問題。如,「類庫(.Net Standard)」項目,可以被.NET Core App、.NET Framework和其餘.NET Standard項目引用。這個是由於.NET Core App和.NET Framework都支持相應版本的.NET標準庫。下表顯示了支持 .NET 標準庫的整套 .NET 運行時。
平臺名稱 | Alias | ||||||||
---|---|---|---|---|---|---|---|---|---|
.NET Standard | netstandard | 1.0 | 1.1 | 1.2 | 1.3 | 1.4 | 1.5 | 1.6 | 2.0 |
.NET 核心 | netcoreapp | → | → | → | → | → | → | 1.0 | vNext |
.NET Framework | net | → | 4.5 | 4.5.1 | 4.6 | 4.6.1 | 4.6.2 | vNext | 4.6.1 |
Mono/Xamarin 平臺 | → | → | → | → | → | → | → | vNext | |
通用 Windows 平臺 | uap | → | → | → | → | 10.0 | → | → | vNext |
Windows | win | → | 8.0 | 8.1 | |||||
Windows Phone | wpa | → | → | 8.1 | |||||
Windows Phone Silverlight | wp | 8.0 |
注意,若是項目是面向多目標框架的,那麼引用類庫時,被引用類庫也要支持面向多目標框架。
5. 單元測試
若是是使用.NET Framework類庫項目來存放單元測試代碼,那麼可能會遇到一點問題。在VS2017 RC中,測試資源管理器沒法識別出這些測試單元。經過新建「單元測試項目(.NET Framework)」,將生成的同名*.csproj覆蓋原來的項目文件,測試管理器便可識別出來。
5. MSBuild自動編譯新的解決方案
Windows下,按Release配置對整個解決方案編譯。
"c:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe" XXX.sln /p:Configuration=Release;Platform="Any CPU"
會在相關類庫根目錄下\bin\Release目錄中,生成net46和netstandard1.6兩個目錄。
6. 使用dotnet-nuget-push發佈包
使用vs2017打包時,只需右擊要打包的項目,選擇「打包」,便可在.\bin\Debug或.\bin\Release下生成XXX.0[.0].0.nupkg文件,而後將這個文件.nupkg上傳至nuget.org中。經過調用dotnet-nuget-push能夠自動化這個發佈過程,所以這種方式會更加方便。
dotnet nuget push XXX.0[.0].0.nupkg -k 4003d786-0000-4004-bfdf-c4f3e8ef9b3a -s http://customsource/
k:服務器的 API 密鑰 s:服務器 URL,如發佈到nuget.org,則能夠這樣寫。
dotnet nuget push XXX.0[.0].0.nupkg -k 4003d786-0000-4004-bfdf-c4f3e8ef9b3a -s https://www.nuget.org/api/v2/package
一般在windows下,會將dotnet-nuget-push寫在批處理文件中來完成基本類庫的部署工做。
總結
經過此方案遷移後,最終保留新的解決方案和項目文件,舊的解決方案和項目文件在移植的過程當中被刪除。以後將按照新的解決方案來跨平臺開發。基本類庫的移植工做就介紹到這裏。源代碼的移植將是個挑戰。譬如部分源碼所引用的API在.NET Core框架下不存在時如何處理?另外,基礎服務和Web Mvc項目的移植,由於要部署到linux中。也將會遇到各類問題。
參考資源
1. 組織項目以支持 .NET Framework 和 .NET Core
3. packagesearch.azurewebsites.net
4. .NET 標準庫