一、引言

站长接触 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 发布过程大致如下:

  1. 内网 AOT 发布一次需 2、3 分钟,这段时间只能看看需求文档、技术文章、需求文档、技术文章。。。
  2. 发布完成,运行无效果,体现在双击未出现界面,进程列表没有它,说明程序崩溃了,查看系统应用事件日志,日志中通常会包含异常警告信息。
  3. 依据日志信息检查代码,修改相关 API。
  4. 再次进行 AOT 发布,重复上述 1 - 3 步骤。

经过一周的努力,项目 AOT 后功能测试终于正常,至此收工。

(二)AOT 需要注意的点及解决方法

1. 添加rd.xml

在主工程创建一个XML文件,例如Roots.xml,内容大致如下:

<linker>
<assembly fullname="CodeWF.Toolbox.Desktop" preserve="All" />
</linker>

需要支持AOT的工程,在该XML中添加一个assembly节点,fullname是程序集名称,CodeWF.Toolbox.Desktop是站长小工具的主工程名,点击查看源码。

在主工程添加ItemGroup节点关联该XML文件:

<ItemGroup>
<TrimmerRootDescriptor Include="Roots.xml" />
</ItemGroup>

2. Prism支持

站长使用了Prism框架及DryIOC容器,若要支持 AOT,需要添加以下 NuGet 包:

<PackageReference Include="Prism.Avalonia" Version="8.1.97.11073" />
<PackageReference Include="Prism.DryIoc.Avalonia" Version="8.1.97.11073" />

rd.xml需要添加

<assembly fullname="Prism" preserve="All" />
<assembly fullname="DryIoc" preserve="All" />
<assembly fullname="Prism.Avalonia" preserve="All" />
<assembly fullname="Prism.DryIoc.Avalonia" preserve="All" />

3. App.config读写

在.NET Core中使用System.Configuration.ConfigurationManager包操作App.config文件,rd.xml需添加如下内容:

<assembly fullname="System.Configuration.ConfigurationManager" preserve="All" />

使用Assembly.GetEntryAssembly().location失败,目前使用ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None)获取的应用程序程序配置,指定路径的方式后续再研究。

4. HttpClient使用

rd.xml添加如下内容:

<assembly fullname="System.Net.Http" preserve="All" />

5. Dapper支持

Dapper的AOT支持需要安装Dapper.AOT包,rd.xml添加如下内容:

<assembly fullname="Dapper" preserve="All" />
<assembly fullname="Dapper.AOT" preserve="All" />

数据库操作的方法需要添加DapperAOT特性,举例如下:

[DapperAot]
public static bool EnsureTableIsCreated()
{
try
{
using var connection = new SqliteConnection(DBConst.DBConnectionString);
connection.Open(); const string sql = $@"
CREATE TABLE IF NOT EXISTS {nameof(JsonPrettifyEntity)}(
{nameof(JsonPrettifyEntity.IsSortKey)} Bool,
{nameof(JsonPrettifyEntity.IndentSize)} INTEGER
)"; using var command = new SqliteCommand(sql, connection);
return command.ExecuteNonQuery() > 0;
}
catch (Exception ex)
{
return false;
}
}

6. System.Text.Json

参考JsonExtensions.cs

序列化

public static bool ToJson<T>(this T obj, out string? json, out string? errorMsg)
{
if (obj == null)
{
json = default;
errorMsg = "Please provide object";
return false;
} var options = new JsonSerializerOptions()
{
WriteIndented = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};
try
{
json = JsonSerializer.Serialize(obj, options);
errorMsg = default;
return true;
}
catch (Exception ex)
{
json = default;
errorMsg = ex.Message;
return false;
}
}

反序列化

public static bool FromJson<T>(this string? json, out T? obj, out string? errorMsg)
{
if (string.IsNullOrWhiteSpace(json))
{
obj = default;
errorMsg = "Please provide json string";
return false;
} try
{
var options = new JsonSerializerOptions()
{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
TypeInfoResolver = new DefaultJsonTypeInfoResolver()
};
obj = JsonSerializer.Deserialize<T>(json!, options);
errorMsg = default;
return true;
}
catch (Exception ex)
{
obj = default;
errorMsg = ex.Message;
return false;
}
}

