使用 .NET 升级助手将 WPF 应用升级到 .NET 6

.NET 升级助手是一种命令行工具,可帮助将 .NET Framework WPF 应用升级到 .NET 6。 本文提供以下内容:

  • 演示如何针对 .NET Framework WPF 应用运行该工具
  • 故障排除提示

若要详细了解如何安装此工具,请查看 .NET 升级助手概述

演示应用

可以使用基本 WPF 示例项目来测试通过升级助手进行升级。

分析应用

.NET 升级助手工具包括一种分析模式,可执行升级应用的简化试运行。 它可以提供有关在升级开始之前可能需要进行的更改的见解。 打开终端,导航到目标项目或解决方案所在的文件夹。 运行 upgrade-assistant analyze 命令,传入正在升级的项目或解决方案的名称。

例如,使用基本 WPF 示例应用运行分析模式会产生以下输出,表明在升级之前不会进行任何更改:

> upgrade-assistant analyze .\WebSiteRatings.sln

[09:50:56 INF] Loaded 5 extensions
[09:50:57 INF] Using MSBuild from C:\Program Files\dotnet\sdk\6.0.101\
[09:50:57 INF] Using Visual Studio install from C:\Program Files\Microsoft Visual Studio\2022\Preview [v17]
[09:50:59 INF] Recommending executable TFM net6.0 because the project builds to a web app
[09:50:59 INF] Recommending Windows TFM net6.0-windows because the project either has Windows-specific dependencies or builds to a WinExe
[09:50:59 INF] Marking assembly reference System.Configuration for removal based on package mapping configuration System.Configuration
[09:50:59 INF] Adding package System.Configuration.ConfigurationManager based on package mapping configuration System.Configuration
[09:50:59 INF] Marking assembly reference System.Web for removal based on package mapping configuration ASP.NET
[09:50:59 INF] Adding framework reference Microsoft.AspNetCore.App based on package mapping configuration ASP.NET
[09:50:59 INF] Marking assembly reference System.Web.Extensions for removal based on package mapping configuration ASP.NET
[09:51:00 INF] Reference to .NET Upgrade Assistant analyzer package (Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers, version 0.3.261602) needs to be added
[09:51:01 INF] Adding Microsoft.Windows.Compatibility 6.0.0
[09:51:01 INF] Recommending Windows TFM net6.0-windows because the project either has Windows-specific dependencies or builds to a WinExe
[09:51:01 INF] Reference to .NET Upgrade Assistant analyzer package (Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers, version 0.3.261602) needs to be added
[09:51:01 INF] Adding Microsoft.Windows.Compatibility 6.0.0
[09:51:01 INF] Running analyzers on WebSiteRatings
[09:51:02 INF] Identified 0 diagnostics in project WebSiteRatings
[09:51:02 INF] Running analyzers on StarVoteControl
[09:51:02 INF] Identified 0 diagnostics in project StarVoteControl

输出中有很多内部诊断信息,但某些信息非常有用。 请注意,分析模式指示升级将建议项目以 net6.0-windows 目标框架名字对象 (TFM) 为目标。 这是因为解决方案引用的项目是 WPF 项目,是一种仅限 Windows 的技术。 控制台应用程序可能会获得直接升级到 TFM net6.0 的建议,除非它使用某些特定于 Windows 的库。

如果报告了任何错误或警告,请在开始升级前处理这些错误或警告。

运行升级助手

打开终端,导航到目标项目或解决方案所在的文件夹。 运行 upgrade-assistant upgrade 命令,传入正在升级的项目或解决方案的名称。

如果提供了项目,升级过程将立即在该项目上启动。 如果提供了解决方案,你将选择通常运行的项目,称为升级入口点。 根据该项目,将创建一个依赖项关系图,并提供有关升级项目的顺序的建议。

upgrade-assistant upgrade .\WebSiteRatings.sln

该工具将运行并显示它将执行的步骤列表。 完成每个步骤后,该工具将提供一组命令,让用户应用或跳过下一步或其他选项,例如:

  • 获取有关此步骤的详细信息。
  • 更改项目。
  • 调整日志记录设置。
  • 停止升级并退出。

Enter 而不选择数字会选择列表中的第一项。

每个步骤初始化后,如果应用该步骤,它可能会提供有关其认为会发生的情况的信息。

选择入口点

升级示例应用的第一步是选择解决方案中的哪个项目作为入口点项目。

Upgrade Steps

