翼度科技»论坛 编程开发 .net 查看内容

AOT使用经验总结

9

主题

9

帖子

27

积分

新手上路

Rank: 1

积分
27
一、引言

站长接触 AOT 已有 3 个月之久,此前在《好消息:NET 9 X86 AOT的突破 - 支持老旧Win7与XP环境》一文中就有所提及。在这段时间里,站长使用 Avalonia 开发的项目也成功完成了 AOT 发布测试。然而,这一过程并非一帆风顺。站长在项目功能完成大半部分才开始进行 AOT 测试,期间遭遇了不少问题,可谓是 “踩坑无数”。为了方便日后回顾,也为了给广大读者提供参考,在此将这段经历进行总结。
.NET AOT是将.NET代码提前编译为本机代码的技术。其优势众多,启动速度快,减少运行时资源占用,还提高安全性。AOT发布后无需再安装.NET运行时等依赖。.NET 8、9 AOT发布后,可在XP、Win7非SP1操作系统下运行。这使得应用部署更便捷,能适应更多老旧系统环境,为开发者拓展了应用场景,在性能提升的同时,也增加了系统兼容性,让.NET应用的开发和部署更具灵活性和广泛性,给用户带来更好的体验。
二、经验之谈

(一)测试策略的重要性

从项目创建伊始,就应养成良好的习惯,即只要添加了新功能或使用了较新的语法,就及时进行 AOT 发布测试。否则,问题积累到后期,解决起来会异常艰难,站长就因前期忽视了这一点,付出了惨痛的代价。无奈的解决方法是重新创建项目,然后逐个还原功能并进行 AOT 测试。经过了一周的加班AOT测试,每个 AOT 发布过程大致如下:

  • 内网 AOT 发布一次需 2、3 分钟,这段时间只能看看需求文档、技术文章、需求文档、技术文章。。。
  • 发布完成,运行无效果,体现在双击未出现界面,进程列表没有它,说明程序崩溃了,查看系统应用事件日志,日志中通常会包含异常警告信息。
  • 依据日志信息检查代码,修改相关 API。
  • 再次进行 AOT 发布,重复上述 1 - 3 步骤。
经过一周的努力,项目 AOT 后功能测试终于正常,至此收工。
(二)AOT 需要注意的点及解决方法

1. 添加rd.xml

在主工程创建一个XML文件,例如Roots.xml,内容大致如下:
  1. <linker>
  2.         <assembly fullname="CodeWF.Toolbox.Desktop" preserve="All" />
  3. </linker>
复制代码
需要支持AOT的工程,在该XML中添加一个assembly节点,fullname是程序集名称,CodeWF.Toolbox.Desktop是站长小工具的主工程名,点击查看源码。
在主工程添加ItemGroup节点关联该XML文件:
  1. <ItemGroup>
  2.     <TrimmerRootDescriptor Include="Roots.xml" />
  3. </ItemGroup>
复制代码
2. Prism支持

站长使用了Prism框架及DryIOC容器,若要支持 AOT,需要添加以下 NuGet 包:
  1. [/code]rd.xml需要添加
  2. [code]
复制代码
3. App.config读写

在.NET Core中使用System.Configuration.ConfigurationManager包操作App.config文件,rd.xml需添加如下内容:
  1. [/code]使用Assembly.GetEntryAssembly().location失败,目前使用ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)获取的应用程序程序配置,指定路径的方式后续再研究。
  2. [size=3]4. HttpClient使用[/size]
  3. rd.xml添加如下内容:
  4. [code]
复制代码
5. Dapper支持

