WPF项目从.Net Framework迁移到.Net6

2022-09-28 19:28:46 浏览数 (1)

前言

先决条件

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

.NET 升级助手是一个 .NET 工具,可以使用以下命令进行全局安装:

代码语言:javascript复制
dotnet tool install -g upgrade-assistant

运行

代码语言:javascript复制
upgrade-assistant upgrade .SchoolClient.sln

目前结论

老项目依赖众多,很多依赖并不支持.net6,因此放弃迁移。 新项目可以考虑使用。

问题处理

打印不显示

打印要替换为

代码语言:javascript复制
System.Diagnostics.Trace.WriteLine("WS:用户上线");

依赖不兼容

自动迁移后的包

我们发现自动迁移后有些包是不可用的。

代码语言:javascript复制
<ItemGroup>
  <PackageReference Include="Aspose.Words" Version="19.10.0" />
  <PackageReference Include="ICSharpCode.SharpZipLib.dll" Version="0.85.4.369" />
  <PackageReference Include="ImageProcessor" Version="2.9.1" />
  <PackageReference Include="Imazen.WebP" Version="10.0.1" />
  <PackageReference Include="LiveCharts.Wpf" Version="0.9.7" />
  <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
  <PackageReference Include="Microsoft.Office.Interop.Word" Version="15.0.4797.1004" />
  <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
  <PackageReference Include="QRCoder" Version="1.3.9" />
  <PackageReference Include="SuperWebSocket" Version="0.9.0.2" />
  <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
  <PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
  <PackageReference Include="System.Data.SQLite" Version="1.0.113.7" />
  <PackageReference Include="WpfAnimatedGif" Version="2.0.0" />
  <PackageReference Include="System.ServiceModel.Federation" Version="4.8.1" />
  <PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.4.336902">
    <PrivateAssets>all</PrivateAssets>
  </PackageReference>
  <PackageReference Include="Microsoft.Windows.Compatibility" Version="6.0.0" />
</ItemGroup>

发现很多库不支持.net core

要找对应支持的版本进行替换

更换后的包

代码语言:javascript复制
<ItemGroup>
  <PackageReference Include="Aspose.Words" Version="19.10.0" />
  <PackageReference Include="ImageProcessor.Core" Version="1.1.0" />
  <PackageReference Include="LiveCharts.Wpf.Core" Version="0.9.8" />
  <PackageReference Include="log4net" Version="2.0.15" />
  <PackageReference Include="Microsoft.AspNet.WebApi.Client" Version="5.2.9" />
  <PackageReference Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
  <PackageReference Include="Microsoft.Office.Interop.Word" Version="15.0.4797.1004" />
  <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
  <PackageReference Include="QRCoder" Version="1.3.9" />
  <PackageReference Include="SharpZipLib" Version="1.3.3" />
  <PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
  <PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
  <PackageReference Include="System.Data.SQLite" Version="1.0.113.7" />
  <PackageReference Include="TouchSocket" Version="0.5.2" />
  <PackageReference Include="WpfAnimatedGif" Version="2.0.0" />
  <PackageReference Include="System.ServiceModel.Federation" Version="4.8.1" />
  <PackageReference Include="Microsoft.Windows.Compatibility" Version="6.0.0" />
</ItemGroup>

