相关文章推荐
胆小的豆浆  ·  Microsoft .NET ...·  4 天前    · 
耍酷的沙滩裤  ·  IIS8 安装 ...·  4 天前    · 
想发财的遥控器  ·  部署Microsoft .NET ...·  4 天前    · 
千杯不醉的黄瓜  ·  uart与usart区别 - ...·  3 周前    · 
心软的香菜  ·  vue ...·  1 月前    · 
光明磊落的镜子  ·  第二章:25+ Python ...·  1 年前    · 

在本章节中,我们将展示如何将应用程序迁移到最新版本的 .NET。 你可以从 .NET Framework、.NET Core 或 .NET 5 迁移到 .NET 7。 我们将介绍一种工具,在大多数情况下,它都可以为你完成所有的工作。 如果你的应用程序具有特殊情况或复杂的依赖项,我们还将展示如何手动执行整个迁移过程。 此外,我们还将探讨在将现有应用程序从 .NET Framework 迁移到 .NET 时可能面临的最常见问题和挑战。

从 .NET Core 或 .NET 5 迁移

如果你已在使用 .NET Core 或 .NET 5 平台,就可以轻松更新应用程序以适用于最新版本的 .NET。 如果是这种情况,则在 Visual Studio 中,只需右键单击“解决方案资源管理器”中的项目,然后选择“属性”。 在“应用程序”>“常规”>“目标框架”下,选择“.NET 7.0”。 保存并重新生成应用程序。 大功告成! 你的应用程序现在在最新版本的 .NET 上运行。 将来当新版本可用时,你可以使用相同的方式升级。

从 .NET Framework 迁移

从 .NET Framework 迁移是一个更复杂的过程,因为 .NET Framework 与其他基于 .NET Core 构建的平台之间存在更多差异。 但好消息是,在大多数情况下,有一款工具可以为你执行所有工作。

使用工具进行迁移

升级助手 是一个迁移工具。 此工具易于使用,你可以在 .NET 网站 上找到分步说明。 在本章节中,我们将了解后台发生的情况以及如何手动移植应用程序。 当升级助手无法迁移应用程序时,你将了解基本的机制,这样就有可能进行手动迁移。

