.NET Core中的包、元包与框架
本文为翻译文章,原文:Packages, Metapackages and Frameworks
.NET Core是一个由NuGet包组成的平台。一些产品受益于细粒度包的定义,也有一些受益于粗粒度包的定义。为了适应这种二重性,.NET Core平台被分为一组细粒度的包(package)以及一些被称为元包(metapackage)的较粗粒度的包。
每一个.NET Core包支持多个.NET运行时,它们代表着不同的框架。这些框架既包括传统的.NET Framework(如net4.6),也包含基于包的新框架,这些新框架建立了定义框架的新模型。这些基于包的框架完全由包定义而成,包与框架之间形成较强的关联关系。
包
.NET Core由一组包构成,这些包提供了基元类型、高级数据类型、应用程序组成类型和一些常见的实用工具。每一个包表示一个和包同名的程序集,如, System.Runtime包中含有System.Runtime.dll程序集。
定义细粒度的包有如下好处:
-
细粒度的包在开发、测试过程中与其它包的关联有限
-
细粒度的包可以提供对不同操作系统和CPU的支持
-
细粒度的包可以只依赖某个特定的库
-
在发布应用时,未被引用的包不会成为应用的一部分,因此应用程序会有更小的体积
有些细粒度包的优点只会在特定场景中表现出来。如,通常.NET Core 的所有包会在同一计划内提供对同一平台的支持。这种情况下,补丁会以小的单个更新包的形式发布和安装。由于这种小范围的变化,验证补丁是否可用所花费的时间,可以限制到对单个库的需求中。
下面列出了.NET Core平台上的一些关键NuGet包:
-
System.Runtime - 这是最基本的.NET Core包,包括Object, String, Array, Action 和IList<T>.
-
System.Collections - 表示一组常用泛型集合,包括List<T> 和Dictionary<K,V>.
-
System.Net.Http - 用于HTTP通讯的类型,包括HttpClient 和HttpResponseMessage.
-
System.IO.FileSystem -用于读写基于磁盘的本地或网络存储类型,包括File和Directory.
-
System.Linq - 用于查询对象,包括including 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包约定,用于描述一组放在一起有意义的包。 这些包是通过依赖项来被描述的。可以通过为这组包指定一个框架来建立一个框架(这话拗口,原文:They can optionally establish a framework for this set of packages by specifying a framework.)。
以前版本的.NET Core工具(project.json和基于csproj的工具)在默认情况下会指定一个框架和元包 。不过,现在,元包被目标框架隐式引用,这样一来每个元包都和目标框架关联在一起。例如,netstandard1.6
框架引用了NetStandard.Library 1.6.0
元包。类似的,netcoreapp1.1
框架引用了Microsoft.NETCore.App 1,1.0
元包。更过信息,参考.NET Core SDK中元包的隐式引用。
指定一个框架会隐式实现对元包引用,同时也会添加对元包依赖项的引用。这样,元包中的所有类库都能够被IDE智能感知,也可以被打包到你的应用中。
使用元包有以下好处:
-
在引用大量细粒度包时有更好的用户体验
-
定义了一组经过测试且运行良好的包(包括指定的各种版本)
.NET 标准库元包(.NET Standard Library metapackage):
- NETStandard.Library - 表示.NET标准类库的一部分。所有的.NET 实现(如,.NET Framework、.NET Core 和 Mono)都支持.NET 标准类库。
NETStandard.Library
用于建立netstandard
框架。
.NET Core核心元包有:
-
Microsoft.NETCore.App - .NET Core发行版本类库的一部分,用于建立 .NETCoreApp框架,它依赖
NETStandard.Library
。 -
Microsoft.NETCore.Portable.Compatibility - 一组基于mscorlib的运行于.NET Core上的可移植类库
框架
每个.NET Core包都支持多个运行时框架。这些框架描述一组可用于你所指定的框架的API(和一些其它特征)。当加入新的API时,这些框架的版本号也会发生相应的变化。
例如, System.IO.FileSystem 支持以下框架:
- .NETFramework,Version=4.6
- .NETStandard,Version=1.3
- 6 Xamarin platforms (如, 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
)的软件包定义该框架上可用的API。这看起来像是循环定义,但不是。基于包的框架上的API由包来定义,框架本身并不定义任何API。
其次,是这种双向关系中的第二部分,资产选择(asset selection)。包可以包含用于多框架的资产。对于一组包或者元包的引用,框架需要决定选择哪种资产,如net46
或netstandard1.3
。选择正确的资产是很重要的。如,一个net46
资产可能不兼容.NET Framework 4.0
或 .NET Core 1.0
。
上图描述了这种双向关系。API指定并定义框架。框架选择资产。资产提供具体的API实现。
.NET Core
平台上使用的两个主要的基于包的框架是:
-
netstandard
-
netcoreapp
.NET 标准
.NET标准(目标框架名:netstandard)框架是指基于.NET标准库(.NET Standard Library)而定义和创建的API。这些库计划支持以.NET 标准框架为目标框架的多个运行时。它们支持任何与.NET标准(.NET Standard )兼容的运行时,如.NET Core、.NET Framework和Mono/Xamarin(下面附上一张图作为补充)。每个运行时都支持一组.NET Standard版本,具体取决于它们所要实现的API。
标准框架隐式引用NETStandard.Library
元包。如,下面的MSBuild项目文件显示了当前项目的目标框架是netstandard1.6
,这个框架引用.NET Standard Library version 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> </PropertyGroup> <ItemGroup> <NetStandardImplicitPackageVersion Include="NetStandardLibrary" Version="1.6.0" /> </ItemGroup> </Project>
netstandard1.3
框架使用NETStandard.Library 1.6.0
可能看起来比较奇怪。但这种使用情况是合法的,因为元包向后兼容低版本的netstandard
。若你已经将1.6.0版本的元包并将其应用到自己的面向多个netstandard
版本的库中。通过这种向后兼容的方式,你只需还原(restore)NETStandard.Library1.6.0
而无需关注更早的版本。
与此相反,netstandard1.6
引用NETStandard.Library 1.3.0
是不被允许的,因为低版本的元包不会公开任何构建高版本框架的资产。元包资产的版本控制使得元包和它们所描述的框架的最高版本相匹配。借助版本控制,NETStandard.Library
的第一个版本是1.6.0,它包含netstandard1.6
的资产。上述例子中的1.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 libraries
可以添加对这些包的引用。
结语
由于水平有限,翻译内容难免有错误和不足之处,希望大家提出改进意见。文章最后是自己建立.NET Core控制台程序的引用包截图和项目配置文件,大家可以作为辅助理解文章内容的补充材料。
项目包引用
项目文件
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>netcoreapp1.1</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="System.Collections.NonGeneric" Version="4.3.0" /> <PackageReference Include="System.IO.FileSystem.AccessControl" Version="4.3.0" /> </ItemGroup> </Project>