如何打包面向 .NET Core 的可移植 .NET 库?

How to package a portable .NET library targeting .NET Core?

如何以现代的通用方式打包可移植的 .NET 库?假设我有一个 AnyCPU 程序集,我希望它可用于支持 .NET Core API 表面的任何 .NET 平台,例如 .NET Framework 4.6 和通用 Windows 平台。

This is a series of questions and answers that document my findings on the topic of modern NuGet package authoring, focusing especially on the changes introduced with NuGet 3. You may also be interested in some related questions:

这个答案建立在 的基础上。请先阅读链接的答案以更好地理解以下内容。

要发布可移植的 .NET 库,您需要创建具有以下结构的 NuGet 包:

\---lib
    \---dotnet
            MyPortableLibrary.dll
            MyPortableLibrary.pdb
            MyPortableLibrary.XML

所有三个文件都将来自发布构建配置下项目的构建输出目录。

上面结构中的dotnet目录有一个特殊的含义——它向NuGet表明目录中的文件将在你的包的所有依赖项兼容的任何平台上使用。因此,您的包可自动用于支持所有依赖项的任何 .NET 平台(例如 .NET Core)。

关键的下一步是确定依赖项列表。由于 a package management issue 无法简单地声明对 .NET Core 本身的依赖(.NET Core 是所有 .NET 平台共享的 API 表面)。相反,您必须手动确定每个 .NET Core 组件依赖项并将其添加到 nuspec 文件中。

.NET Core 包的依赖检测过程包括两个步骤:

  1. 确定您的库引用的 .NET Core 程序集。
  2. 确定包含这些程序集的 NuGet 包。

Visual Studio 没有提供您需要的信息。相反,您需要构建您的库并检查生成的 DLL 文件。以下 PowerShell 脚本将显示 .NET 程序集的引用:

Get-ChildItem MyPortableLibrary.dll | % { [Reflection.Assembly]::LoadFile($_.FullName).GetReferencedAssemblies() | % { $_.Name + ".dll" } }

此命令的输出将是程序集名称列表,例如:

System.Runtime.dll
System.Resources.ResourceManager.dll
System.Numerics.Vectors.dll

获得列表后,打开项目目录中的project.lock.json 文件。此文件包含有关项目使用的所有 NuGet 包的信息。在其他数据中,您会发现 JSON 的各种块,例如:

"System.Numerics.Vectors/4.1.0": {
    "dependencies": {
        "System.Globalization": "[4.0.10, )",
        "System.Resources.ResourceManager": "[4.0.0, )",
        "System.Runtime": "[4.0.20, )",
        "System.Runtime.Extensions": "[4.0.10, )"
    },
    "frameworkAssemblies": [
        "mscorlib",
        "System.Numerics"
    ],
    "compile": {
        "ref/net46/System.Numerics.Vectors.dll": {}
    },
    "runtime": {
        "lib/net46/System.Numerics.Vectors.dll": {}
    }
},

这个JSON块表示"compile"下列出的汇编文件是由顶级值(System.Numerics.Vectors版本4.1.0)中列出的包提供的。使用此信息将每个引用的程序集映射到 NuGet 包。请注意,虽然包和程序集名称通常相同,但情况并非总是如此!

对于不属于 .NET Core 的任何 NuGet 包,您可以跳过上述过程,因为您已经知道您所依赖的确切包。此处描述的依赖项检测逻辑只是必需的,因为由于上面链接的问题,您不能直接在 .NET Core(Microsoft.NETCore 包)上声明依赖项。

现在根据以下示例简单地列出 nuspec 文件中的所有依赖项:

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
    <metadata minClientVersion="3.2">
        <id>Example.MyPortableLibrary</id>
        <version>1.0.0</version>
        <authors>Firstname Lastname</authors>
        <description>Example of a portable library with NuGet package dependencies.</description>
        <dependencies>
            <dependency id="System.Numerics.Vectors" version="4.1.0" />
            <dependency id="System.Resources.ResourceManager" version="4.0.0" />
            <dependency id="System.Runtime" version="4.0.20" />
        </dependencies>
    </metadata>
    <files>
        <file src="..\bin\Release\MyPortableLibrary.*" target="lib\dotnet" />
    </files>
</package>

就是这样!生成的包可用于任何兼容的 .NET 平台,例如 .NET Framework 4.6 或通用 Windows 平台。请记住在创建 NuGet 包之前使用 Release 配置构建您的解决方案。

示例库和相关打包文件available on GitHub。这个答案对应的解决方案是PortableLibrary.

请参阅 Lucian Wischik's blog 以更深入地了解在此类 NuGet 包上运行的逻辑。

在过去的几个月里,关于制作可移植 .NET 框架 libraries/NuGet 包的最佳方式似乎有很多争论。如果您可以稍等片刻,您会想要阅读有关 .NET Standard. Immo Landwerth wrote a detailed blog post introducing the .NET Standard in September of 2016. Support for .NET Standard 2.0 in .NET Core is expected to arrive in early 2017, in the same timeframe as Visual Studio 2017, which is currently in Release Candidate 阶段的信息。 .NET Standard 2.0 将由 .NET Framework、.NET Core 和 Xamarin 实现。它还将包括 .NET Framework 二进制文件的兼容性垫片,这将使采用更容易。

更新:

Oren Novotny 有一篇关于此主题的非常有用的博客 post:Multi-targeting the world: a single project to rule them all。他讨论了 .NET Standard 以及如何使用 Visual Studio 2017 实现多目标。博客 post 是在 Visual Studio 2017 的 RTM 之前发布的,但它非常有帮助。