包、元包和框架(.NET Core 指南)

風靈使發表於2019-01-28

.NET Core 是一種由 NuGet 包組成的平臺。 有些產品體驗受益於包的細粒度定義,而另一些受益於粗粒度的定義。 為了適應這種二元定義,一款好的產品應該作為一組細粒度的包釋出,並在更粗的粒度組塊中進行分發,單個包的正式的名字叫做元包。

每個 .Net Core 包都支援以框架形式通過多個 .Net 實現程式碼執行。 其中有些框架是傳統框架,例如表示 .NET Frameworknet46。 而另一些則是新框架,可視為是“基於包的框架”,這種是框架的另外一種新的定義模型。 這些基於包的框架整個都是由包組成的,它們自身也被定義成包,這就在包與框架之間形成了一種比較密切的關係。

.NET Core 被分成一組包,它們提供了基元型別,以及更高層的資料型別,應用組合型別和通用實用工具。 每一個包都代表著單獨的同名程式集。 例如,System.Runtime 這個包就包含了 System.Runtime.dll 程式集。

以細粒度方式定義這些包具有以下好處:

  • 細粒度的包可以在它自己的計劃內交付,只需完成僅對相關的其他有限的包進行測試即可。
  • 細粒度的包可以提供不同的 OS 和 CPU 支援。
  • 細粒度的包可以單獨依賴於某一個庫。
  • 應用可以變得更小,因為沒有引用的包不會變成應用發行的一部分。

上述某些好處只適用於某些特定場合。 例如,.NET Core 的所有包通常都會在同一計劃內提供對同一平臺的支援。 在這種情況下,補丁與更新會以小的單獨包的形式釋出和安裝。 由於這種小範圍的變化,補丁的驗證與時間花費,都可以限制到單個庫的需求範圍中。

以下是 .NET Core 重要的 NuGet 包列表:

  • System.Runtime - 最基礎的 .NET Core 包,包括 Object、String、Array、ActionIList<T>
  • System.Collections - 一組(主要)泛型集合,包括 List<T>Dictionary<TKey,TValue>
  • System.Net.Http - 一組用於 HTTP 網路通訊的型別,包括 HttpClientHttpResponseMessage
  • System.IO.FileSystem - 一組用於讀寫到本地或網路磁碟儲存的型別,包括 FileDirectory
  • System.Linq - 一組用於查詢物件的型別,包括 EnumerableILookup<TKey,TElement>
  • System.Reflection - 一組用於載入、檢查和啟用型別的型別,包括 AssemblyTypeInfoMethodInfo

通常,包含元包要比包含各個包更加簡單可靠。 但是當需要單個包時,可以按以下示例所示的那樣來包含它,此示例引用 System.Runtime 包。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.6</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="System.Runtime" Version="4.3.0" />
  </ItemGroup>
</Project>

元包

元包就是一個 NuGet 包約定,描述了一組意義相關的包。 開發團隊利用依賴項來描述這一組包。 他們通過這一組包來描述一個框架,然後有選擇地釋出出去。

預設情況下,早期版本的 .NET Core 工具(同時基於 project.jsoncsproj 的工具)指定一個框架和一個元包。 但目前,由目標框架隱式引用元包,以便將每個元包繫結到一個目標框架。 例如,netstandard1.6 框架引用NetStandard.Library 1.6.0 版元包。 同樣,netcoreapp2.1 框架引用 Microsoft.NETCore.App 2.1.0 版元包。 有關詳細資訊,請參閱 .NET Core SDK 中的隱式元包引用。

以某個框架為目標以及隱式引用元包,這實際上是新增了對元包中每一個獨立包的引用依賴。 這使這些包中的所有庫都可用於 IntelliSense(或類似體驗),同時也可用於釋出應用。

使用元包具有以下好處:

  • 在引用大量細粒度包方面,提供了一種方便的使用者體驗。
  • 定義了一組經過充分測試且執行良好的包(包括指定的各種版本)。

.NET Standard 元包為:

  • NETStandard.Library - 描述了屬於“.NET Standard”一部分的各種庫。 適用於所有支援 .NET Standard.NET 實現(例如,.NET Framework、.NET CoreMono)。也就是“netstandard”框架。

重要的 .NET Core 元包有:

  • Microsoft.NETCore.App - 描述了屬於 .NET Core 發行版的部分庫。 也就是 .NETCoreApp 框架。它依賴於更小的 NETStandard.Library
  • Microsoft.AspNetCore.App - 包含來自 ASP.NET CoreEntity Framework Core的所有受支援的包(包含第三方依賴項的包除外)。 有關詳細資訊,請參閱 ASP.NET CoreMicrosoft.AspNetCore.App 元包。
  • Microsoft.AspNetCore.All - 包含來自 ASP.NET Core、Entity Framework Core 以及ASP.NET CoreEntity Framework Core 使用的內部和第三方依賴項的所有受支援包。 有關詳細資訊,請參閱ASP.NET Core 2.xMicrosoft.AspNetCore.All 元包。
  • Microsoft.NETCore.Portable.Compatibility - 一組相容外觀,使基於 mscorlib的可移植類庫(PCL) 得以在 .Net Core上執行。

框架

