将.NET Framework项目升级为.NET 6

省流:升级: 运行 upgrade-assistant upgrade 命令,传入正在升级的项目或解决方案的名称。

upgrade-assistant upgrade "F:\DouyinBarrageGrab-main\BarrageService.sln"

upgrade-assistant upgrade "路径"

将.NET Framework项目升级为.NET 6

C# 利用.NET 升级助手将.NET Framework项目升级为.NET 6

.NET6 正式版本已经发布有一阵子了,今天我就体验一下如何将.NET Framework的项目升级为.NET 6.

升级条件:

  • Windows 操作系统
  • .NET 6 SDK
  • Visual Studio 2022 17.0 或更高版本

环境准备

①首先是VS2022下载,直接上微软官方网站,下载地址:

https://visualstudio.microsoft.com/zh-hans/downloads/

下载后,在线安装就行,如果之前有vs的其它版本也不用卸载,VS可以多个版本共存,安装的时候.NET 6的运行时默认会勾选安装,

②如果需要单独下载SDK和运行时,可以上微软官网,地址:

.NET SDKs downloads for Visual Studio (microsoft.com)

③安装好以后,可以在window命令窗口确认.NET 6是否安装正常

.NET Portability Analyzer安装和使用

升级前最好先分析下你的项目依赖的类库是否支持最新的.NET平台,这里可以利用扩展工具.NET Portability Analyzer去完成(仅支持.NET 5以下版本):

img

扩展工具下载安装完成后,重启VS。在工程或者解决方案的右键菜单中,选择:Portability Analyzer Settings。在配置页面,选择要分析的.net 版本即可。

设置完成后,在VS的工程或者解决方案右键菜单中,选择:Analyze Assembly Portability,然后过一会儿就会出现结果。第二个Sheet页面详细的说明了不支持的API信息。

dotnet tool install -g upgrade-assistant

项目升级

①分析 : .NET 升级助手工具包括一种分析模式,它可以提供有关在升级开始之前可能需要进行的更改的见解。在命令窗口 运行 upgrade-assistant analyze 命令,传入正在升级的项目或解决方案的名称。如下图:输出中有很多内部诊断信息,但某些信息非常有用。请注意,分析模式指示升级将建议项目以 net6.0-windows 目标框架名字对象 (net6.0-windows) 为目标。这是因为解决方案引用的项目是 WPF 项目,是一种仅限 Windows 的技术。控制台应用程序可能会获得直接升级到 TFM net6.0 的建议,除非它使用某些特定于 Windows 的库。

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

②升级: 运行 upgrade-assistant upgrade 命令,传入正在升级的项目或解决方案的名称。

img

该工具将运行并显示它将执行的步骤列表。完成每个步骤后,该工具将提供一组命令,让用户应用或跳过下一步或其他选项。按 Enter 而不选择数字会选择列表中的第一项。

详细步骤可参考官网文档--链接地址:

https://docs.microsoft.com/zh-cn/dotnet/core/porting/upgrade-assistant-wpf-framework

打开项目

升级完成后,选择VS2022打开项目,我旧项目依赖的Caliburn.Micro版本是3.2的不支持.NET 6,它已经自动升级到最高版本

变更项:

①packages.config被迁移到项目.csproj文件

升级助手介绍和安装

我们之前的应用在 .NET Framework 上运行,现在想将它们移植到 .NET 6。 当然你可以选择手动移植,但是更方便的方法是利用微软提供的升级助手去完成这个操作.

首先是升级助手安装,方法很简单,在命令窗口执行:

