65.9K
CodeProject 正在变化。 阅读更多。
Home

将 .NET Framework 库移植到 .NET Core

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (21投票s)

2017年6月5日

CPOL

17分钟阅读

viewsIcon

83353

.NET Core/.NET Standard 是 .NET 平台的最新版本。本教程将带您进入这个新世界,并提供实用建议,助您成功将 .NET Framework 库移植到 .NET Core。

本文的更新时间为 2017 年 6 月,与互联网上许多其他 教程指南不同。
任何提到“project.json”的内容都已过时,VS 2017 需要 .csproj 文件。

介绍

我通过在 FluentFTP 库上的工作,被引入了 .NET Core/.NET Standard 的世界。我需要将其移植到 .NET Core,因此我决定学习这项技术。毕竟,它只是 .NET Framework 的一个简化版本,能有多难?事实证明这是一次艰难的经历,在找不到任何相关教程后,我决定自己写一篇。我不认为这是最好的方法,但这是对我有效的方法。

我的库是基于 .NET Framework 4.0 构建的,是移植的理想选择,因为它不使用 WindowsForms 或任何 UI 控件,而这些控件目前在 .NET Core 上不可用。您只能使用核心数据类型、集合、文件 I/O、XML、图像、计时器、进程等。几乎所有构建功能齐全的控制台应用或后端库所需的内容。

您可能知道 .NET Core 是 .NET Framework 的 开源版本,由 Microsoft 构建。它将整个 .NET Framework 分割成托管在 Nuget 上的“包”。每个包会拉入所需的依赖项包,使您的库保持精简。

移植这个相对简单的库是一次艰难的经历,我决定写下我的经验和技巧,以帮助开发人员将他们的技术移植到 .NET Core,从而为该平台做出贡献。您可以在 Awesome .NET Core 项目中浏览当前可用的 .NET Core 库。如果您已经将一个很棒的库移植到了 .NET Core,请不要忘记通过 fork 并 提交 PR 来将其添加到列表中!

本文以教程的形式编写,旨在帮助您完成自己的项目,但我将使用 FluentFTP 项目的屏幕截图和示例文件,因为它为每个步骤提供了实际示例。

回顾

简要回顾 .NET Core 及其与 .NET Framework 相比的优势。

  • 它具有跨平台性,因此您的应用程序可以在 Windows、Unix、Mac 等上运行。我还没有看到合法的用途,因为如果您有一个服务器端应用程序,您就可以直接在具有完整 .NET Framework 的 Windows Server 上运行它。但是,使用 .NET Core,您现在可以在 Unix 上运行它,这是一个免费的操作系统。
  • 它具有模块化,因为整个 .NET Framework 被切成小块并上传到 Nuget。因此,您的应用程序必须指定其依赖项包,这些包又拥有自己的依赖项。当您从 nuget 安装应用程序时,它只下载所需的包,不多不少
  • 在线,这意味着最好让您的应用程序运行的每个系统都连接到互联网。如果您想要离线功能,可以在编译时将依赖项与您的应用程序一起打包,但这样您就无法获得基于 nuget 的更新。

要求

  • 您需要 Visual Studio 2017 Community Edition 或更高版本来编译库的 .NET Core 版本。它必须安装在联网的 PC 上,并且在您登录后永久免费。您可以使用 VS 2015,但它已过时且不推荐。
  • 您还需要Visual Studio 2012 或 2015 来编译库的 .NET Framework 版本。您可以像我一样在单台机器上同时安装 2012 和 2017。
  • 您需要Windows 7 或更高版本,因为所有步骤都旨在在 Windows 机器上构建,使用批处理文件等。我仍然使用 Windows 7,所以我建议使用它。在 Unix 上构建不在本文讨论范围内,因为它需要 VS Code,这是 Microsoft 完全不同的开源 IDE,具有完全不同的项目配置(这是最困难的部分)。
  • 您需要NuGet 位于C:\Tools\Nuget\nuget.exe 以创建您的 nuget 包
  • 您将需要 NuGet Package Explorer 来查看最终的 nuget 包(.nupkg),并确保所有元数据和库都已成功拉取。如果您不打算发布到 nuget,请跳过此项。

目标