迁移过程包括四个连续步骤:

  • 准备工作:了解项目所具有的依赖项,对之后的操作有一个概念。 在此步骤中,将当前项目带入一种简化迁移启动点的状态。

  • 迁移项目文件:.NET 项目使用新的 SDK 样式项目格式。 创建一个采用此格式的新项目文件,或更新必须使用 SDK 样式的项目文件。

  • 修复代码并生成:在 .NET 中生成代码,解决 .NET Framework 和 .NET 之间的 API 级差异。 如需要,将第三方包更新为支持 .NET 的包。

  • 运行并测试:可能存在直到运行时才会显示的差异。 因此,请勿忘记运行应用程序并测试所有操作是否按预期方式工作。

    迁移 packages.config 文件

    在 .NET Framework 应用程序中,所有对外部包的引用都在 packages.config 文件中声明。 在 .NET 中,不再需要使用 packages.config 文件。 而请使用项目文件中的 PackageReference 属性来指定应用的 NuGet 包。

    因此,你需要从一种格式转换为另一种格式。 可手动进行更新,从而获取 packages.config 文件中包含的依赖项并将其迁移到采用 PackageReference 格式的项目文件中。 或者,可让 Visual Studio 为你完成该工作:右键单击 packages.config 文件,然后选择“将 packages.config 迁移到 PackageReference”选项。

    验证 .NET 中每个依赖项的兼容性

    迁移包引用后,必须检查每个引用的兼容性。 可在 nuget.org 上浏览应用程序使用的每个 NuGet 包的依赖项。如果包具有 .NET Standard 依赖项,那么它将在 .NET 7 上运行,因为 .NET 支持 所有版本的 .NET Standard。 下图显示了 Castle.Windsor 包的依赖项:

    若要检查包的兼容性,可使用 https://fuget.org ,该工具提供了有关版本和依赖项的更详细信息。

    该项目可能引用了不支持 .NET 的旧版包,但你可能会找到支持它的较新版本。 因此,将包更新到较新版本通常是一个不错的建议。 但是,你应该考虑到,更新包版本可能会引入一些中断性变更,这些变更将强制你更新代码。

    如果找不到兼容的版本,会发生什么情况? 如果你只是因为这些中断性变更而不想更新包的版本,该怎么办? 请不要担心,因为可以依赖于 .NET 应用程序中的 .NET Framework 程序包。 请勿忘记对其进行大量测试,因为如果外部包调用的 API 在 .NET 中不可用,则可能会导致运行时错误。 在你使用不会更新的旧包时,这非常有用,你只需重新设定目标即可在 .NET 上工作。

    检查 API 兼容性

    由于 .NET Framework 和 .NET 中的 API 图面类似但并不完全相同,因此必须检查 .NET 上可用的 API 和不可用的 API。 可使用 .NET 可移植性分析器工具来显示 .NET 上不存在的已使用 API。 它会查看应用的二进制级别,提取调用的所有 API,然后列出目标框架(本例中,目标框架是 .NET 7)上不可用的 API。

    有关此工具的详细信息,请参阅:

    https://learn.microsoft.com/dotnet/standard/analyzers/portability-analyzer

    对于此工具来说,有趣的是,它只显示与你自己的代码的差异,而不显示与你不能更改的外部包中代码的差异。 请记住,你应该已经更新了这些包中的大部分包,使它们能够与 .NET 一起工作。

    Migrate

  • 创建新的 .NET 项目
  • 在大多数情况下,你会希望将现有项目更新为新的 .NET 格式。 不过,你也可以创建一个新项目,同时保留旧项目。 更新旧项目的主要缺点是你会丢失设计器支持,而这对你和你的开发团队来说可能很重要。 如果要继续使用设计器,则必须与旧 .NET 项目并行创建一个新 .NET 项目,并共享资产。 如果需要修改设计器中的 UI 元素,可切换到旧项目来执行该操作。 由于资源已链接,因此这些更改也会在 .NET 项目中更新。

    适用于 .NET 的 SDK 样式项目 比 .NET Framework 的项目格式简单得多。 除了前面提到的 PackageReference 条目外,无需执行更多操作。 新的项目格式 包括具有特定扩展名的文件 (如 .cs .xaml 文件),而无需在项目文件中显式包含这些文件。

    AssemblyInfo 注意事项

    特性针对 .NET 项目自动生成。 如果项目包含 AssemblyInfo.cs 文件,则定义将重复,这会导致编译冲突。 可删除旧版 AssemblyInfo.cs 文件,或通过将以下条目添加到 .NET 项目文件来禁用自动生成:

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
      </PropertyGroup>
    </Project>
    

    嵌入的资源会自动包含在内,但资源不会,因此需要将资源迁移到新的项目文件。

    通过“将 packages.config 迁移到 PackageReference”选项,可轻松地将外部包引用移至前面提到的新格式。

    更新包引用

    如前一部分中所示,更新发现的兼容包版本。

    修复代码和生成

    Microsoft.Windows.Compatibility

    如果应用程序依赖于 .NET 上不可用的 API(如注册表或 ACL),则必须包含对 Microsoft.Windows.Compatibility 包的引用以添加这些特定于 Windows 的 API。 它们可在 .NET 上工作,但由于它们无法跨平台,因此未包含在内。

    有一个名为“平台兼容性分析器”(https://learn.microsoft.com/dotnet/standard/analyzers/platform-compat-analyzer) 的工具,可帮助你识别与你的代码不兼容的 API。

    使用 #if 指令

    如果在以 .NET Framework 和 .NET 为目标时需要不同的执行路径,则应使用编译常量。 向代码中添加一些 #if 指令,为这两个目标保留相同的代码库。

    .NET 上不可用的技术

    某些技术在 .NET 上不可用,例如:

  • AppDomain
  • 代码访问安全性
  • Windows 工作流
  • 因此,如果在应用程序中使用这些技术,则需要寻找这些技术的替代技术。 有关详细信息,请参阅文章在 .NET Core 和 .NET 5+ 上不可用的 .NET Framework 技术

    重新生成自动生成的客户端

    如果应用程序使用自动生成的代码(例如 WCF 客户端),则可能需要重新生成此代码,从而以 .NET 为目标。 有时,你可以找到某些缺少的引用,因为它们可能不包含在一组默认的 .NET 程序集中。 使用 https://apisof.net/ 等工具,可轻松地找到缺少的引用所在的程序集,并从 NuGet 添加该引用。

    回滚包版本

    一般情况下,我们之前已提到过,你最好更新每个单独的包版本,使其与 .NET 兼容。 然而,你会发现以一个程序集的更新版本和兼容版本为目标是不值得的。 如果更改的成本不可接受,可考虑回滚包版本,保留你在 .NET Framework 上使用的版本。 尽管它们可能不以 .NET 为目标,但它们应该能够正常工作,除非它们调用了某些不受支持的 API。

    运行和测试

    若应用程序生成无错误,便可通过测试每个功能开始迁移的最后一步。

    在最后一步中,你会发现一些不同的问题,具体取决于应用程序的复杂性以及使用的依赖项和 API。

    例如,如果你使用配置文件 (app.config),则可能会在运行时发现一些错误,如“配置节”不存在。 使用 Microsoft.Extensions.Configuration NuGet 包应可修复该错误。

    出现错误的另一个原因是使用了 BeginInvokeEndInvoke 方法,因为它们在 .NET 上不受支持。 .NET 不支持它们,因为它们依赖于远程处理,而远程处理 .NET 上并不存在。 若要解决此问题,请尝试使用 await 关键字(如可用)或 Task.Run 方法。

    可使用兼容性分析器,识别代码中可能会在 .NET 中运行时引发问题的 API 和代码模式。 转到 https://github.com/dotnet/platform-compat,对你的项目使用 .NET API 分析器。

    迁移 Windows 窗体应用程序

    为了展示 Windows 窗体应用程序的完整迁移过程,我们选择迁移 https://github.com/dotnet-architecture/eShopModernizing/tree/master/eShopLegacyNTier/src/eShopWinForms 处提供的 eShop 示例应用程序。 可在 https://github.com/dotnet-architecture/eShopModernizing/tree/master/eShopModernizedNTier/src/eShopWinForms 处找到迁移的完整结果。

    此应用程序显示一个产品目录,并允许用户导航、筛选和搜索产品。 从体系结构的角度来看,该应用依赖于充当后端数据库外观的外部 WCF 服务。

    下图中显示了主应用程序窗口:

    如果你打开 .csproj 项目文件,则可看到如下所示的内容:

    如前所述,.NET 项目的样式更精简,你需要将项目结构迁移到新的 .NET SDK 样式。

    在解决方案资源管理器中,右键单击该 Windows 窗体项目,然后选择“卸载项目”>“编辑”。

    现在,可更新该 .csproj 文件。 你将删除整个内容,并将其替换为以下代码:

    <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net7.0-windows</TargetFramework>
        <UseWindowsForms>true</UseWindowsForms>
        <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
      </PropertyGroup>
    </Project>
    

    保存并重载该项目。 你现在已经完成了项目文件的更新,并且该项目面向 .NET 7。

    如果此时编译项目,你会发现一些与 WCF 客户端引用相关的错误。 由于此代码是自动生成的,因此必须重新生成它,使其以 .NET 为目标。

    删除 Reference.cs 文件并生成新的服务客户端。

    右键单击“连接的服务”,然后选择“添加连接的服务”选项 。

    “连接的服务”窗口随即打开。 选择“Microsoft WCF Web 服务”选项。

    如果你在与本示例相同的解决方案中有 WCF 服务,则可选择“发现”选项,而不是指定服务 URL。

    找到该服务后,该工具将反映该服务实现的 API 协定。 将命名空间的名称更改为 eShopServiceReference,如下图所示:

    选择“完成”按钮。 一段时间后,你将看到生成的代码。

    应会显示三个自动生成的文件:

  • Getting Started:一个指向 GitHub 的链接,提供某些有关 WCF 的信息。
  • ConnectedService.json:用于连接到服务的配置参数。
  • Reference.cs:实际 WCF 客户端代码。
  • 如果再次编译,你会看到许多来自 Helper 文件夹内的 .cs 文件的错误 。 此文件夹存在于 .NET Framework 版本中,但不包含在旧 .csproj 中。 但对于新的 SDK 样式项目,默认包括项目文件位置下存在的每个代码文件。 也就是说,新的 .NET Core 项目会尝试编译 Helper 文件夹内的文件。 由于不需要该文件夹,因此你可安全地将其删除。

    如果再次编译该项目并执行它,你将看不到产品图像。 问题在于,文件的路径现在略有变化。 若要解决此问题,需要在路径中添加添加另一个深度级别,更新文件 CatalogView.cs 中的以下行:

    string image_name = Environment.CurrentDirectory + "\\..\\..\\Assets\\Images\\Catalog\\" + catalogItems.Picturefilename;
    
    string image_name = Environment.CurrentDirectory + "\\..\\..\\..\\Assets\\Images\\Catalog\\" + catalogItems.Picturefilename;
    

    在此更改后,可检查该应用程序是否在 .NET 7 上按预期启动并运行。

    迁移 WPF 应用程序

    使用 Shop.ClassicWPF 示例应用程序来执行迁移。 下图显示了迁移之前的应用的屏幕截图:

    此应用程序使用本地 SQL Server Express 数据库来保存产品目录信息。 可直接从 WPF 应用程序访问此数据库。

    首先,必须将 .csproj 文件更新为 .NET Core 应用程序使用的新 SDK 样式。 按照 Windows 窗体迁移中所述的相同步骤进行操作:卸载项目、打开 .csproj 文件、更新其内容并重载该项目。

    在本例中,请删除 .csproj 文件的所有内容,并将其替换为以下代码:

     <Project Sdk="Microsoft.NET.Sdk">
      <PropertyGroup>
        <OutputType>WinExe</OutputType>
        <TargetFramework>net7.0-windows</TargetFramework>
        <UseWpf>true</UseWpf>
        <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
      </PropertyGroup>
    </Project>
    

    如果重载并编译该项目,你会收到以下错误:

    由于已删除所有 .csproj 内容,因此你丢失了旧项目中的项目引用规范。 只需将此行添加到 .csproj 文件,即可再次包含该项目引用:

    <ItemGroup>
        <ProjectReference Include="..\\eShop.SqlProvider\\eShop.SqlProvider.csproj" />
    <ItemGroup>
    

    你也可通过右键单击“依赖项”节点并选择“添加项目引用”,让 Visual Studio 为你提供帮助 。 从解决方案中选择该项目,然后选择“确定”:

    添加缺少的项目引用后,应用程序将在 .NET 上按预期方式编译和运行。

    上一页下一页