每個 .NET Core 包支援一組執行時框架。 框架描述了一組可用的 API(以及潛在的其他特性),所以你可以在指定一個目標框架時使用這些功能。 新增新的 API 時,它們就會進入版本控制流程。

例如,System.IO.FileSystem 支援以下框架:

  • .NETFramework,Version=4.6
  • .NETStandard,Version=1.3
  • 6 種 Xamarin 平臺(例如,xamarinios10)

將前兩個框架進行對比很有幫助,因為它們各自代表了一種不同的框架定義方式。

.NETFramework,Version=4.6 框架表示 .NET Framework 4.6 中可用的 API。 你可以生成使用 .NET Framework 4.6 引用程式集編譯的庫,並以NuGet 包的方式在 net46 lib 資料夾中釋出這些庫。 這樣,你的庫就會被那些基於或者相容 .Net Framework 4.6 的應用所使用。 這是所有框架的傳統工作原理。

.NETStandard,Version=1.3 框架是一個基於包的框架。 它依賴基於框架的包,來定義和公開與框架有關的 API

基於包的框架

框架和包之間是一種雙向關係。 首先是為一個給定的框架定義了 API,例如 netstandard1.3。 以 netstandard1.3 為目標的包(或相容的框架,如 netstandard1.0)定義了適用於 netstandard1.3API。 聽起來像是迴圈定義,然而並不是。 從“基於包的”這個詞本身的角度來講,框架的 API 定義是來自於包的。 框架本身並不定義任何 API

其次,是這個雙向關係中的資產選擇。 包可以包含多個框架的資產。 對於一組包和/或元包的引用,框架需要決定它應選擇哪些資產,例如,是 net46 還是 netstandard1.3。 選擇正確的資產很重要。 例如,net46 資產可能並不與 .NET Framework 4.0.NET Core 1.0 相容。

可以在下圖中看到這種關係。 API 選擇框架作為目標並定義了框架。 而框架用於資產選擇。 資產實現了 API

基於包的框架組合

.Net Core 基礎之上,基於包的框架主要有兩個:

  • netstandard
  • netcoreapp

.NET Standard

.NET Standard(目標框架名字物件:netstandard)框架表示在 .NET Standard 基礎之上生成並由其定義的 API。 如果構建的庫將用於在多個執行時上執行,就應將此框架作為目標。 這樣便可在任何一種相容 .NET 標準的執行時上受支援,例如 .NET Core、.NET FrameworkMono/Xamarin。 每個執行時都支援一組 .NET Standard 版本,具體取決於實現的 API

netstandard 框架隱式引用 NETStandard.Library 元包。 例如,以下 MSBuild 專案檔案指示專案以 netstandard1.6 為目標,其引用 NETStandard.Library 1.6 版元包。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.6</TargetFramework>
  </PropertyGroup>
</Project>

但專案檔案中的框架和元包引用不需要匹配,並且可使用專案檔案中的 <NetStandardImplicitPackageVersion> 元素指定低於元包版本的框架版本。 例如,以下專案檔案有效。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.3</TargetFramework>
    <NetStandardImplicitPackageVersion>1.6.0</NetStandardImplicitPackageVersion>
  </PropertyGroup>
</Project>

面向 netstandard1.3 卻使用 NETStandard.Library 1.6.0 版本,這一點很奇怪。 然而,這是一個有效的用例,因為元包支援更舊的 netstandard 版本。 可能恰好你已將 1.6.0 版的元包進行了標準化,然後將其用於所有庫,而這些庫可以面向各種 netstandard 版本。 使用此方法,只需還原 NETStandard.Library 1.6.0,無需載入早期版本。

反之,把 netstandard1.6 設為目標,卻使用 1.3.0 版的 NETStandard.Library 也是無效的。 你不能把更高版本的框架設為目標,卻使用更低版本的元包,因為更低版本的元包不會公開任何更高版本框架的資產。 元包資產的版本控制方案與描述框架的最高版本匹配。 藉助於版本控制方案,NETStandard.Library 的第一個版本是 v1.6.0,因為它包含 netstandard1.6 資產。 而上例中的 v1.3.0 版本,只是為了舉例方便,實際上並不存在。

.NET Core 應用程式

.NET Core 應用程式(目標框架名字物件:netcoreapp)框架表示 .NET Core 發行版及其提供的控制檯應用程式模型附帶的包和相關 API.NET Core 必須使用此框架,因為必須要使用其中的控制檯應用程式模型。同時只執行於 .Net Core 平臺的庫也應使用此模型。 使用此框架後,所有應用和庫將只能夠在 .Net Core 上執行。

Microsoft.NETCore.App 元包的目標框架是 netcoreapp。 它提供了約 60 個庫的訪問許可權,其中約 40 個由 NETStandard.Library 包提供,還有另外 20 個庫。 可以引用目標框架為 netcoreapp 或與框架(如 netstandard)相容的庫獲得對其他 API 的訪問許可權。

Microsoft.NETCore.App 提供的大部分其他庫還可以使用 netstandard 作為目標,如果其他 netstandard 庫滿足這些框架的依賴項的話。 這意味著,netstandard 庫也可以引用這些包作為依賴項。

相關文章