【译】如何使库与本机 AOT 兼容(二)

💡 原文中文,约7400字,阅读约需18分钟。
📝

内容提要

OpenTelemetry是一个用于云应用程序的流行可观测性框架。.NET OpenTelemetry库需要进行一些修复以兼容AOT。主要问题包括使用反射动态填充泛型类型和使用System.Linq.Expressions。该库还需要更新以支持JSON序列化和属性获取。文章还讨论了在Microsoft.Extensions.*库中使用Roslyn源生成器以及Dapper等某些库的本机AOT限制。与客户有效沟通AOT兼容性非常重要。

🔎

延伸解读

AOT 兼容性改造中的权衡

OpenTelemetry 为兼容 AOT 进行了多处修改,包括对 ContextSlotType 构造函数做重大更改,仅接受特定类型。这虽然限制了灵活性,但换来了 AOT 工具的可分析性。类似地,移除 System.Linq.Expressions 的使用避免了 Lambda.Compile() 在 AOT 下的性能损失。这些改动表明,库在支持 AOT 时往往需要在 API 通用性和兼容性之间做出取舍。

源生成器:AOT 兼容的关键工具

文章多次提到使用 Roslyn 源生成器解决 AOT 兼容问题,如 OpenTelemetry 的 JSON 序列化改用 System.Text.Json 源生成器,Microsoft.Extensions.* 库引入选项验证和配置绑定源生成器。源生成器在构建时生成代码,避免了运行时反射和动态 IL 生成,从而与 AOT 兼容。对于需要深度改造的库,源生成器提供了一条可行路径。

并非所有库都能支持 AOT

Dapper 因核心设计依赖运行时生成 IL,无法直接支持 AOT,因此推出了重写版本 Dapper.AOT。而像 MEF 这类可扩展性框架,其本质是在运行时加载未知扩展,与 AOT 理念根本冲突。Newtonsoft.Json 则因需兼顾现有客户和重大更改成本,可能永远不支持 AOT。这些案例说明,库是否支持 AOT 取决于其设计本质和商业考量。

与客户沟通 AOT 支持计划的重要性

文章强调,库作者应开诚布公地告知客户其对 AOT 的支持目标和计划。如果决定不支持,应明确告知,以便客户制定替代方案;如果支持但需要大量工作,也应让客户了解时间线。有效的沟通能帮助客户规划应用程序和依赖库,减少因兼容性问题带来的风险。这是软件开发中极具价值的特质。

❓

Q&A

OpenTelemetry 库在兼容 AOT 时遇到了哪些主要问题?

主要问题包括使用反射动态填充泛型类型(如 MakeGenericType)和使用 System.Linq.Expressions,以及 JSON 序列化和属性获取需要更新以支持 AOT。

如何修复 OpenTelemetry 中 ContextSlotType 的构造函数以支持 AOT?

修复方法是进行一个小的重大更改,只接受在 ContextSlotType 上设置 2 或 3 个特定类型,这些类型是硬编码的,因此不会被删除,使 AOT 工具能看到所有必要代码。

为什么在 AOT 应用程序中建议删除 Expression.Compile() 的使用?

因为当 Lambda.Compile() 表达式时,它会使用解释器来计算表达式,这可能导致性能下降,所以建议在 AOT 应用程序中删除 Expression.Compile() 的使用。

Dapper 库如何解决与原生 AOT 不兼容的问题?

Dapper 的核心设计在运行时生成 IL,与原生 AOT 不兼容,因此推出了 Dapper.AOT 版本,它在构建时生成 ADO.NET 代码,而不是在运行时动态生成 IL,从而兼容 AOT 并减少启动时间。

哪些 .NET 库可能永远不支持原生 AOT?请举例说明。

例如托管可扩展性框架(MEF)和 Newtonsoft.Json。MEF 的设计目的是在运行时加载扩展,与 AOT 不兼容;Newtonsoft.Json 由于现有客户和重大更改的考虑,可能决定不支持 AOT。

在 Microsoft.Extensions.* 库中,如何使用 Roslyn 源生成器来支持 AOT?

可以通过设置 MSBuild 属性 EnableConfigurationBindingGenerator 为 true 来启用配置绑定源生成器,它会生成代码来设置属性,避免反射。对于选项验证,可以创建实现 IValidateOptions 的分部类并应用 [OptionsValidator] 属性,源生成器会在构建时生成验证代码。

🏷️

标签

➡️

继续阅读