其中有的库要找Core版本的,如

  • ImageProcessor =>ImageProcessor.Core
  • LiveCharts.Wpf => LiveCharts.Wpf.Core`.

有的库就要换成新的了

  • SuperWebSocket => TouchSocket
  • Imazen.WebP => https://github.com/psvmc/Imazen.WebP

预生成事件保持不变

代码语言:javascript复制
xcopy /Y /i /e $(ProjectDir)wwwroot $(TargetDir)wwwroot
xcopy /Y /d $(ProjectDir)DLLlibwebp.dll $(TargetDir)

程序不包含适合于入口点

报错

程序不包含适合于入口点的静态 “Main” 方法

解决方式

生成操作选择 应用程序定义

自定义工具输入 MSBuild:Compile

appSettings读写

.net framework的读写方式

以前配置在App.config中的appSettings

之前的读写方法

代码语言:javascript复制
/// <summary>
/// 配置文件读取
/// </summary>
/// <param name="key">配置文件中key字符串</param>
/// <returns></returns>
public static string GetConfigValue(string key)
{
  try
  {
    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    //获取AppSettings的节点
    AppSettingsSection appsection = (AppSettingsSection)config.GetSection("appSettings");
    return appsection.Settings[key].Value;
  }
  catch
  {
    return "";
  }
}


/// <summary>
/// 配置文件写入
/// </summary>
/// <param name="key">配置文件中key字符串</param>
/// <param name="value">配置文件中value字符串</param>
/// <returns></returns>
public static bool SetConfigValue(string key, string value)
{
  try
  {
    //打开配置文件
    Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
    //获取AppSettings的节点
    AppSettingsSection appsection = (AppSettingsSection)config.GetSection("appSettings");
    appsection.Settings[key].Value = value;
    config.Save();

    return true;
  }
  catch
  {
    return false;
  }
}

迁移后就不能用了,原来的配置文件变成了

相应的读写方法也变了。

.net6读取appsettings.json

Nuget 安装 Microsoft.Extensions.Configuration

代码语言:javascript复制
using Microsoft.Extensions.Configuration;

using System.Linq;

namespace SchoolClient.Utils
{
  public class ZConfigCoreUtil
  {
    private static IConfiguration _config;

    public static void init()
    {
      _config = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
    }

    /// <summary>
    /// 读取指定节点的字符串
    /// </summary>
    /// <param name="sessions"></param>
    /// <returns></returns>
    public static string GetVaule(params string[] sessions)
    {
      if (_config == null)
      {
        init();
      }
      try
      {
        if (sessions.Any())
        {
          return _config[string.Join(":", sessions)];
        }
      }
      catch
      {
        return "";
      }
      return "";
    }

    public static void test()
    {
      System.Diagnostics.Trace.WriteLine("IsDebug:"   GetVaule("IsDebug"));
    }
  }
}

但是只支持读取配置了,不支持写入,所以要把项目运行时读写的字段换一种方式。

其实这也是合理的,我也推荐项目本身的配置和项目运行的配置分开保存,项目的配置只能读取,运行中的配置则可以读写。

下面两种方式任取其一即可。

推荐使用JSON方式。

XML方式读写

代码语言:javascript复制
using System;
using System.Configuration;

namespace SchoolClient.Utils
{
  public static class ZConfigUtil
  {
    ///<summary>
    ///返回app.config文件中appSettings配置节的value项
    ///</summary>
    ///<param name="strKey"></param>
    ///<returns></returns>
    public static string GetVaule(string strKey)
    {
      return GetVaule(strKey, "app.config");
    }

    public static string GetVaule(string strKey, string filepath)
    {
      ExeConfigurationFileMap file = new ExeConfigurationFileMap();
      file.ExeConfigFilename = filepath;
      Configuration config = ConfigurationManager.OpenMappedExeConfiguration(file, ConfigurationUserLevel.None);
      foreach (string key in config.AppSettings.Settings.AllKeys)
      {
        if (key == strKey)
        {
          return config.AppSettings.Settings[strKey].Value.ToString();
        }
      }
      return null;
    }

    ///<summary>
    ///在app.config文件中appSettings配置节增加一对键值对
    ///</summary>
    ///<param name="newKey"></param>
    ///<param name="newValue"></param>
    public static void SetVaule(string newKey, string newValue)
    {
      SetVaule(newKey, newValue, "app.config");
    }

    public static void SetVaule(string newKey, string newValue, string filepath)
    {
      ExeConfigurationFileMap file = new ExeConfigurationFileMap();
      file.ExeConfigFilename = filepath;
      Configuration config = ConfigurationManager.OpenMappedExeConfiguration(file, ConfigurationUserLevel.None);
      bool exist = false;
      foreach (string key in config.AppSettings.Settings.AllKeys)
      {
        if (key == newKey)
        {
          exist = true;
        }
      }
      if (exist)
      {
        config.AppSettings.Settings.Remove(newKey);
      }
      config.AppSettings.Settings.Add(newKey, newValue);
      config.Save(ConfigurationSaveMode.Modified);
      ConfigurationManager.RefreshSection("appSettings");
    }

    public static void test()
    {
      SetVaule("ServerIp", "127.0.0.1");
      SetVaule("ServerPort", "8899");
      Console.WriteLine("ServerIp:"   GetVaule("ServerIp"));
      Console.WriteLine("ServerXXX:"   (GetVaule("ServerXXX") == null));
    }
  }
}

JSON方式读写

代码语言:javascript复制
using Newtonsoft.Json;

using System;
using System.IO;

namespace SchoolClient.Utils
{
  public class ZConfigJsonUtil
  {
    private static string readtxt(string filepath)
    {
      string config_txt = "";
      using (FileStream fs = new FileStream(filepath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
      {
        using (StreamReader sr = new StreamReader(fs))
        {
          config_txt = sr.ReadToEnd();
        }
      }

      return config_txt;
    }

    private static void writetxt(string filepath, string txt)
    {
      using (FileStream fs = new FileStream(filepath, FileMode.Truncate, FileAccess.ReadWrite))
      {
        using (StreamWriter sw = new StreamWriter(fs))
        {
          sw.Write(txt);
        }
      }
    }

    private static string getconfigpath()
    {
      string docpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
      string configfolderpath = Path.Combine(docpath, "_XHCLASS");
      if (!Directory.Exists(configfolderpath))
      {
        Directory.CreateDirectory(configfolderpath);
      }

      string configpath = Path.Combine(configfolderpath, "config.json");
      return configpath;
    }

    /// <summary>
    /// 初始化配置
    /// </summary>
    public static void init()
    {
      string configpath = getconfigpath();
      string txt = readtxt(configpath);
      if (string.IsNullOrEmpty(txt))
      {
        txt = "{}";
        ConfigModel jsonobj = JsonConvert.DeserializeObject<ConfigModel>(txt);
        string docpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        string configfolderpath = Path.Combine(docpath, "_XHCLASS");
        jsonobj.savepath = configfolderpath;
        writetxt(configpath, JsonConvert.SerializeObject(jsonobj));
      }
    }

    /// <summary>
    /// 获取配置文件
    /// </summary>
    /// <returns></returns>
    public static ConfigModel configGet()
    {
      string configpath = getconfigpath();
      string txt = readtxt(configpath);
      if (string.IsNullOrEmpty(txt))
      {
        txt = "{}";
      }
      try
      {
        return JsonConvert.DeserializeObject<ConfigModel>(txt); ;
      }
      catch (Exception)
      {
        return new ConfigModel();
      }
    }

    /// <summary>
    /// 保存配置文件
    /// </summary>
    /// <param name="config"></param>
    public static void configSet(ConfigModel config)
    {
      string configpath = getconfigpath();
      writetxt(configpath, JsonConvert.SerializeObject(config));
    }

    /// <summary>
    /// 设置临时文件存储位置
    /// </summary>
    /// <param name="value"></param>
    public static void setSavepath(string value)
    {
      ConfigModel config = configGet();
      config.savepath = value;
      configSet(config);
    }

    /// <summary>
    /// 获取临时文件存储位置
    /// </summary>
    /// <returns></returns>
    public static string getSavepath()
    {
      var config = configGet();
      if (string.IsNullOrEmpty(config.savepath))
      {
        string docpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        string configfolderpath = Path.Combine(docpath, "_XHCLASS");
        return configfolderpath;
      }
      else
      {
        return config.savepath;
      }
    }
  }

  public class ConfigModel
  {
    public string savepath { get; set; }
  }
}

输出精简

多运行时精简

输出目录中创建了一个名为runtime的文件夹,里面有很多与平台相关的dll。

解决方法

在csproj文件中的PropertyGroup中,将SelfContained属性设置为false并指定一个RuntimeIdentifier

如下所示:

代码语言:javascript复制
<PropertyGroup>
    <SelfContained>false</SelfContained>
    <RuntimeIdentifier>win-x86</RuntimeIdentifier>
</PropertyGroup>

64位的

代码语言:javascript复制
<PropertyGroup>
    <SelfContained>false</SelfContained>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>

多语言精简

我们没有为项目指定语言, 所以会列出多种语言。

解决方法:

在csproj文件中的PropertyGroup中,

代码语言:javascript复制
<PropertyGroup>
  <SatelliteResourceLanguages>zh-Hans</SatelliteResourceLanguages>
</PropertyGroup>

0 人点赞