Dapper的AOT支持需要安装Dapper.AOT包,rd.xml添加如下内容:
  1. [/code]数据库操作的方法需要添加DapperAOT特性,举例如下:
  2. [code][DapperAot]public static bool EnsureTableIsCreated(){<ItemGroup>
  3.     <TrimmerRootDescriptor Include="Roots.xml" />
  4. </ItemGroup>try<ItemGroup>
  5.     <TrimmerRootDescriptor Include="Roots.xml" />
  6. </ItemGroup>{<ItemGroup>
  7.     <TrimmerRootDescriptor Include="Roots.xml" />
  8. </ItemGroup><ItemGroup>
  9.     <TrimmerRootDescriptor Include="Roots.xml" />
  10. </ItemGroup>using var connection = new SqliteConnection(DBConst.DBConnectionString);<ItemGroup>
  11.     <TrimmerRootDescriptor Include="Roots.xml" />
  12. </ItemGroup><ItemGroup>
  13.     <TrimmerRootDescriptor Include="Roots.xml" />
  14. </ItemGroup>connection.Open();<ItemGroup>
  15.     <TrimmerRootDescriptor Include="Roots.xml" />
  16. </ItemGroup><ItemGroup>
  17.     <TrimmerRootDescriptor Include="Roots.xml" />
  18. </ItemGroup>const string sql = $@"<ItemGroup>
  19.     <TrimmerRootDescriptor Include="Roots.xml" />
  20. </ItemGroup><ItemGroup>
  21.     <TrimmerRootDescriptor Include="Roots.xml" />
  22. </ItemGroup><ItemGroup>
  23.     <TrimmerRootDescriptor Include="Roots.xml" />
  24. </ItemGroup>CREATE TABLE IF NOT EXISTS {nameof(JsonPrettifyEntity)}(<ItemGroup>
  25.     <TrimmerRootDescriptor Include="Roots.xml" />
  26. </ItemGroup><ItemGroup>
  27.     <TrimmerRootDescriptor Include="Roots.xml" />
  28. </ItemGroup><ItemGroup>
  29.     <TrimmerRootDescriptor Include="Roots.xml" />
  30. </ItemGroup><ItemGroup>
  31.     <TrimmerRootDescriptor Include="Roots.xml" />
  32. </ItemGroup>{nameof(JsonPrettifyEntity.IsSortKey)} Bool,<ItemGroup>
  33.     <TrimmerRootDescriptor Include="Roots.xml" />
  34. </ItemGroup><ItemGroup>
  35.     <TrimmerRootDescriptor Include="Roots.xml" />
  36. </ItemGroup><ItemGroup>
  37.     <TrimmerRootDescriptor Include="Roots.xml" />
  38. </ItemGroup><ItemGroup>
  39.     <TrimmerRootDescriptor Include="Roots.xml" />
  40. </ItemGroup>{nameof(JsonPrettifyEntity.IndentSize)} INTEGER<ItemGroup>
  41.     <TrimmerRootDescriptor Include="Roots.xml" />
  42. </ItemGroup><ItemGroup>
  43.     <TrimmerRootDescriptor Include="Roots.xml" />
  44. </ItemGroup>)";<ItemGroup>
  45.     <TrimmerRootDescriptor Include="Roots.xml" />
  46. </ItemGroup><ItemGroup>
  47.     <TrimmerRootDescriptor Include="Roots.xml" />
  48. </ItemGroup>using var command = new SqliteCommand(sql, connection);<ItemGroup>
  49.     <TrimmerRootDescriptor Include="Roots.xml" />
  50. </ItemGroup><ItemGroup>
  51.     <TrimmerRootDescriptor Include="Roots.xml" />
  52. </ItemGroup>return command.ExecuteNonQuery() > 0;<ItemGroup>
  53.     <TrimmerRootDescriptor Include="Roots.xml" />
  54. </ItemGroup>}<ItemGroup>
  55.     <TrimmerRootDescriptor Include="Roots.xml" />
  56. </ItemGroup>catch (Exception ex)<ItemGroup>
  57.     <TrimmerRootDescriptor Include="Roots.xml" />
  58. </ItemGroup>{<ItemGroup>
  59.     <TrimmerRootDescriptor Include="Roots.xml" />
  60. </ItemGroup><ItemGroup>
  61.     <TrimmerRootDescriptor Include="Roots.xml" />
  62. </ItemGroup>return false;<ItemGroup>
  63.     <TrimmerRootDescriptor Include="Roots.xml" />
  64. </ItemGroup>}}
复制代码
6. System.Text.Json