在本教程中,我们的目标是为库的 .NET Framework 和 .NET Core 版本使用单个代码库,使用 #if 指令有条件地为特定平台编译代码。由于在单个 VS 2017 项目中很难实现这一点,我 resort 于使用两个项目,一个用于 .NET Fx 的 VS 2012 项目,一个用于 .NET Core 的 VS 2017 项目。两个项目都引用相同的代码库,允许共享代码和共享修复。无需维护两个代码库并手动同步代码。

我们还将致力于使用相同的两个项目和单个代码库来定位多个 .NET Framework 版本。您始终可以使用相同的方法稍后添加更多版本。

平台 Binaries 文件夹 解决方案
.NET 2.0 net20 FluentFTP_NET_VS2012.sln
.NET 4.0 net40 FluentFTP_NET_VS2012.sln
.NET 4.5 net45 FluentFTP_NET_VS2012.sln
.NET Core 5.0 dnxcore50 FluentFTP_Core_VS2017.sln
.NET Standard 1.6 netstandard1.6 FluentFTP_Core_VS2017.sln

我们最终将创建一个NuGet 包,将我们的库发布到 NuGet,我将介绍创建有效的 .nuspec、构建 nuget 包,甚至在 NuGet 上发布库所需的步骤。您的库将使用 NuGet 安装在 VS 2010、VS 2012 和 VS 2017 中。这是使用优秀的 NuGet Package Explorer 查看时的结果 .nupkg

.NET Core vs .NET Standard

.NET Core 和 .NET Standard 之间的区别如下:

  • .NET Standard 是一个“标准化”的 .NET 类集合,可在所有 .NET 平台(无论是 .NET Framework、.NET Core、Xamarin、PCL 还是其他)上使用。所谓的“一个库,统治所有” (参见下面 Microsoft 的图)。关于版本,.NET Standard 1.4 已足以满足我们的库需求,但是,我们实现 FTPS 需要 SslStream 类,因此我们不得不采用 .NET Standard 1.6。
  • .NET Core 只是 .NET Standard 的一种实现,并包含一些额外功能,但这些都超出了本文的范围。以“dnxcore50”标识符发布到 nuget。

引用 Jon Skeet 的话:

引用

.NET Core 是 .NET Standard 的一种实现它可以在多个操作系统上使用,但这并不相同——还有其他 .NET Standard 的实现。

因此,如果您创建一个 .NET Core 库,它将可以访问 .NET Core 中实现的但不属于 .NET Standard 的内容,并且您的库将兼容 .NET Standard 的其他实现,例如 Xamarin、Tizen、完整的 .NET 桌面框架等。

简而言之:为了实现最大的可移植性,请让您的库目标为 .NET Standard。

下载

如果您想下载完成的项目,可以获取 FluentFTP 源文件 ZIP 17.4.2(截至 2017 年 6 月 5 日的最新版本),或者您可以从 github 仓库 下载/fork 源文件。

