.NET Core 编程指南中文版 —— 5 包、元包和框架
包、元包和框架
.NET Core 是一个由 NuGet 包组成的平台。 一些产品体验受益于包的细粒度定义,而另一些受益于粗粒度的定义。 为了适应这种二元性,.NET Core 以一组细粒度的包和粗粒度的块(通俗地称为元包)的形式发布。
每个 .Net Core 包都支持在多个 .Net 实现(代表着多个框架)上运行。 其中有些框架是传统框架,例如表示 .NET Framework 的 net46
。 而另一些则是新框架,可视为是“基于包的框架”,它们建立了定义框架的新模型。 这些基于包的框架全部都是由包组成的,它们自身也被定义成包,这就在包与框架之间形成了一种很强的关联关系。
包
.NET Core 被分成一组包,它们提供了基元类型,以及更高层的数据类型、应用组合类型和通用实用工具。 每一个包都代表着单独的同名程序集。 例如,System.Runtime 这个包就包含了 System.Runtime.dll 程序集。
以细粒度方式定义这些包具有以下好处:
- 细粒度的包可以在它自己的计划内交付,只需完成仅对相关的其他有限的包进行测试即可。
- 细粒度的包可以提供不同的 OS 和 CPU 支持。
- 细粒度的包可以仅依赖于某一个库。
- 应用可以变得更小,因为没有引用的包不会变成应用发行的一部分。
上述某些好处只适用于某些特定场合。 例如,.NET Core 的所有包通常都会在同一计划内提供对同一平台的支持。 在这种情况下,补丁与更新会以小的单独包的形式发布和安装。 由于这种小范围的变化,补丁的验证与时间花费,都可以限制到单个库的需求范围中。
以下是 .NET Core 重要的 NuGet 包列表:
- System.Runtime - 最基础的 .NET Core 包,包括 Object、String、Array、Action 和 IList<T>。
- System.Collections - 一组(主要)泛型集合,包括 List<T> 和 Dictionary<TKey,TValue>。
- System.Net.Http - 一组用于 HTTP 网络通信的类型,包括 HttpClient 和 HttpResponseMessage。
- System.IO.FileSystem - 一组用于读写到本地或网络磁盘存储的类型,包括 File 和 Directory。
-
System.Linq - 一组用于查询对象的类型,包括
Enumerable
和 ILookup<TKey,TElement>。 - System.Reflection - 一组用于加载、检查和**类型的类型,包括 Assembly、TypeInfo 和 MethodInfo。
通常,包含元包要比包含各个包更加简单可靠。 但是当需要单个包时,可以按以下示例所示的那样来包含它,此示例引用 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.json 和 csproj 的工具)指定一个框架和一个元包。 但目前,由目标框架隐式引用元包,以致每个元包绑定到一个目标框架。 例如,netstandard1.6
框架引用 NetStandard.Library 1.6.0 版元包。 同样,netcoreapp2.1
框架引用 Microsoft.NETCore.App 2.1.0 版元包。 有关详细信息,请参阅 .NET Core SDK 中的隐式元包引用。
以某个框架为目标以及隐式引用元包,这实际上是添加了对元包中每一个独立包的引用依赖。 这使这些包中的所有库都可用于智能感知(或类似体验),同时也可用于发布应用。
使用元包具有以下好处:
- 在引用大量细粒度包方面,提供了一种方便的用户体验。
- 定义了一组经过充分测试且运行良好的包(包括指定的各种版本)。
.NET Standard 元包为:
- NETStandard.Library - 描述了属于“.NET Standard”一部分的各种库。 适用于所有支持 .NET Standard 的 .NET 实现(例如,.NET Framework、.NET Core 和 Mono)。 也就是“netstandard”框架。
重要的 .NET Core 元包有:
-
Microsoft.NETCore.App - 描述了属于 .NET Core 发行版的部分库。 也就是
.NETCoreApp
框架。 它依赖于更小的NETStandard.Library
。 - Microsoft.AspNetCore.App - 包含来自 ASP.NET Core 和 Entity Framework Core 的所有受支持的包(包含第三方依赖项的包除外)。 有关详细信息,请参阅 ASP.NET Core 的 Microsoft.AspNetCore.App 元包。
- Microsoft.AspNetCore.All - 包含来自 ASP.NET Core、Entity Framework Core 以及 ASP.NET Core 和 Entity Framework Core 使用的内部和第三方依赖项的所有受支持包。 有关详细信息,请参阅 ASP.NET Core 2.x 的 Microsoft.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.3
的 API。 听起来像是循环定义,然而并不是。 从“基于包的”这个词本身的角度来讲,框架的 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 Framework 和 Mono/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
库也可以引用这些包作为依赖项。