参考JsonExtensions.cs
序列化
  1. public static bool ToJson(this T obj, out string? json, out string? errorMsg){<ItemGroup>
  2.     <TrimmerRootDescriptor Include="Roots.xml" />
  3. </ItemGroup>if (obj == null)<ItemGroup>
  4.     <TrimmerRootDescriptor Include="Roots.xml" />
  5. </ItemGroup>{<ItemGroup>
  6.     <TrimmerRootDescriptor Include="Roots.xml" />
  7. </ItemGroup><ItemGroup>
  8.     <TrimmerRootDescriptor Include="Roots.xml" />
  9. </ItemGroup>json = default;<ItemGroup>
  10.     <TrimmerRootDescriptor Include="Roots.xml" />
  11. </ItemGroup><ItemGroup>
  12.     <TrimmerRootDescriptor Include="Roots.xml" />
  13. </ItemGroup>errorMsg = "Please provide object";<ItemGroup>
  14.     <TrimmerRootDescriptor Include="Roots.xml" />
  15. </ItemGroup><ItemGroup>
  16.     <TrimmerRootDescriptor Include="Roots.xml" />
  17. </ItemGroup>return false;<ItemGroup>
  18.     <TrimmerRootDescriptor Include="Roots.xml" />
  19. </ItemGroup>}<ItemGroup>
  20.     <TrimmerRootDescriptor Include="Roots.xml" />
  21. </ItemGroup>var options = new JsonSerializerOptions()<ItemGroup>
  22.     <TrimmerRootDescriptor Include="Roots.xml" />
  23. </ItemGroup>{<ItemGroup>
  24.     <TrimmerRootDescriptor Include="Roots.xml" />
  25. </ItemGroup><ItemGroup>
  26.     <TrimmerRootDescriptor Include="Roots.xml" />
  27. </ItemGroup>WriteIndented = true,<ItemGroup>
  28.     <TrimmerRootDescriptor Include="Roots.xml" />
  29. </ItemGroup><ItemGroup>
  30.     <TrimmerRootDescriptor Include="Roots.xml" />
  31. </ItemGroup>Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,<ItemGroup>
  32.     <TrimmerRootDescriptor Include="Roots.xml" />
  33. </ItemGroup><ItemGroup>
  34.     <TrimmerRootDescriptor Include="Roots.xml" />
  35. </ItemGroup>TypeInfoResolver = new DefaultJsonTypeInfoResolver()<ItemGroup>
  36.     <TrimmerRootDescriptor Include="Roots.xml" />
  37. </ItemGroup>};<ItemGroup>
  38.     <TrimmerRootDescriptor Include="Roots.xml" />
  39. </ItemGroup>try<ItemGroup>
  40.     <TrimmerRootDescriptor Include="Roots.xml" />
  41. </ItemGroup>{<ItemGroup>
  42.     <TrimmerRootDescriptor Include="Roots.xml" />
  43. </ItemGroup><ItemGroup>
  44.     <TrimmerRootDescriptor Include="Roots.xml" />
  45. </ItemGroup>json = JsonSerializer.Serialize(obj, options);<ItemGroup>
  46.     <TrimmerRootDescriptor Include="Roots.xml" />
  47. </ItemGroup><ItemGroup>
  48.     <TrimmerRootDescriptor Include="Roots.xml" />
  49. </ItemGroup>errorMsg = default;<ItemGroup>
  50.     <TrimmerRootDescriptor Include="Roots.xml" />
  51. </ItemGroup><ItemGroup>
  52.     <TrimmerRootDescriptor Include="Roots.xml" />
  53. </ItemGroup>return true;<ItemGroup>
  54.     <TrimmerRootDescriptor Include="Roots.xml" />
  55. </ItemGroup>}<ItemGroup>
  56.     <TrimmerRootDescriptor Include="Roots.xml" />
  57. </ItemGroup>catch (Exception ex)<ItemGroup>
  58.     <TrimmerRootDescriptor Include="Roots.xml" />
  59. </ItemGroup>{<ItemGroup>
  60.     <TrimmerRootDescriptor Include="Roots.xml" />
  61. </ItemGroup><ItemGroup>
  62.     <TrimmerRootDescriptor Include="Roots.xml" />
  63. </ItemGroup>json = default;<ItemGroup>
  64.     <TrimmerRootDescriptor Include="Roots.xml" />
  65. </ItemGroup><ItemGroup>
  66.     <TrimmerRootDescriptor Include="Roots.xml" />
  67. </ItemGroup>errorMsg = ex.Message;<ItemGroup>
  68.     <TrimmerRootDescriptor Include="Roots.xml" />
  69. </ItemGroup><ItemGroup>
  70.     <TrimmerRootDescriptor Include="Roots.xml" />
  71. </ItemGroup>return false;<ItemGroup>
  72.     <TrimmerRootDescriptor Include="Roots.xml" />
  73. </ItemGroup>}}