1. [Next step] Select an entrypoint
2. Select project to upgrade

Choose a command:
   1. Apply next step (Select an entrypoint)
   2. Skip next step (Select an entrypoint)
   3. See more step details
   4. Configure logging
   5. Exit

选择“命令 1”开始该步骤。 结果显示:

[13:28:49 INF] Applying upgrade step Select an entrypoint
Please select the project you run. We will then analyze the dependencies and identify the recommended order to upgrade projects.
   1. StarVoteControl
   2. WebSiteRatings

列出了两个项目:主 WPF 应用 (WebSiteRatings) 和 UserControl 项目 (StarVoteControl)。 为入口点选择 WebSiteRatings 项目,即“项 2”。

选择要升级的项目

确定入口点后,下一步是选择要首先升级的项目。 在此示例中,该工具确定应首先升级 WPF UserControl 项目 (StarVoteControl),因为主 WPF 应用项目依赖于该控件。

[13:44:47 INF] Applying upgrade step Select project to upgrade
Here is the recommended order to upgrade. Select enter to follow this list, or input the project you want to start with.
   1. StarVoteControl
   2. WebSiteRatings

此处建议的路径是首先升级 StarVoteControl 项目,因为 WebSiteRatings 项目依赖于它。 最好遵循建议的升级路径。

升级 StarVoteControl 是一个简单的项目,不会出现任何升级后问题。 因此,本文继续进行,就好像该项目已经升级,下一个要升级的项目是 WebSiteRatings。

升级项目

选择某个项目后,将列出该工具将执行的升级步骤的列表。

重要

出于本示例的目的,描述了 WebSiteRatings 项目。 假设已成功升级 StarVoteControl 项目。 演示 WebSiteRatings 项目的原因是,它包含更多在升级应用时会遇到的常见问题。

根据正在升级的项目,你可能会也可能不会看到此示例中列出的每个步骤。

以下输出描述了升级项目所涉及的步骤:

[16:09:24 INF] Initializing upgrade step Back up project

Upgrade Steps

Entrypoint: C:\code\migration\wpf\sampleApp\WebSiteRatings\WebSiteRatings.csproj
Current Project: C:\code\migration\wpf\sampleApp\WebSiteRatings\WebSiteRatings.csproj

1. [Next step] Back up project
2. Convert project file to SDK style
3. Clean up NuGet package references
4. Update TFM
5. Update NuGet Packages
6. Add template files
7. Upgrade app config files
    a. Convert Application Settings
    b. Convert Connection Strings
    c. Disable unsupported configuration sections
8. Update source code
    a. Apply fix for UA0002: Types should be upgraded
    b. Apply fix for UA0012: 'UnsafeDeserialize()' does not exist
9. Move to next project

Choose a command:
   1. Apply next step (Back up project)
   2. Skip next step (Back up project)
   3. See more step details
   4. Select different project
   5. Configure logging
   6. Exit

备注

本文的其余部分不会明确显示升级步骤,除非有重要的内容需要指出。仍显示每个步骤的结果。

创建备份

在升级项目的这一示例中,你将应用每个步骤。 第一步(命令 1)是备份项目:

[16:10:42 INF] Applying upgrade step Back up project
Please choose a backup path
   1. Use default path [C:\code\migration\wpf\sampleApp.backup]
   2. Enter custom path

该工具选择一个以当前文件夹命名的默认备份路径,但后面追加了 .backup。 可以选择自定义路径作为默认路径的替代。 对于每个升级的项目,会将项目的文件夹复制到备份文件夹。 在此示例中,WebSiteRatings 文件夹在备份步骤中从 sampleApp\WebSiteRatings 复制到 sampleApp.backup\WebSiteRatings:

[16:10:44 INF] Backing up C:\code\migration\wpf\sampleApp\WebSiteRatings to C:\code\migration\wpf\sampleApp.backup\WebSiteRatings
[16:10:45 INF] Project backed up to C:\code\migration\wpf\sampleApp.backup\WebSiteRatings
[16:10:45 INF] Upgrade step Back up project applied successfully
Please press enter to continue...

升级项目文件

项目将从 .NET Framework 项目格式升级到 .NET SDK 项目格式。

