包、元包和框架Packages, metapackages and frameworks

每个 .Net Core 包都支持以框架形式通过多个 .Net 实现代码运行。其中有些框架是传统框架,例如表示 .NET Framework 的 。而另一些则是新框架,可视为是“基于包的框架”,这种是框架的另外一种新的定义模型。这些基于包的框架整个都是由包组成的,它们自身也被定义成包,这就在包与框架之间形成了一种比较密切的关系。

.NET Core 被分成一组包,它们提供了基元类型,以及更高层的数据类型,应用组合类型和通用实用工具。每一个包都代表着单独的同名程序集。例如, 这个包就包含了 System.Runtime.dll 程序集。

以细粒度方式定义这些包具有以下好处:

  • 细粒度的包可以在它自己的计划内交付,只需完成仅对相关的其他有限的包进行测试即可。
  • 细粒度的包可以提供不同的 OS 和 CPU 支持。
  • 应用可以变得更小,因为没有引用的包不会变成应用发行的一部分。上述某些好处只适用于某些特定场合。例如,.NET Core 的所有包通常都会在同一计划内提供对同一平台的支持。在这种情况下,补丁与更新会以小的单独包的形式发布和安装。由于这种小范围的变化,补丁的验证与时间花费,都可以限制到单个库的需求范围中。

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

  • System.Runtime - 最基础的 .NET Core 包,包括 、String、、Action 和 。
  • System.Collections - 一组(主要)泛型集合,包括 和 Dictionary
  • - 一组用于 HTTP 网络通信的类型,包括 HttpClient 和 。
  • System.IO.FileSystem - 一组用于读写到本地或网络磁盘存储的类型,包括 和 Directory
  • - 一组用于查询对象的类型,包括 EnumerableILookup
  • - 一组用于加载、检查和激活类型的类型,包括 Assembly、 和 MethodInfo通常,包含要比包含各个包更加简单可靠。但是当需要单个包时,可以按以下示例所示的那样来包含它,此示例引用 System.Runtime 包。

元包就是一个 NuGet 包约定,描述了一组意义相关的包。开发团队利用依赖项来描述这一组包。他们通过这一组包来描述一个框架,然后有选择地发布出去。

默认情况下,早期版本的 .NET Core 工具(同时基于 project.json 和 csproj 的工具)指定一个框架和一个元包。但目前,由目标框架隐式引用元包,以便将每个元包绑定到一个目标框架。例如,netstandard1.6 框架引用 NetStandard.Library 1.6.0 版元包。同样,netcoreapp2.1 框架引用 Microsoft.NETCore.App 2.1.0 版元包。有关详细信息,请参阅 。

以某个框架为目标以及隐式引用元包,这实际上是添加了对元包中每一个独立包的引用依赖。这使这些包中的所有库都可用于 IntelliSense(或类似体验),同时也可用于发布应用。

使用元包具有以下好处:

  • 在引用大量细粒度包方面,提供了一种方便的用户体验。
  • Microsoft.NETCore.App - 描述了属于 .NET Core 发行版的部分库。也就是 。它依赖于更小的 NETStandard.Library

  • Microsoft.AspNetCore.App - 包含来自 ASP.NET Core 和 Entity Framework Core 的所有受支持的包(包含第三方依赖项的包除外)。有关详细信息,请参阅 。
  • Microsoft.AspNetCore.All - 包含来自 ASP.NET Core、Entity Framework Core 以及 ASP.NET Core 和 Entity Framework Core 使用的内部和第三方依赖项的所有受支持包。有关详细信息,请参阅 。
  • Microsoft.NETCore.Portable.Compatibility - 一组兼容外观,使基于 mscorlib 的可移植类库(PCL) 得以在 .Net Core上运行。

每个 .NET Core 包支持一组运行时框架。框架描述了一组可用的 API(以及潜在的其他特性),所以你可以在指定一个目标框架时使用这些功能。添加新的 API 时,它们就会进入版本控制流程。

例如, 支持以下框架:

  • .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.3netstandard1.3 为目标的包(或兼容的框架,如 netstandard1.0)定义了适用于 netstandard1.3 的 API。听起来像是循环定义,然而并不是。从“基于包的”这个词本身的角度来讲,框架的 API 定义是来自于包的。框架本身并不定义任何 API。

其次,是这个双向关系中的资产选择。包可以包含多个框架的资产。对于一组包和/或元包的引用,框架需要决定它应选择哪些资产,例如,是 net46 还是 netstandard1.3选择正确的资产很重要。例如, 资产可能并不与 .NET Framework 4.0 或 .NET Core 1.0 兼容。

在 .Net Core 基础之上,基于包的框架主要有两个:

  • netstandard
  • netcoreapp

.NET Standard(目标框架名字对象netstandard)框架表示在 基础之上生成并由其定义的 API。如果构建的库将用于在多个运行时上运行,就应将此框架作为目标。这样便可在任何一种兼容 .NET 标准的运行时上受支持,例如 .NET Core、.NET Framework 和 Mono/Xamarin。每个运行时都支持一组 .NET Standard 版本,具体取决于实现的 API。

netstandard 框架隐式引用 NETStandard.Library 元包。例如,以下 MSBuild 项目文件指示项目以 netstandard1.6 为目标,其引用 元包。

但项目文件中的框架和元包引用不需要匹配,并且可使用项目文件中的 <NetStandardImplicitPackageVersion> 元素指定低于元包版本的框架版本。例如,以下项目文件有效。

面向 netstandard1.3 却使用 NETStandard.Library 1.6.0 版本,这一点很奇怪。然而,这是一个有效的用例,因为元包支持更旧的 netstandard 版本。可能恰好你已将 1.6.0 版的元包进行了标准化,然后将其用于所有库,而这些库可以面向各种 netstandard 版本。使用此方法,只需还原 NETStandard.Library 1.6.0,无需加载早期版本。

反之,将 设为目标,却使用 1.3.0 版的 NETStandard.Library,这是无效的。不能将高版本的框架设为目标,却使用低版本的元包,因为低版本的元包不会对高版本的框架公开任何资产。元包的版本控制方案断言元包匹配它们所描述的框架的最高版本。凭借版本控制方案,NETStandard.Library 的第一个版本是 v1.6.0,因为它包含 netstandard1.6 资产。而上例中使用 v1.3.0 版本只是为了保持对称,实际上它并不存在。

.NET Core 应用程序(目标框架名字对象netcoreapp)框架表示 .NET Core 发行版及其提供的控制台应用程序模型附带的包和相关 API。.NET Core 必须使用此框架,因为必须要使用其中的控制台应用程序模型。同时只运行于 .Net Core 平台的库也应使用此模型。使用此框架后,所有应用和库将只能够在 .Net Core 上运行。

Microsoft.NETCore.App 提供的大部分其他库还可以使用 netstandard 作为目标,如果其他 netstandard 库满足这些框架的依赖项的话。这意味着,netstandard 库也可以引用这些包作为依赖项。