复制代码
反序列化
  1. public static bool FromJson(this string? json, out T? obj, out string? errorMsg){<ItemGroup>
  2.     <TrimmerRootDescriptor Include="Roots.xml" />
  3. </ItemGroup>if (string.IsNullOrWhiteSpace(json))<ItemGroup>
  4.     <TrimmerRootDescriptor Include="Roots.xml" />
  5. </ItemGroup>{<ItemGroup>
  6.     <TrimmerRootDescriptor Include="Roots.xml" />
  7. </ItemGroup><ItemGroup>
  8.     <TrimmerRootDescriptor Include="Roots.xml" />
  9. </ItemGroup>obj = default;<ItemGroup>
  10.     <TrimmerRootDescriptor Include="Roots.xml" />
  11. </ItemGroup><ItemGroup>
  12.     <TrimmerRootDescriptor Include="Roots.xml" />
  13. </ItemGroup>errorMsg = "Please provide json string";<ItemGroup>
  14.     <TrimmerRootDescriptor Include="Roots.xml" />
  15. </ItemGroup><ItemGroup>
  16.     <TrimmerRootDescriptor Include="Roots.xml" />
  17. </ItemGroup>return false;<ItemGroup>
  18.     <TrimmerRootDescriptor Include="Roots.xml" />
  19. </ItemGroup>}<ItemGroup>
  20.     <TrimmerRootDescriptor Include="Roots.xml" />
  21. </ItemGroup>try<ItemGroup>
  22.     <TrimmerRootDescriptor Include="Roots.xml" />
  23. </ItemGroup>{<ItemGroup>
  24.     <TrimmerRootDescriptor Include="Roots.xml" />
  25. </ItemGroup><ItemGroup>
  26.     <TrimmerRootDescriptor Include="Roots.xml" />
  27. </ItemGroup>var options = new JsonSerializerOptions()<ItemGroup>
  28.     <TrimmerRootDescriptor Include="Roots.xml" />
  29. </ItemGroup><ItemGroup>
  30.     <TrimmerRootDescriptor Include="Roots.xml" />
  31. </ItemGroup>{<ItemGroup>
  32.     <TrimmerRootDescriptor Include="Roots.xml" />
  33. </ItemGroup><ItemGroup>
  34.     <TrimmerRootDescriptor Include="Roots.xml" />
  35. </ItemGroup><ItemGroup>
  36.     <TrimmerRootDescriptor Include="Roots.xml" />
  37. </ItemGroup>Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,<ItemGroup>
  38.     <TrimmerRootDescriptor Include="Roots.xml" />
  39. </ItemGroup><ItemGroup>
  40.     <TrimmerRootDescriptor Include="Roots.xml" />
  41. </ItemGroup><ItemGroup>
  42.     <TrimmerRootDescriptor Include="Roots.xml" />
  43. </ItemGroup>TypeInfoResolver = new DefaultJsonTypeInfoResolver()<ItemGroup>
  44.     <TrimmerRootDescriptor Include="Roots.xml" />
  45. </ItemGroup><ItemGroup>
  46.     <TrimmerRootDescriptor Include="Roots.xml" />
  47. </ItemGroup>};<ItemGroup>
  48.     <TrimmerRootDescriptor Include="Roots.xml" />
  49. </ItemGroup><ItemGroup>
  50.     <TrimmerRootDescriptor Include="Roots.xml" />
  51. </ItemGroup>obj = JsonSerializer.Deserialize(json!, options);<ItemGroup>
  52.     <TrimmerRootDescriptor Include="Roots.xml" />
  53. </ItemGroup><ItemGroup>
  54.     <TrimmerRootDescriptor Include="Roots.xml" />
  55. </ItemGroup>errorMsg = default;<ItemGroup>
  56.     <TrimmerRootDescriptor Include="Roots.xml" />
  57. </ItemGroup><ItemGroup>
  58.     <TrimmerRootDescriptor Include="Roots.xml" />
  59. </ItemGroup>return true;<ItemGroup>
  60.     <TrimmerRootDescriptor Include="Roots.xml" />
  61. </ItemGroup>}<ItemGroup>
  62.     <TrimmerRootDescriptor Include="Roots.xml" />
  63. </ItemGroup>catch (Exception ex)<ItemGroup>
  64.     <TrimmerRootDescriptor Include="Roots.xml" />
  65. </ItemGroup>{<ItemGroup>
  66.     <TrimmerRootDescriptor Include="Roots.xml" />
  67. </ItemGroup><ItemGroup>
  68.     <TrimmerRootDescriptor Include="Roots.xml" />
  69. </ItemGroup>obj = default;<ItemGroup>
  70.     <TrimmerRootDescriptor Include="Roots.xml" />
  71. </ItemGroup><ItemGroup>
  72.     <TrimmerRootDescriptor Include="Roots.xml" />
  73. </ItemGroup>errorMsg = ex.Message;<ItemGroup>
  74.     <TrimmerRootDescriptor Include="Roots.xml" />
  75. </ItemGroup><ItemGroup>
  76.     <TrimmerRootDescriptor Include="Roots.xml" />
  77. </ItemGroup>return false;<ItemGroup>
  78.     <TrimmerRootDescriptor Include="Roots.xml" />
  79. </ItemGroup>}}