[16:10:51 INF] Applying upgrade step Convert project file to SDK style
[16:10:51 INF] Converting project file format with try-convert, version 0.3.261602+8aa571efd8bac422c95c35df9c7b9567ad534ad0
[16:10:51 INF] Recommending TFM net6.0-windows because of dependency on project C:\code\migration\wpf\sampleApp\StarVoteControl\StarVoteControl.csproj
C:\code\migration\wpf\sampleApp\WebSiteRatings\WebSiteRatings.csproj contains an App.config file. App.config is replaced by appsettings.json in .NET Core. You will need to delete App.config and migrate to appsettings.json if it's applicable to your project.
[16:10:52 INF] Converting project C:\code\migration\wpf\sampleApp\WebSiteRatings\WebSiteRatings.csproj to SDK style
[16:10:53 INF] Project file converted successfully! The project may require additional changes to build successfully against the new .NET target.
[16:10:55 INF] Upgrade step Convert project file to SDK style applied successfully
Please press enter to continue...

注意每个步骤的输出。 请注意,示例输出表明你将在升级后完成手动步骤:

App.config 被 .NET Core 中的 appsettings.json 替换。 如果它适用于你的项目,你将需要删除 App.config 并迁移到 appsettings.json。

作为此升级步骤的一部分,packages.config 引用的 NuGet 包将迁移到项目文件。

清理 NuGet 引用

更新项目格式后,下一步是清理 NuGet 包引用。

除了你的应用引用的包,packages.config 文件还包含对这些包的依赖项的引用。 例如,如果添加了对包 A 的引用,而该包依赖于包 B,则这两个包都将在 packages.config 文件中引用。 在新的项目系统中,只需要对包 A 的引用。 此步骤分析包引用并删除不需要的包引用。

[16:55:18 INF] Applying upgrade step Clean up NuGet package references
[16:55:18 INF] Removing outdated assembly reference: System.Configuration
[16:55:18 INF] Removing outdated package reference: ControlzEx, Version=4.4.0
[16:55:18 INF] Removing outdated package reference: Microsoft.Xaml.Behaviors.Wpf, Version=1.1.19
[16:55:18 INF] Removing outdated package reference: SQLite.Native, Version=3.12.3
[16:55:18 INF] Adding package reference: System.Configuration.ConfigurationManager, Version=5.0.0
[16:55:18 INF] Adding package reference: Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers, Version=0.3.261602
[16:55:21 WRN] No version of System.Configuration.ConfigurationManager found that supports ["net452"]; leaving unchanged
[16:55:21 INF] Upgrade step Clean up NuGet package references applied successfully
Please press enter to continue...

你的应用仍在引用 .NET Framework 程序集。 其中一些程序集可以作为 NuGet 包提供。 此步骤会分析这些程序集并引用适当的 NuGet 包。

处理 TFM

接下来,该工具会将 TFM 从 .NET Framework 更改为建议的 SDK。 在此示例中,它是 net6.0-windows

[16:56:36 INF] Applying upgrade step Update TFM
[16:56:36 INF] Recommending TFM net6.0-windows because of dependency on project C:\code\migration\wpf\sampleApp\StarVoteControl\StarVoteControl.csproj
[16:56:40 INF] Updated TFM to net6.0-windows
[16:56:40 INF] Upgrade step Update TFM applied successfully
Please press enter to continue...

升级 NuGet 包

接下来,该工具会将项目的 NuGet 包更新到支持更新的 TFM net6.0-windows 的版本。

[16:58:06 INF] Applying upgrade step Update NuGet Packages
[16:58:06 INF] Removing outdated package reference: Microsoft.CSharp, Version=4.7.0
[16:58:06 INF] Removing outdated package reference: System.Data.DataSetExtensions, Version=4.5.0
[16:58:06 INF] Removing outdated package reference: EntityFramework, Version=6.2.0
[16:58:06 INF] Adding package reference: EntityFramework, Version=6.4.4
[16:58:11 INF] Upgrade step Update NuGet Packages applied successfully
Please press enter to continue...

模板、配置和代码文件

如果工具确定不需要对项目执行任何操作,则工具会自动跳过后续几个步骤。

更新包后,下一步是更新任何模板文件。 在此示例中,没有需要更新或添加到项目中的模板文件。 此步骤将被跳过,下一步会自动启动:升级应用配置文件。 在此示例中,该步骤只需转换连接字符串:

[17:02:52 INF] Applying upgrade step Convert Connection Strings
[17:02:53 INF] Upgrade step Convert Connection Strings applied successfully
[17:02:53 INF] Applying upgrade step Upgrade app config files
[17:02:53 INF] Upgrade step Upgrade app config files applied successfully

