首先,让我们看看通过dotnet yourapp.dll 运行可移植.NET Core 应用程序时会发生什么:
- muxer 可执行文件 (
dotnet.exeon windows) 从host\fxr\ 下dotnet.exe 旁边的文件夹中加载主机框架解析器 (hostfxr) 的一个版本。
- 主机框架解析器查看
yourapp.runtimeconfig.json(或使用dotnet exec 时配置的不同文件)以找出您的目标框架。这会提取框架名称 (Microsoft.NETCore.App) 和版本(例如 1.1.0)。
- 然后它会在
shared\Microsoft.NETCore.App 文件夹中查找可用版本(基于框架名称)。
- 根据
yourapp.runtimeconfig.json 的可用版本和框架版本,它决定使用哪个版本。或者它可能决定出错并抱怨所需的版本不可用。
目前 (.NET Core 1.0),框架解析器将使用适用于 runtimeconfig.json 中指定的主要和次要版本的最新补丁版本,但不会使用低于 runtimeconfig.json 指定的版本。例如。如果运行时配置指定1.1.1,则将使用1.1.2 运行时,但如果只有1.1.0 可用,则会记录错误。也没有跨次要版本的版本前滚。因此,如果只安装了任何1.1.*,运行时配置设置为1.0.0 的应用将触发错误。
对于 .NET Core 2.0,计划进行次要版本前滚,以防找不到匹配的次要版本 - 如果安装了 1.0.5 和 1.1.2 运行时,则运行时配置为 1.0.4 的应用程序将在1.0.5 运行时上运行。如果只安装了1.1.2,相同的应用程序将在1.1.2 上运行。如果只安装了2.0.0,同一个应用程序将无法运行。有关此更改的详细信息和讨论,请参阅 GitHub issue for .NET Core 2+ Version Binding。
让我们看看运行时配置中的值是从哪里来的。当您以框架 netcoreapp1.1 为目标时,您使用的工具将决定:
- 要使用哪个 NuGet 包(+ 版本),以便获得能够编译的编译引用。
- 将哪个版本写入
yourapp.runtimeconfig.json
在csproj文件中,使用的框架版本由属性决定
<RuntimeFrameworkVersion>1.1.2</RuntimeFrameworkVersion>
如果未指定此值,则工具将使用它知道的最新版本
适用于 .NET Core 1.0 和 1.1。
对于 .NET Core 2.0,可移植应用程序将默认使用补丁版本 0,而自包含应用程序将使用工具知道的最新版本。之所以进行此更改,是因为工具(CLI“SDK”/Visual Studio)更新和运行时更新已同时发布,因此应用程序默认需要在目标系统上安装新的运行时。如果未安装该运行时,则会发生错误。如果托管商需要几天时间才能赶上测试和安装更新,那就太糟糕了。该版本仍然可以通过设置<RuntimeFrameworkVersion>explicitly 来强制/要求。
关于包:1.* 工具使用的包是元包。所以引用Microsoft.NETCore.App 或NETStandard.Library 会引入很多其他的NuGet 包。 .NET Core 2.0 和 .NET Standard 2.0 不再是这种情况 - 包是扁平的,包含您需要的一切。此外,当您创建 NuGet 包时,这些包将不再是生成的包的依赖项。它们仅用于编译参考,除了Microsoft.NETCore.App 知道要为自包含应用程序引入哪些额外的包。
以前,使用 NETStandard.Library 版本 1.6.1 构建的库会导致使用 .NET Core 1.0 应用程序包含大量更新的 DLL 文件,这些文件实际上是 .NET Core 1.1 的一部分。我不知道这是否意味着 LTS 政策将涵盖或不涵盖最终使用这些 DLL 的应用程序。而且很难看出它们属于哪个 .NET Core 版本,因为它们源自的包版本通常是 4.0.*、4.1.* 和 4.3.*。
对于 ASP.NET Core 包,这要容易得多,因为它们是版本化的 1.0.* 和 1.1.*,因此您可以查看它们来自哪个“分支”,并且您可以通过指定 NuGet 来更好地控制使用的版本csproj 文件中的包。
回顾一下,让我们回到最初的问题:
- 给定一个具有许多 .Net Core 依赖项的项目。如何确定哪个版本的运行时需要在
最终用户机器?
这里真正的依赖是Microsoft.NETCore.App 的哪个版本被写入yourapp.runtimeconfig.json 文件。必须安装相同主次号和相同或更高补丁号的版本,将使用最新的补丁版本。安装 .NET Core 2.0 解析器时,或者将使用具有相同主编号的最高版本,但将首选主次编号相同的版本。
如果仅安装了具有较新主要版本的运行时,则该应用无法在目标系统上运行。 (例如1.0.5 应用程序和仅2.0.0 运行时)
- 运行时版本是否需要完全匹配,或者最终用户计算机上安装的运行时是否可以比所需版本更新?
运行时配置的版本是最低要求。如需选择正确版本的较新运行时,请参见上文。
- 假设我想坚持使用一些 LTS 版本的运行时。如何确定我需要参考的软件包版本?如何
我可以确保没有引用较新的包吗?
Microsoft.NETCore.App 的版本将自动从目标框架中推断出来(例如netcoreapp1.0 => 1.0.*,补丁版本取决于您使用的工具版本)。要覆盖版本,请按上述方法设置 <RuntimeFrameworkVersion> 属性。
如果新的 NuGet 包被传递引用,例如通过使用 .NET Core 1.0 应用程序中的 Newtonsoft.Json 10.0.0(请参阅 GitHub issue),可能会将一些额外的 DLL 添加到项目的输出中。这些是属于运行时一部分的较新版本的 DLL,但会覆盖运行时中的版本。
如果您确实想确保不使用任何 FTS 版本,则需要在您的 csproj 文件中明确引用所有这些包,以便 NuGet 降级使用的包的版本(并发出包降级警告)。
这里的问题是在 1.0 和 1.1 包中没有没有解决问题的情况。如果将来支持 1.0 和 2.0 但不再支持 1.1 时这将成为一个问题,我们将不得不逐个查看如何处理。 (尽管社区肯定有压力/要求发布更新的 1.1 版本,即使微软不支持)。
如果您使用2.0 或更高版本,这些实现包将从您的应用的依赖关系图中剔除,并且在部署时不再考虑。这是冲突解决逻辑的一部分,该逻辑知道新的平面包包含与各个包相同的 DLL 文件。
- 一旦我知道最终用户机器上需要哪个运行时版本,我如何(以编程方式)确定该版本的
运行时(或更新的、向后兼容的)可用?
- 扫描
dotnet.exe 旁边的shared\Microsoft.NETCore.App 子文件夹并实现与主机相同的逻辑。
- 在
host\fxr 旁边的dotnet.exe 中PInvoke 到最新hostfxr.dll 的本机代码。但这做起来相当复杂。