把这个app.config转成<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <!--配置更改后重启才能生效-->
  <appSettings>
    <!--过滤Websocket数据源进程,可用','进行分隔,程序将会监听以下进程的弹幕信息-->
    <add key="processFilter" value="直播伴侣,douyin,chrome,msedge,QQBrowser,360se,firefox,2345explorer,iexplore" />
    <!--Websocket监听端口-->
    <add key="wsListenPort" value="8888" />
    <!--true:监听在0.0.0.0,接受任意Ip连接,false:监听在127.0.0.1,仅接受本机连接-->
    <add key="listenAny" value="true" />
    <!--系统代理端口-->
    <add key="proxyPort" value="8827" />
    <!--上游代理地址,例如开启了系统代理,但是需要将其他无关请求转发到VPN工具中,例如:127.0.0.1:11223,不要带http://-->
    <add key="upstreamProxy" value="" />
    <!--在控制台输出弹幕-->
    <add key="printBarrage" value="true" />
    <!--要在控制台打印的弹幕类型,多个使用','分隔,(空代表不过滤) 1[普通弹幕],2[点赞消息],3[进入直播间],4[关注消息],5[礼物消息],6[统计消息],7[粉丝团消息],8[直播间分享],9[下播]-->
    <add key="printFilter" value="" />
    <!--要推送的弹幕消息类型,多个使用','分隔,同上-->
    <add key="pushFilter" value="" />
    <!--要日志记录的弹幕消息类型,多个使用','分隔,同上-->
    <add key="logFilter" value="1,2,4,5,6,7,8" />
    <!--要进行过滤的Web房间ID,多个使用','分隔,根据缓存来过滤的,直播伴侣不支持 -->
    <add key="webRoomIds" value="" />
    <!--是否启用系统代理,若设置为false 则需要在程序手动指定代理地址 -->
    <add key="usedProxy" value="true" />
    <!--开启内置的域名过滤,设置为false会解包所有https请求,cpu占用很高,建议在无法获取弹幕数据时调整 -->
    <add key="filterHostName" value="true" />
    <!--已知的弹幕域名列表 ','分隔  用作过滤规则中,凡是webcast开头的域名程序都会自动列入白名单-->
    <add key="hostNameFilter" value="" />
    <!--隐藏控制台-->
    <add key="hideConsole" value="false" />
    <!--弹幕文件日志-->
    <add key="barrageFileLog" value="false" />
    <!--显示窗体-->
    <add key="showWindow" value="false" />
    <!--进入直播间自动暂停播放-->
    <add key="autoPause" value="true" />
    <!--强制启用轮询模式获取弹幕 (对于容易断开连接或者更加追求稳定的直播间,可以启用这个开关,虽然响应速度不如WebSocket,但是绝对稳定!)-->
    <add key="forcePolling" value="false" />
    <!--弹幕轮询间隔,当 forcePolling 为 true 时生效 (毫秒,1000毫秒=1秒,不建议小于1000毫秒,太小可能会被封IP,值越小,弹幕流越丝滑,对于观众多的直播间可以改小)-->
    <add key="pollingInterval" value="3000" />
    <!--禁用直播页浏览器脚本缓存 (如果需要确保脚本每次能够正常匹配替换,则启用它,可能会损失一定的页面加载速度)-->
    <add key="disableLivePageScriptCache" value="true" />
    <!--指定要发送的COM串口 格式:'COM1:9600' 代表用COM1发送,波特率9600,若不需要使用串口抄送服务请留空 -->
    <add key="comPort" value="" />
    <!--启用自定义消息过滤,启用后可去程序根目录 Scripts/engine/comPortFilter.js 调整过滤规则 -->
    <add key="useComPortFilter" value="true" />
  </appSettings>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.0.1.2" newVersion="4.0.1.2" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.2" />
  </startup>
  <system.web>
    <membership defaultProvider="ClientAuthenticationMembershipProvider">
      <providers>
        <add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" />
      </providers>
    </membership>
    <roleManager defaultProvider="ClientRoleProvider" enabled="true">
      <providers>
        <add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400" />
      </providers>
    </roleManager>
  </system.web>
</configuration>appsettings.json格式,如下
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
然后在这读取替换成读取appsettings.json
 public AppSetting()
 {
     try
     {
         ProcessFilter = AppSettings["processFilter"].Trim().Split(',');
         //string processFilter = System.Configuration.ConfigurationManager.AppSettings["processFilter"];

         WsProt = int.Parse(AppSettings["wsListenPort"]);
         PrintBarrage = AppSettings["printBarrage"].ToLower() == "true";
         ProxyPort = int.Parse(AppSettings["proxyPort"]);
         PrintFilter = Enum.GetValues(typeof(PackMsgType)).Cast<int>().Where(w => w > 0).ToArray();
         PushFilter = Enum.GetValues(typeof(PackMsgType)).Cast<int>().Where(w => w > 0).ToArray();
         LogFilter = Enum.GetValues(typeof(PackMsgType)).Cast<int>().Where(w => w > 0).ToArray();
         FilterHostName = bool.Parse(AppSettings["filterHostName"].Trim());
         HostNameFilter = AppSettings["hostNameFilter"].Trim().Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray();
         UsedProxy = bool.Parse(AppSettings["usedProxy"].Trim());
         ListenAny = bool.Parse(AppSettings["listenAny"].Trim());
         UpstreamProxy = AppSettings["upstreamProxy"].Trim();
         HideConsole = bool.Parse(AppSettings["hideConsole"].Trim());
         BarrageLog = bool.Parse(AppSettings["barrageFileLog"].Trim());
         ShowWindow = bool.Parse(AppSettings["showWindow"].Trim());
         AutoPause = bool.Parse(AppSettings["autoPause"].Trim());
         ForcePolling = bool.Parse(AppSettings["forcePolling"].Trim());
         PollingInterval = int.Parse(AppSettings["pollingInterval"].Trim());
         DisableLivePageScriptCache = bool.Parse(AppSettings["disableLivePageScriptCache"].Trim());
         WebRoomIds = AppSettings["webRoomIds"].Trim().Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray();

         ConfigComPort();
         ConfigFilter();
     }
     catch (Exception ex)
     {
         Logger.PrintColor("配置文件读取失败,请检查配置文件是否正确");
         throw ex;
     }
 }