完成此项目升级之前的最后一步是更新任何过时的代码引用。 基于要升级的项目类型,将为此步骤显示一系列已知代码修补程序。 某些修补程序可能不适用于你的项目。

8. Update source code
    a. Apply fix for UA0002: Types should be upgraded
    b. Apply fix for UA0012: 'UnsafeDeserialize()' does not exist

在这种情况下,没有任何建议的修补程序应用于示例项目,并且此步骤会在上一步完成后立即自动完成。

[17:02:58 INF] Initializing upgrade step Update source code
[17:02:58 INF] Running analyzers on WebSiteRatings
[17:02:59 INF] Identified 0 diagnostics in project WebSiteRatings
[17:02:59 INF] Initializing upgrade step Move to next project

完成升级

如果还有更多项目要迁移,该工具可让你选择接下来要升级的项目。 当没有更多项目要升级时,该工具会将你带到“完成升级”步骤:

1. [Next step] Finalize upgrade

Choose a command:
   1. Apply next step (Finalize upgrade)
   2. Skip next step (Finalize upgrade)
   3. See more step details
   4. Configure logging
   5. Exit

升级完成后,迁移的 WPF 项目将类似于以下 XML:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-windows</TargetFramework>
    <OutputType>WinExe</OutputType>
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
    <UseWPF>true</UseWPF>
    <ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
  </PropertyGroup>
  <ItemGroup>
    <None Update="sqlite.db">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\StarVoteControl\StarVoteControl.csproj" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="System.Configuration.ConfigurationManager" Version="5.0.0" />
    <PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.3.261602">
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
    <PackageReference Include="EntityFramework" Version="6.4.4" />
  </ItemGroup>
  <ItemGroup>
    <Service Include="{508349B6-6B84-4DF5-91F0-309BEEBAD82D}" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="MahApps.Metro" Version="2.4.9" />
    <PackageReference Include="Microsoft.Data.Sqlite" Version="1.0.0" />
    <PackageReference Include="SQLite" Version="3.12.3" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="appsettings.json" />
  </ItemGroup>
</Project>

请注意,.NET 升级助手还会向项目添加分析器,它可帮助继续执行升级过程,例如 Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers NuGet 包。

升级后

升级项目后,需要对其进行编译和测试。 在完成升级过程中,你肯定还有更多工作要做。 你的应用的 .NET Framework 版本可能包含项目实际未使用的库引用。 你需要分析每个引用并确定它是否是必需的。 该工具可能还添加或升级了对错误版本的 NuGet 包引用。

本文发布时,需要以下更新才能完成示例项目的升级:

  • System.Configuration.ConfigurationManager NuGet 包升级到版本 6.0.0。 升级工具选择了错误的版本 (5.0.0):

    <PackageReference Include="System.Configuration.ConfigurationManager" Version="6.0.0" />
    

修复该项后,示例应用会进行编译和运行。 但是,还有更多可以升级的内容,例如,此应用使用的是 Microsoft.Data.Sqlite 1.0.0 NuGet 包,这是直接支持 .NET Framework 的最后一个版本。 此包依赖于 SQLite 包。 如果为 6.0.0 升级了 Microsoft.Data.Sqlite,则会删除该依赖项。

现代化:appsettings.json

.NET Framework 使用 App.config 文件为你的应用加载设置,例如连接字符串和日志记录提供程序。 .NET 现在使用 appsettings.json 文件进行应用设置。

通过 System.Configuration.ConfigurationManager NuGet 包在 .NET 中支持 App.config 文件,并且通过 Microsoft.Extensions.Configuration NuGet 包提供对 appsettings.json 的支持。

随着其他库升级到 .NET,它们将通过支持 appsettings.json 而不是 App.config 实现现代化。例如,已针对 .NET 6 升级的 .NET Framework 中的日志记录提供程序不再使用 App.config 进行设置。 你最好遵循其方向,同时避免使用 App.config。