复制代码
7. 反射问题

参考项目CodeWF.NetWeaver

  • 创建指定类型的List或Dictionary实例:
  1. public static object CreateInstance(Type type){<ItemGroup>
  2.     <TrimmerRootDescriptor Include="Roots.xml" />
  3. </ItemGroup>var itemTypes = type.GetGenericArguments();<ItemGroup>
  4.     <TrimmerRootDescriptor Include="Roots.xml" />
  5. </ItemGroup>if (typeof(IList).IsAssignableFrom(type))<ItemGroup>
  6.     <TrimmerRootDescriptor Include="Roots.xml" />
  7. </ItemGroup>{<ItemGroup>
  8.     <TrimmerRootDescriptor Include="Roots.xml" />
  9. </ItemGroup><ItemGroup>
  10.     <TrimmerRootDescriptor Include="Roots.xml" />
  11. </ItemGroup>var lstType = typeof(List);<ItemGroup>
  12.     <TrimmerRootDescriptor Include="Roots.xml" />
  13. </ItemGroup><ItemGroup>
  14.     <TrimmerRootDescriptor Include="Roots.xml" />
  15. </ItemGroup>var genericType = lstType.MakeGenericType(itemTypes.First());<ItemGroup>
  16.     <TrimmerRootDescriptor Include="Roots.xml" />
  17. </ItemGroup><ItemGroup>
  18.     <TrimmerRootDescriptor Include="Roots.xml" />
  19. </ItemGroup>return Activator.CreateInstance(genericType)!;<ItemGroup>
  20.     <TrimmerRootDescriptor Include="Roots.xml" />
  21. </ItemGroup>}<ItemGroup>
  22.     <TrimmerRootDescriptor Include="Roots.xml" />
  23. </ItemGroup>else<ItemGroup>
  24.     <TrimmerRootDescriptor Include="Roots.xml" />
  25. </ItemGroup>{<ItemGroup>
  26.     <TrimmerRootDescriptor Include="Roots.xml" />
  27. </ItemGroup><ItemGroup>
  28.     <TrimmerRootDescriptor Include="Roots.xml" />
  29. </ItemGroup>var dictType = typeof(Dictionary);<ItemGroup>
  30.     <TrimmerRootDescriptor Include="Roots.xml" />
  31. </ItemGroup><ItemGroup>
  32.     <TrimmerRootDescriptor Include="Roots.xml" />
  33. </ItemGroup>var genericType = dictType.MakeGenericType(itemTypes.First(), itemTypes[1]);<ItemGroup>
  34.     <TrimmerRootDescriptor Include="Roots.xml" />
  35. </ItemGroup><ItemGroup>
  36.     <TrimmerRootDescriptor Include="Roots.xml" />
  37. </ItemGroup>return Activator.CreateInstance(genericType)!;<ItemGroup>
  38.     <TrimmerRootDescriptor Include="Roots.xml" />
  39. </ItemGroup>}}
复制代码

  • 反射调用List和Dictionary的Add方法添加元素失败,下面是伪代码:
  1. // Listvar addMethod = type.GetMethod("Add");addMethod.Invoke(obj, new[]{ child })<ItemGroup>
  2.     <TrimmerRootDescriptor Include="Roots.xml" />
  3. </ItemGroup>// Dictionaryvar addMethod = type.GetMethod("Add");addMethod.Invoke(obj, new[]{ key, value })
复制代码
解决办法,转换为实现的接口调用:
  1. // List<T>
  2. (obj as IList).Add(child);
  3. // Dictionary<Key, Value>
  4. (obj as IDictionary)[key] = value;
复制代码

  • 获取数组、List、Dictionary的元素个数