7. 反射问题

参考项目CodeWF.NetWeaver

  1. 创建指定类型的List<T>Dictionary<T>实例:
public static object CreateInstance(Type type)
{
var itemTypes = type.GetGenericArguments();
if (typeof(IList).IsAssignableFrom(type))
{
var lstType = typeof(List<>);
var genericType = lstType.MakeGenericType(itemTypes.First());
return Activator.CreateInstance(genericType)!;
}
else
{
var dictType = typeof(Dictionary<,>);
var genericType = dictType.MakeGenericType(itemTypes.First(), itemTypes[1]);
return Activator.CreateInstance(genericType)!;
}
}
  1. 反射调用List<T>Dictionary<T>Add方法添加元素失败,下面是伪代码:
// List<T>
var addMethod = type.GetMethod("Add");
addMethod.Invoke(obj, new[]{ child }) // Dictionary<Key, Value>
var addMethod = type.GetMethod("Add");
addMethod.Invoke(obj, new[]{ key, value })

解决办法,转换为实现的接口调用:

// List<T>
(obj as IList).Add(child); // Dictionary<Key, Value>
(obj as IDictionary)[key] = value;
  1. 获取数组、List<T>Dictionary<key, value>的元素个数

同上面Add方法反射获取Length或Count属性皆返回0,value.Property("Length", 0),封装的Property非AOT运行正确:

public static T Property<T>(this object obj, string propertyName, T defaultValue = default)
{
if (obj == null) throw new ArgumentNullException(nameof(obj));
if (string.IsNullOrEmpty(propertyName)) throw new ArgumentNullException(nameof(propertyName)); var propertyInfo = obj.GetType().GetProperty(propertyName);
if (propertyInfo == null)
{
return defaultValue;
} var value = propertyInfo.GetValue(obj); try
{
return (T)Convert.ChangeType(value, typeof(T));
}
catch (InvalidCastException)
{
return defaultValue;
}
}

AOT成功:直接通过转换为基类型或实现的接口调用属性即可:

// 数组
var length = ((Array)value).Length; // List<T>
if (value is IList list)
{
var count = list.Count;
} // Dictionary<key, value>
if (value is IDictionary dictionary)
{
var count = dictionary.Count;
}

8. Windows 7支持

如遇AOT后无法在Windows 7运行,请添加YY-Thunks包:

<PackageReference Include="YY-Thunks" Version="1.1.4-Beta3" />

并指定目标框架为net9.0-windows

9. Winform\兼容XP

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

10. 其他

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

三、总结

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

AOT可参考项目:

AOT使用经验总结的更多相关文章

  1. MyEclipse10--的使用经验

    MyEclipse10--的使用经验总结 ------------------ 1.MyEclipse中的验证validation----->>用MyEclipse做ExtJs项目研发的时 ...

  2. XCode的个人使用经验

    Xcode是强大的IDE(但个人觉得不如Visual Studio做得好),其强大功能无需本人再赘述,本文也不是一篇“快捷键列表”,因为XCode上的快捷键极其多,而且还有不少是需要同时按下四个按键的 ...

  3. Flask-admin使用经验技巧总结

    笔者是看狗书入门的flask,狗书上对于flask-admin这个扩展并没有进行讲解,最近因为项目需要,学习使用flask-admin,瞬间体会到了flask开发的快速.扩展的强大 Flask-adm ...

  4. Unity3D使用经验总结 缺点篇

    不论是从官方手册,还是各种第三方教程,几乎涉及到的,都是讲如何使用U3D,以及U3D的优点. 虽然我是用的一个让步语气,但请不要否认U3D的这些优点,它们的确存在. 但对于一个引擎的特性来说,优点与缺 ...

  5. Unity3D使用经验总结 优点篇

    09年还在和其它小伙伴开发引擎的时候,Unity3D就初露头角. 当时就对这种基于组件式的设计结构很不理解. 觉得拆分过于细致,同时影响效率. 而时至今日,UNITY3D已经成为了众多团队的首选3D引 ...

  6. 控件使用经验-MVP模式+控件封装

    项目背景 几年前参与了一个面向学校的人事管理软件的开发,基于WinForm平台.今天主要想谈一谈其中关于控件的使用经验.这个项目我们大量使用了第三方控件.由于这个产品的生命周期很长,我们在设计时要考虑 ...

  7. 走进异步世界-犯傻也值得分享:ConfigureAwait(false)使用经验分享

    在上周解决“博客程序异步化改造之后遭遇的性能问题”的过程中,我们干了一件自以为很有成就感的事——在表现层(MVC与WebForms)将所有使用await的地方都加上了ConfigureAwait(fa ...

  8. Ahead-of-time compilation(AOT)

    Ahead-of-time (AOT) compilation is the act of compiling a high-level programming language such as C  ...

  9. ART、JIT、AOT、Dalvik之间的关系

    原文地址: https://github.com/ZhaoKaiQiang/AndroidDifficultAnalysis/blob/master/10.ART%E3%80%81JIT%E3%80% ...

  10. C# JIT & AOT

    http://msdn.microsoft.com/library/z1zx9t92 http://msdn.microsoft.com/en-us/library/ht8ecch6(v=vs.90) ...

随机推荐

  1. 【主席树】P3919 【模板】可持久化线段树 1

    P3919 [模板]可持久化线段树 1(可持久化数组) - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include <bits/stdc++.h> using na ...

  2. JavaScript设计模式样例九 —— 桥接模式

    桥接模式(Bridge Pattern) 定义:是用于把抽象化与实现化解耦,使得二者可以独立变化. 目的:将抽象部分与实现部分分离,使它们都可以独立的变化. 场景:实现系统可能有多个角度分类,每一种角 ...

  3. JAVA——水仙花数问题

    2024/07/12 1.问题 2.错误解法 3.错误分析 4.正确解法 5.其他:关于Java中幂函数的用法 6.参考 1.问题 2.错误解法 import java.util.Scanner; p ...

  4. Density-invariant Features for Distant Point Cloud Registration论文阅读

    Density-invariant Features for Distant Point Cloud Registration 2023 ICCV *Quan Liu, Hongzi Zhu, Yun ...

  5. 预设型 DP

    预设型 DP <美好的一天>--青春学概论 한 잔 술에 취해 잠긴 목엔 沉醉于一杯酒 갈라지는 목소린 다시 带着沙哑的嗓音 두 잔 자기 전엔 기분 좋음 入睡前饮下第二杯让心情愉悦 ...

  6. WebShell流量特征检测_中国菜刀篇

    80后用菜刀,90后用蚁剑,95后用冰蝎和哥斯拉,以phpshell连接为例,本文主要是对这四款经典的webshell管理工具进行流量分析和检测. 什么是一句话木马? 1.定义 顾名思义就是执行恶意指 ...

  7. 动态规划——详解leetcode518 零钱兑换 II

    动态规划 零钱兑换 II 参考书目:<程序员代码面试指南:IT名企算法与数据结构题目最优解> 给定不同面额的硬币和一个总金额.写出函数来计算可以凑成总金额的硬币组合数.假设每一种面额的硬币 ...

  8. 使用Joi 完成JavaScript 数据校验

    无论是在前端还是后端(Node.js),数据校验都是一件不可或缺的事情,使用JOI让我们轻松的完成数据校验 > npm install joi 这里持续记录使用心得 版本:17.4.2 官网:h ...

  9. Flutter Forward 活动正式发布

    2023 年 1 月 25 日,Flutter 团队将在肯尼亚首都内罗毕举办 Flutter Forward 大会,并同时开启线上直播,敬请期待! 活动将于北京时间 1 月 25 日 22:30 开始 ...

  10. Unity中的光源类型(向前渲染路径进行光照计算)

    Unity中的光源类型 Unity中共支持4种光源类型: 平行光 点光源 聚光灯 面光源(在光照烘焙时才可以发挥作用) 光源的属性: 位置 方向(到某个点的方向) 颜色 强度 衰减(到某个点的衰减) ...