通过上一部分中升级的 WPF 示例应用,我们可以删除对 System.Configuration.ConfigurationManager 的依赖,并移至 appsettings.json 以获取本地数据库使用的连接字符串。

  1. 删除 System.Configuration.ConfigurationManager NuGet 包。

  2. 添加 Microsoft.Extensions.ConfigurationMicrosoft.Extensions.Configuration.Json NuGet 包。

    你的应用可能需要各种相关的 Microsoft.Extensions.Configuration NuGet 包。

  3. 从项目中删除 App.config 文件。

    在示例应用中,此文件仅包含单个连接字符串,该字符串已由升级工具迁移到 appsettings.json 文件。

  4. 将 appsettings.json 文件设置为复制到输出目录。

    使用“属性”窗格通过 Visual Studio 设置此选项,或直接编辑项目并添加以下 ItemGroup

      <ItemGroup>
        <Content Include="appsettings.json">
          <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </Content>
      </ItemGroup>
    
  5. 编辑 App.xaml.cs 文件,设置加载 appsettings.json 文件的配置对象,添加的行突出显示:

    using System.Windows;
    using Microsoft.Extensions.Configuration;
    
    namespace WebSiteRatings
    {
        /// <summary>
        /// Interaction logic for App.xaml
        /// </summary>
        public partial class App : Application
        {
            public static IConfiguration Config { get; private set; }
    
            public App()
            {
                Config = new ConfigurationBuilder()
                    .AddJsonFile("appsettings.json")
                    .Build();
            }
        }
    }
    
  6. 在 .\Models\Database.cs 文件中,更改 OpenConnection 方法以使用新的 App.Config 属性。 这需要导入 Microsoft.Extensions.Configuration 命名空间:

    using Microsoft.Data.Sqlite;
    using System.Collections.Generic;
    using Microsoft.Extensions.Configuration;
    
    namespace WebSiteRatings.Models
    {
        internal class Database
        {
            public static SqliteConnection OpenConnection() =>
                new SqliteConnection(App.Config.GetConnectionString("database"));
    
            // More code below...
    

    GetConnectionStringMicrosoft.Extensions.Configuration 命名空间提供的扩展方法。

现代化:Web 浏览器控件

项目引用的 WebBrowser 控件基于已过时的 Internet Explorer。 适用于 .NET 的 WPF 包括一个基于 Microsoft Edge 的新浏览器控件。 完成以下步骤以升级到新的 WebView2 Web 浏览器控件:

  1. 添加对 Microsoft.Web.WebView2 NuGet 包的引用。

  2. 在 MainWindow.xaml 文件中:

    1. 将控件导入根元素中的 wpfControls 命名空间:

      <mah:MetroWindow x:Class="WebSiteRatings.MainWindow"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
              xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
              xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
              xmlns:local="clr-namespace:WebSiteRatings"
              xmlns:vm="clr-namespace:WebSiteRatings.ViewModels"
              xmlns:VoteControl="clr-namespace:StarVoteControl;assembly=StarVoteControl"
              xmlns:wpfControls="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
              Loaded="MetroWindow_Loaded"
              mc:Ignorable="d"
              Title="My Sites" Height="650" Width="1000">
      
    2. 在声明 <Border> 元素的位置下,删除 WebBrowser 控件并将其替换为 wpfControls:WebView2 控件:

      <Border Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" BorderThickness="1" BorderBrush="Black" Margin="5">
          <wpfControls:WebView2 x:Name="browser" ScrollViewer.CanContentScroll="True" />
      </Border>
      
  3. 编辑 MainWindow.xaml.cs 代码隐藏文件。 更新 ListBox_SelectionChanged 方法以将 browser.Source 属性设置为有效的 Uri。 此代码以前作为字符串传入网站 URL,但新的 WebView2 控件需要 Uri

    private void ListBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var siteCollection = (ViewModels.SiteCollection)DataContext;
    
        if (siteCollection.SelectedSite != null)
            browser.Source = new Uri(siteCollection.SelectedSite.Url);
        else
            browser.NavigateToString("<body></body>");
    }
    

根据你的应用的用户运行的 Windows 版本,他们可能需要安装 WebView2 运行时。 有关详细信息,请参阅 WPF 应用中的 WebView2 入门

Visual Basic 项目

如果使用 Visual Basic 对项目进行编码,升级助手可能包含其他步骤,例如迁移 My 命名空间。 当你的项目使用这些功能时,你应该只会看到添加的这些步骤。 使用示例应用时,MatchingGame.Logic 中的代码使用 My 命名空间来访问注册表。 此项目将包含一个与 My 相关的步骤:

7. Update Visual Basic project
    a. Update vbproj to support "My." namespace

故障排除提示

使用 .NET 升级助手时,可能会出现一些已知问题。 某些情况下,.NET 升级助手在内部使用的 try-convert 工具会出现问题。

有关更多故障排除提示和已知问题,可查看此工具的 GitHub 存储库

另请参阅