同上面Add方法反射获取Length或Count属性皆返回0,value.Property("Length", 0),封装的Property非AOT运行正确:
  1. public static T Property(this object obj, string propertyName, T defaultValue = default){<ItemGroup>
  2.     <TrimmerRootDescriptor Include="Roots.xml" />
  3. </ItemGroup>if (obj == null) throw new ArgumentNullException(nameof(obj));<ItemGroup>
  4.     <TrimmerRootDescriptor Include="Roots.xml" />
  5. </ItemGroup>if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException(nameof(propertyName));<ItemGroup>
  6.     <TrimmerRootDescriptor Include="Roots.xml" />
  7. </ItemGroup>var propertyInfo = obj.GetType().GetProperty(propertyName);<ItemGroup>
  8.     <TrimmerRootDescriptor Include="Roots.xml" />
  9. </ItemGroup>if (propertyInfo == null)<ItemGroup>
  10.     <TrimmerRootDescriptor Include="Roots.xml" />
  11. </ItemGroup>{<ItemGroup>
  12.     <TrimmerRootDescriptor Include="Roots.xml" />
  13. </ItemGroup><ItemGroup>
  14.     <TrimmerRootDescriptor Include="Roots.xml" />
  15. </ItemGroup>return defaultValue;<ItemGroup>
  16.     <TrimmerRootDescriptor Include="Roots.xml" />
  17. </ItemGroup>}<ItemGroup>
  18.     <TrimmerRootDescriptor Include="Roots.xml" />
  19. </ItemGroup>var value = propertyInfo.GetValue(obj);<ItemGroup>
  20.     <TrimmerRootDescriptor Include="Roots.xml" />
  21. </ItemGroup>try<ItemGroup>
  22.     <TrimmerRootDescriptor Include="Roots.xml" />
  23. </ItemGroup>{<ItemGroup>
  24.     <TrimmerRootDescriptor Include="Roots.xml" />
  25. </ItemGroup><ItemGroup>
  26.     <TrimmerRootDescriptor Include="Roots.xml" />
  27. </ItemGroup>return (T)Convert.ChangeType(value, typeof(T));<ItemGroup>
  28.     <TrimmerRootDescriptor Include="Roots.xml" />
  29. </ItemGroup>}<ItemGroup>
  30.     <TrimmerRootDescriptor Include="Roots.xml" />
  31. </ItemGroup>catch (InvalidCastException)<ItemGroup>
  32.     <TrimmerRootDescriptor Include="Roots.xml" />
  33. </ItemGroup>{<ItemGroup>
  34.     <TrimmerRootDescriptor Include="Roots.xml" />
  35. </ItemGroup><ItemGroup>
  36.     <TrimmerRootDescriptor Include="Roots.xml" />
  37. </ItemGroup>return defaultValue;<ItemGroup>
  38.     <TrimmerRootDescriptor Include="Roots.xml" />
  39. </ItemGroup>}}
复制代码
AOT成功:直接通过转换为基类型或实现的接口调用属性即可:
  1. // 数组var length = ((Array)value).Length;// List if (value is IList list){<ItemGroup>
  2.     <TrimmerRootDescriptor Include="Roots.xml" />
  3. </ItemGroup>var count = list.Count;}// Dictionaryif (value is IDictionary dictionary){<ItemGroup>
  4.     <TrimmerRootDescriptor Include="Roots.xml" />
  5. </ItemGroup>var count = dictionary.Count;}
复制代码
8. Windows 7支持

如遇AOT后无法在Windows 7运行,请添加YY-Thunks包:
[code][/code]并指定目标框架为net9.0-windows。
9. Winform\兼容XP

如果第8条后还运行不了,请参考上一篇文章《.NET 9 AOT的突破 - 支持老旧Win7与XP环境 - 码界工坊 (dotnet9.com)》添加VC-LTL包,这里不赘述。
10. 其他

还有许多其他需要注意的地方,后续想起来逐渐完善本文。
三、总结

AOT 发布测试虽然过程中可能会遇到诸多问题,但通过及时的测试和正确的配置调整,最终能够实现项目的顺利发布。希望以上总结的经验能对大家在 AOT 使用过程中有所帮助,让大家在开发过程中少走弯路,提高项目的开发效率和质量。同时,也期待大家在实践中不断探索和总结,共同推动技术的进步和发展。
AOT可参考项目:

来源:https://www.cnblogs.com/Dotnet9-com/p/18529239
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!

举报 回复 使用道具