包含在 ZIP 和 Github 仓库中

  • 适用于 VS 2012 和 VS 2017 的解决方案和项目文件
    • 适用于 VS 2012 和 .NET Framework:FluentFTP_NET_VS2012.slnFluentFTP.csproj
    • 适用于 VS 2017 和 .NET Core:FluentFTP_Core_VS2017.slnFluentFTP_Core.csproj
  • 共享代码库(FluentFTP 文件夹)
  • 用于更新 .NET Core 包(restore.bat)和构建 nuget 包(build_nuget.bat)的批处理文件
  • 用于构建 nuget 包的 Nuspec 文件(FluentFTP.nuspec
  • 用于强命名签名的密钥文件(sn.snk

仅包含在 ZIP 中

  • 预先构建的二进制文件,用于显示文件夹结构(FluentFTP\bin
  • 预先构建的 nupkg 文件,用于显示最终的 nuget 包(FluentFTP\nuget\FluentFTP.17.4.2.nupkg

入门

为 VS 2012 和 VS 2017 创建项目

使用任何方法为 VS 2012 和 VS 2017 创建 C# 库项目。您可以创建新项目,或使用我提供的项目作为模板。

接下来,为多个 .NET Framework 版本添加构建配置;打开 VS 2012 项目并通过 UI 添加构建配置,或手动修改文件(我就是这样做的)。下面详细列出了相关文件和所需的更改。

FluentFTP_NET_VS2012.sln

这里我们有 3 组构建配置

  • Debug / Release - 构建 .NET 4.5 版本
  • DebugNET4 / ReleaseNET4 - 构建 .NET 4.0 版本
  • DebugNET2 / ReleaseNET2 - 构建 .NET 2.0 版本

FluentFTP.csproj

这里,我们有与上述相同的构建配置,每个配置有一个 PropertyGroup。相关选项是:

  • DefineConstants - 根据需要添加条件编译常量(例如,NETFX, NET2, NETFX45
  • OutputPath - 遵循 nuget 指定的标准命名系统(.NET 2 为 net20,依此类推)
  • TargetFrameworkVersion - 您需要的 .NET 版本

FluentFTP_Core.csproj

解决方案文件无需更改 VS 2017,但是您必须在 Notepad++ 中打开 .csproj 文件,并确保已填充某些关键变量。

  • TargetFrameworks - 非常重要。在这里,我使用了 netstandard1.6dnxcore50(.NET Core 5.0),因为我想同时支持这两个框架。如果您需要 .NET Standard 1.4,则需要在此处添加它,或者如果您只想支持 .NET Standard,则删除 dnxcore50 标识符。
  • DefineConstants - 添加 CORE 常量以进行共享代码库的条件编译。
  • NetStandardImplicitPackageVersion - 我们使用 .NET Standard 1.6。
  • TargetFrameworkIdentifier - 我们使用 .NET Standard。
  • TargetFrameworkVersion - 我们使用 .NET Standard 1.6。
  • AssemblyOriginatorKeyFile - 如果您需要强命名签名,请在此处指定您的密钥文件。
  • PackageReference(s) - 非常重要。您的库所需的 .NET Core 框架中的哪些程序集。
    • System.IO 开始,稍后根据需要添加更多。
    • 要了解应添加哪些引用,请参阅“引用 .NET 类”部分。
    • 要了解要填写的版本号,请访问程序集的 nuget 页面,并使用提供的最新版本。
    • 一些程序集是隐式包含的,例如 System.Runtime System.Collections。但是,您仍然需要在 .nuspec 文件中列出它们。

创建 NUSPEC 文件

如果您不打算发布到 nuget,请跳过此项。我建议使用 Notepad++ 手动创建 .nuspec 文件,并以 我的 作为模板。关键参数如下所示并已详细说明:

  • <frameworkAssemblies> 包含您需要的任何额外 .NET Framework 引用。您需要为要为其发布 .NET Framework 版本的每个版本添加一个条目,因此在本例中是 net20net40 net45
  • <dependencies> 是较新 nuget 版本支持的一个较新标签,用于指定每一个 .NET Core 依赖项,即使是像 System.Runtime System.Collections 这样的显式依赖项。
    • 每次添加 .NET Core 依赖项时,都必须在此处添加(请参阅“引用 .NET 类”部分)。
    • 虽然我为 .NET Framework 版本也放置了 <group> 条目,但您不必填充它们,但请添加它们,以免较新版本的 nuget 失败。

移植您的代码

一旦设置好所有项目,就可以开始编译并将库移植到 .NET Core 了。启动 VS 2017 并打开您的 .NET Core 解决方案。如果您使用的是我的模板,那么您需要的文件是 FluentFTP_Core_VS2017.sln。点击编译。如果您使用自己的库,您应该会看到数百个错误。别担心!大多数错误都可以轻松解决。

有用技巧

使用 #if CORE 标记 .NET Core 所需的替代代码。一种常见模式是:

#if CORE
    socket.Close();
#else
    socket.Dispose();
#endif

以及“禁用”代码以编译到您的 .NET Core 版本中:

#if !CORE
    // advanced code that only works on .NET framework
#endif

如果您也要为 .NET 2.0 编译,那么您需要用以下方式保护您的导入:

#if (CORE || NETFX)
using System.Threading;
#endif
#if (CORE || NETFX45)
using System.Threading.Tasks;
#endif

常见错误

  • 找不到类型 X - 您需要找到所需的类属于哪个 .NET Core 程序集,并在您的 .NET Core .csproj.nuspec 文件(如果发布到 nuget)中添加引用。请记住,每次添加引用后都要运行 restore.bat,否则编译错误将不会消失!有关更多信息,请参阅“引用 .NET 类”部分。
  • 类型 X 中未提供方法 X - 它可能已被重命名或删除。要进行检查,只需打开相关类的 .NET Core API 参考,并手动查看其成员。

一些关键点

  • 反射在 .NET Core 中可用,但您需要引用以下内容:
    • System.Reflection
    • System.Reflection.Primitives
    • System.Reflection.Extensions
    • System.Reflection.TypeExtensions
    • 如果您需要 IL 生成,则添加 System.Reflection.Emit System.Reflection.Emit.ILGeneration
  • 任务和线程以及 async/await 可用,但您需要引用以下内容:
    • System.Threading.Thread
    • System.Threading.Tasks
  • 套接字可用,但您需要引用以下内容:
    • System.Net.Sockets.
    • System.Net.Security 如果您想要 SslStream
    • 此外,socket.Close() 现在是 socket.Dispose()
  • 异步受支持(请参阅上面的要点),但旧的 IAsyncResult基于的异步不支持。您必须使用 #if 标签禁用这些部分,或升级到 async/await
  • 序列化(通过将数据转换为二进制)不受支持,但 XML 和 JSON 序列化支持。(请参阅 System.Runtime.Serialization.Xml System.Runtime.Serialization.Json
  • 位图不可用(System.Drawing.Image),但对于 PointRect 等几何图元,请参阅 System.Drawing.Primitives
  • 加密可用,但许多类已重命名和重构,例如 new SHA1CryptoServiceProvider() 现在是 SHA256.Create()
  • StackTrace 可用,但您需要额外的 System.Diagnostics.StackTrace,因此如果它不是必需的,您可能希望从代码中删除它,而不是添加额外的依赖项。
  • DataTable 和 DataSetSystem.Data 命名空间中不可用,但提供者模型和 SQL 客户端等其他功能可用。
  • XAML 不受支持,但如果您以 UWP 为目标,则必须使用 Windows RT XAML API。

一些关键的缺失组件(来源

  • System.AppDomain - 应用域
  • System.Drawing.Image - 图形、位图图像
  • System.DirectoryServices - LDAP、Active Directory
  • System.Transactions - 事务上下文、分布式事务
  • System.Xml.Xsl - XSLT
  • System.Xml.Schema - XSD
  • System.Net.Mail - 发送电子邮件
  • System.Runtime.Remoting - 远程处理、RPC
  • System.Runtime.Serialization.Xml - 二进制序列化
  • System.IO.Ports - 串行端口
  • System.Workflow - Windows Workflow Foundation

引用 .NET 类

许多类和方法被重命名或删除,以清理或现代化 API。即使是未重命名的类,也需要以特定方式引用,.NET Core 编译器才能将其包含在您的项目中。因此,当您想使用 .NET 类时,流程如下:

  • 在在线 .NET Core 网站上查找类(例如,Directory),或使用以下 其他工具
  • 在标题中,您会找到程序集:System.IO.FileSystem.dll - 这告诉您该类所需的 nuget 包(请参见下图)
  • 将该 nuget 包添加到您的 .NET Core “.csproj” 和 “.nuspec” 文件中。(请参见下图)
  • 运行“dotnet restore”命令以下载新的程序集(运行 restore.bat
  • 添加一个 import 并在您的代码中使用该类。
  • 希望此时它能编译,如果不行,尝试使用最新版本的 .NET Standard(当前为 1.6)。

.NET Core 文档

将引用添加到您的 .NET Core .csproj 中

将引用添加到您的 .nuspec 中

创建您的 NuGet 包

一旦您的库能够编译和构建,您可能希望将其发布到 NuGet。如果您还没有了解,NuGet 是一种很酷的方式,可以即时自动地将库(及其所有依赖项)拉入 .NET 项目。它由 Microsoft 收购,并在 Visual Studio 中得到官方支持。

要测试创建 nuget 包,请运行 build_nuget.bat。任何与 nuget 相关的错误都会显示在控制台窗口中。修复它们。一切正常后,您应该会在控制台中看到“成功创建包 XYZ”。

常见错误

  • 参数缺失。 Nuspec 格式一直在变化,如果您缺少参数,在尝试构建时 nuget.exe 会通知您。使用 Google 查找新参数的默认值。
  • 程序集引用不正确。 Nuget 有时会警告您 <dependencies> 中指定的程序集在 Nuget 上不存在。打开 nuget 网站(或 这些工具中的一个)并搜索您需要的 .NET Core 程序集的准确名称。

打开您的 nuget 目录(在我的模板中位于 FluentFTP\nuget),然后双击新创建的 .nupkg 文件,在 NuGet Package Explorer 中打开您的 NuGet 包。

常见错误

  • 元数据不正确。 请记住,您的 nuspec 必须是有效的 XML,因此您不能在元数据字符串中使用 <>
  • 文件树(右侧)中没有显示任何程序集。 检查 .nuspec 文件中的 <files> 部分。要包含某个目录中的所有文件,请使用此方法:
  • <files>
       <file src="bin\Release\**" target="lib" />
    </files>

如果一切顺利,您应该会看到类似这样的内容:

发布到 NuGet

我确信有更自动化的方法可以实现这一点,但以下是我每次需要将库的更新推送到 NuGet 时使用的步骤。

编译您的库

  1. AssemblyInfo.cs 中增加版本号
  2. FluentFTP.nuspec 中增加版本号
  3. FluentFTP_Core.csproj 中增加版本号
  4. 打开 VS 2012 - 编译“NET Release”、“Release_NET2”、“Release_NET4”
  5. 打开 VS 2017 - 编译“Release”

构建并发布 Nuget 包

  1. 运行“build_nuget.bat”脚本以创建 .nupkg
  2. 访问 nuget 上的此链接以下载包
  3. 登录到您的 nuget 帐户(如果没有,请创建一个)
  4. 浏览到您的 nuget 包(.nupkg
  5. 添加“发布说明”(支持有限的 markdown,例如项目符号列表)
  6. 上传包
  7. 您将在包“发布”时收到一封电子邮件。

总结

虽然 .NET Core 生态系统正在被 Microsoft 积极改进,但它仍然需要大量工作,尤其是在文档方面,才能使其易于使用。移植一个简单的库花费了一个多星期的辛勤工作,尽管我的教程应该有助于项目配置和编译,但在移植和测试阶段,开发人员仍然需要付出很多努力。应该有一个“一键式”应用程序移植工具,可以将您的项目重构为与 .NET Core 兼容。

最困难的部分是将其编译成 nuget 兼容的格式(发现 dnxcore50 标识符)以及为库的 .NET Framework 和 .NET Core 版本创建有效的“合并”nuspec。

我希望我的教程能帮助您将 .NET 库移植到 .NET Core,如果它有帮助,请在下方评论!

谢谢。

有用链接

在移植过程中,您将需要这些工具来帮助查找 .NET Core 类与 .NET Framework 类之间的对应关系。有时一个类已被重命名,有时它根本不存在。

  • .NET API 浏览器 - Microsoft 的出色工具,可帮助您查找包含在 .NET Core / .NET Standard 中的类。您可以输入一个类的名称,例如“stream”,以查看包含哪些同名类。
  • 反向包搜索 - 与上述类似,但您可以搜索类和成员!输入一个类的名称然后按 Enter(它没有“边输入边查找”功能)。它会列出所有同名的类以及该类位于哪个 .NET Core / .NET Standard 版本的信息。
  • NuGet 搜索 - 一旦您知道所需的 .NET Core 程序集,就可以在 nuget 上搜索以获取确切的包名称,并获取最新版本,然后将其添加到您的 .nuspec.csproj 中。
  • .NET API 可移植性分析器 - Microsoft 的一个工具,用于分析您的项目并突出显示 .NET Core 中不可用的类。我从未使用过它,所以没有评论。
  • 移植 FluentFTP - 将 FluentFTP 从 .NET Framework 4.0 移植到 .NET Core 5.0 和 .NET Standard 1.6 所需的更改。链接指向我关于此的 github 提交历史记录。

历史

  • 2017 年 6 月 5 日 - 文章的第一个版本,包含简介、回顾、要求、目标、.NET Core vs .NET Standard、下载、入门、引用 .NET 类、创建 NuGet 包、发布到 NuGet、有用链接和摘要。
  • 2017 年 6 月 6 日 - 在顶部添加了“最新”注释。将 NuGet 添加到要求列表中。将下载内容分为仅在 ZIP 中可用和通用内容。
  • 2017 年 6 月 9 日 - 修正了关于 .NET Core“仅在线”的说明。提及离线也可行。
© . All rights reserved.