升级后 有些不兼容 比如app.config 已经不能正常读取,需要重新构建代码

 private readonly string _configFilePath = "app.config"; // Replace with your actual file path
 private static readonly AppSetting ins = new AppSetting(); 
 public static AppSetting Current { get { return ins; } }
 public AppSetting()
 { 
     try
     {
         var xmlDoc = new XmlDocument();
         xmlDoc.Load(_configFilePath);

         XmlNodeList appSettingsNodes = xmlDoc.SelectNodes("/configuration/appSettings/add");
         if (appSettingsNodes != null)
         {
             foreach (XmlNode node in appSettingsNodes)
             {
                 string key = node.Attributes["key"].Value;
                 string value = node.Attributes["value"].Value;

                 // Process each key-value pair according to your needs
                 // Process each key-value pair according to your needs
                 switch (key)
                 {
                     case "processFilter":
                         ProcessFilter = value.Trim().Split(',');
                         break;
                     case "wsListenPort":
                         WsProt = int.Parse(value.Trim());
                         break;
                     case "printBarrage":
                         PrintBarrage = bool.Parse(value.Trim().ToLower());
                         break;
                     case "proxyPort":
                         ProxyPort = int.Parse(value.Trim());
                         break;
                     case "printFilter":
                         PrintFilter = Enum.GetValues(typeof(PackMsgType)).Cast<int>().Where(w => w > 0).ToArray();
                         break;
                     case "pushFilter":
                         PushFilter = Enum.GetValues(typeof(PackMsgType)).Cast<int>().Where(w => w > 0).ToArray();
                         break;
                     case "logFilter":
                         LogFilter = Enum.GetValues(typeof(PackMsgType)).Cast<int>().Where(w => w > 0).ToArray();
                         break;
                     case "filterHostName":
                         FilterHostName = bool.Parse(value.Trim());
                         break;
                     case "hostNameFilter":
                         HostNameFilter = value.Trim().Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray();
                         break;
                     case "usedProxy":
                         UsedProxy = bool.Parse(value.Trim());
                         break;
                     case "listenAny":
                         ListenAny = bool.Parse(value.Trim());
                         break;
                     case "upstreamProxy":
                         UpstreamProxy = value.Trim();
                         break;
                     case "hideConsole":
                         HideConsole = bool.Parse(value.Trim());
                         break;
                     case "barrageFileLog":
                         BarrageLog = bool.Parse(value.Trim());
                         break;
                     case "showWindow":
                         ShowWindow = bool.Parse(value.Trim());
                         break;
                     case "autoPause":
                         AutoPause = bool.Parse(value.Trim());
                         break;
                     case "forcePolling":
                         ForcePolling = bool.Parse(value.Trim());
                         break;
                     case "pollingInterval":
                         PollingInterval = int.Parse(value.Trim());
                         break;
                     case "disableLivePageScriptCache":
                         DisableLivePageScriptCache = bool.Parse(value.Trim());
                         break;
                     case "webRoomIds":
                         WebRoomIds = value.Trim().Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray();
                         break;
                     default:
                         // Handle unrecognized keys if needed
                         break;
                 }
             }
         }


         //    ProcessFilter = AppSettings["processFilter"].Trim().Split(','); 
         //WsProt = int.Parse(AppSettings["wsListenPort"]);
         //PrintBarrage = AppSettings["printBarrage"].ToLower() == "true";
         //ProxyPort = int.Parse(AppSettings["proxyPort"]);
         //PrintFilter = Enum.GetValues(typeof(PackMsgType)).Cast<int>().Where(w => w > 0).ToArray();
         //PushFilter = Enum.GetValues(typeof(PackMsgType)).Cast<int>().Where(w => w > 0).ToArray();
         //LogFilter = Enum.GetValues(typeof(PackMsgType)).Cast<int>().Where(w => w > 0).ToArray();
         //FilterHostName = bool.Parse(AppSettings["filterHostName"].Trim());
         //HostNameFilter = AppSettings["hostNameFilter"].Trim().Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray();
         //UsedProxy = bool.Parse(AppSettings["usedProxy"].Trim());
         //ListenAny = bool.Parse(AppSettings["listenAny"].Trim());
         //UpstreamProxy = AppSettings["upstreamProxy"].Trim();
         //HideConsole = bool.Parse(AppSettings["hideConsole"].Trim());
         //BarrageLog = bool.Parse(AppSettings["barrageFileLog"].Trim());
         //ShowWindow = bool.Parse(AppSettings["showWindow"].Trim());
         //AutoPause = bool.Parse(AppSettings["autoPause"].Trim());
         //ForcePolling = bool.Parse(AppSettings["forcePolling"].Trim());
         //PollingInterval = int.Parse(AppSettings["pollingInterval"].Trim());
         //DisableLivePageScriptCache = bool.Parse(AppSettings["disableLivePageScriptCache"].Trim());
         //WebRoomIds = AppSettings["webRoomIds"].Trim().Split(',').Where(w => !string.IsNullOrWhiteSpace(w)).ToArray();