Summary of experience in using AOT

Summary of experience in using AOT

From the very beginning of project creation, you should develop a good habit of conducting AOT release testing in a timely manner whenever new features are added or newer syntax is used.

最后更新 2/7/2026 2:26 PM
沙漠尽头的狼
预计阅读 10 分钟
分类
.NET
专题
C# AOT
标签
.NET C# Dapper Prism AOT

1. Introduction

站长接触 AOT 已有 3 个月之久,此前在《好消息:NET 9 X86 AOT 的突破 - 支持老旧 Win7 与 XP 环境》一文中就有所提及。在这段时间里,站长使用 Avalonia 开发的项目也成功完成了 AOT 发布测试。然而,这一过程并非一帆风顺。站长在项目功能完成大半部分才开始进行 AOT 测试,期间遭遇了不少问题,可谓是 “踩坑无数”。为了方便日后回顾,也为了给广大读者提供参考,在此将这段经历进行总结。

NET AOT is a technology for compiling. NET code into native code in advance. It has many advantages, such as fast start-up speed, reducing runtime resource consumption, and improving security. After AOT is released, there is no need to install dependencies such as the. NET runtime. After the release of NET 8 and 9 AOT, it can run under XP, Win7 and non-SP1 operating systems. This makes application deployment more convenient, adapts to more old system environments, expands application scenarios for developers, improves performance, and also increases system compatibility, making the development and deployment of. NET applications more flexible and extensive, bringing a better experience to users.

2. Talk about experience

1. The importance of testing strategies

From the very beginning of project creation, you should develop a good habit of conducting AOT release testing in a timely manner whenever new features are added or newer syntax is used. Otherwise, problems will accumulate in the later stage and will be extremely difficult to solve. The webmaster paid a heavy price for ignoring this point in the early stage. The helpless solution is to recreate the project, then restore the functions one by one and conduct AOT testing. After a week of overtime AOT testing, the release process of each AOT is roughly as follows:

  1. It takes 2 to 3 minutes to publish AOT on the intranet. During this time, you can only read requirements documents, technical articles, requirements documents, and technical articles...
  2. Publishing is completed, but running has no effect. This is reflected in the fact that the interface does not appear when double-clicking and it is not in the process list, indicating that the program has crashed. Check the system application event log, which usually contains abnormal warning information.
  3. Check the code based on log information and modify relevant APIs.
  4. Release AOT again and repeat steps 1 - 3 above.

After a week of hard work, the functional test after the AOT of the project finally became normal, and it was over.

(2) Points to pay attention to in AOT and solutions

1. Add 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 support

The webmaster uses the Prism framework and the DryIOC container. To support AOT, you need to add the following NuGet packages:

<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" />

8.1.97.11073 Version is the last open source version, and 9.X and later are paid versions

3. App.config reading and writing

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

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

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

4. Using HttpClient

rd.xml添加如下内容:

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

一般不直接使用HttpClient,可尝试Refit之类的三方库用作Http请求更便捷:

<assembly fullname="Refit" preserve="All" />

5. Dapper support

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

serialization

public static bool ToJson<T>(this T obj, JsonSerializerOptions? options,  out string? json, out string? errorMsg)
{
    if (obj == null)
    {
        json = default;
        errorMsg = "Please provide object";
        return false;
    }

    if (options == null)
    {
        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;
    }
}

to deserialize

public static bool FromJson<T>(this string? json, JsonSerializerOptions? options, out T? obj, out string? errorMsg)
{
    if (string.IsNullOrWhiteSpace(json))
    {
        obj = default;
        errorMsg = "Please provide json string";
        return false;
    }

    try
    {
        if (options == null)
        {
            options = new JsonSerializerOptions()
            {
                Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
                TypeInfoResolver = new DefaultJsonTypeInfoResolver(),
                Converters = { new NullableDateTimeConverter() }
            };
        }

        obj = JsonSerializer.Deserialize<T>(json!, options);
        errorMsg = default;
        return true;
    }
    catch (Exception ex)
    {
        obj = default;
        errorMsg = ex.Message;
        return false;
    }
}

上面配置简单类序列化与反序列化正常用,如果类中存在int?double?之类的可空类型:

public class Project
{
    public int? Id { get; set; }

    public string Name { get; set; }

    public int Record { get; set; }

    [XmlArray(ElementName = "Members")]
    [XmlArrayItem(typeof(Member))]
    public List<Member> Members { get; set; }
}

则创建一个JsonSerializerContext的子类即可解决:

[JsonSerializable(typeof(Project))]
[JsonSerializable(typeof(List<Member>))]
internal partial class ProjectJsonSerializerContext : JsonSerializerContext
{
}

** Note: Just create it well and you don't need to proactively call it **

Traditional JsonSerializer.Serialize(project) relies on runtime reflection to parse Project class properties, characteristics and other information, but AOT compilation prunes reflection metadata that is not explicitly referenced during compilation time, resulting in serialization failure (throwing NotSupportedException or failing to serialize/deserialize certain properties).

ProjectJsonSerializerContext works by generating static metadata during compile time.

7. reflection problem

参考项目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 })

Solution, convert it to implemented interface calls:

// 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 success: You can directly call the attribute by converting it to the base type or implemented interface:

// 数组
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 support

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

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

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

9. Winform\Compatible with XP

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

10. other

There are many other points that need to be paid attention to, and I will gradually improve this article when I think about it later.

3. Summary

Although many problems may be encountered during AOT release testing, through timely testing and correct configuration adjustments, the project can finally be released smoothly. I hope that the experience summarized above can be helpful to everyone in the use of AOT, so that everyone can avoid detours during the development process and improve the development efficiency and quality of the project. At the same time, we also expect everyone to continue to explore and summarize in practice and jointly promote the progress and development of technology.

AOT can refer to items:

Keep Exploring

延伸阅读

更多文章
同分类 / 同专题 8/29/2023

.NET 8.0 AOT DebugView

Debugview is an application that allows you to monitor debug output on your local system or any computer on a network accessible via TCP/IP.

继续阅读