PAYATHON 2026

ServiceStack 客户端启用 PublishAot 后报错,如何解决?

数据阿明

结论

仅添加 <PublishAot>true</PublishAot>,不能保证 JsonServiceClient 在 Native AOT 下正常运行。对于这个简单的 JSON GET 请求,如果必须使用 Native AOT,可以改用 HttpClient,通过 System.Text.Json 源生成提供反序列化所需的类型信息。

题目没有提供具体的 .NET SDK 和 ServiceStack 包版本,因此无法认定所有版本、所有 ServiceStack 客户端都不支持 AOT,也无法保证某个配置能修复当前异常。ServiceStack 维护者在 2024 年关于 iOS/AOT 的讨论中提到过支持限制,但该讨论有特定的平台和时间背景,不能直接作为所有后续版本的兼容性结论。ServiceStack 维护者说明

为什么普通运行正常,AOT 发布后失败?

Native AOT 在发布时提前编译代码,并要求进行裁剪分析。如果调用路径依赖运行时动态生成代码,或通过反射访问未被保留的成员,程序可能在发布后失败。普通运行成功,不能证明相同代码满足这些限制。.NET Native AOT 文档

异常只在 AOT 发布后出现,可以优先排查客户端内部的 AOT 和裁剪兼容性。不过,ArgumentNullException 中的 method 只是异常参数名。仅凭这段堆栈,无法确定具体哪个方法或哪项元数据缺失,也不能断定 DTO 一定被裁剪了。

示例代码还有一处不一致:

var results = await client.GetAsync<List<fake>>("posts");

await 后得到的是 List<fake>,后续应使用:

Console.WriteLine($"got {results.Count}");

原代码中的 results.Result.Count 与这个返回类型不符。应先让最小复现代码与实际运行代码一致;这处修改本身不能解决 ServiceStack 内部的 AOT 兼容性问题。

必须使用 Native AOT 时,如何改写?

下面的示例保留原有 DTO 属性名,改用 HttpClient 请求接口,并显式传入源生成的 JsonTypeInfo。Microsoft 文档建议在 Native AOT 场景中使用 JSON 源生成,避免依赖默认的反射序列化路径。System.Text.Json 源生成文档

将控制台程序的 Program.cs 改为:

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Json;
using System.Text.Json.Serialization;

using var client = new HttpClient
{
    BaseAddress = new Uri("https://jsonplaceholder.typicode.com/")
};

var results = await client.GetFromJsonAsync(
    "posts",
    AppJsonContext.Default.PostList);

if (results is null)
{
    throw new InvalidOperationException("响应 JSON 为 null。");
}

Console.WriteLine($"got {results.Count}");

public class fake
{
    [JsonPropertyName("userId")]
    public int userid { get; set; }

    public int id { get; set; }
    public string title { get; set; } = "";
    public string body { get; set; } = "";
}

[JsonSerializable(typeof(List<fake>), TypeInfoPropertyName = "PostList")]
internal partial class AppJsonContext : JsonSerializerContext
{
}

示例中:

  • [JsonSerializable] 注册实际响应类型 List<fake>。
  • GetFromJsonAsync 使用接收 JsonTypeInfo 的重载,将生成的类型信息传入反序列化过程。
  • [JsonPropertyName("userId")] 明确将 JSON 的 userId 映射到原有属性 userid,避免依赖大小写匹配设置。

在支持 Native AOT 的 .NET 控制台项目中,保留以下发布配置:

<PropertyGroup>
  <PublishAot>true</PublishAot>
</PropertyGroup>

按目标平台发布。例如,目标为 Linux x64 时:

dotnet publish -c Release -r linux-x64

linux-x64 需要按实际部署平台替换,构建环境也必须满足目标平台的 Native AOT 工具链要求。验证时,应运行发布目录中的原生可执行文件。示例会输出实际返回的列表数量;题目中的 got 100 是原始运行结果,不是这里重新测试得到的结果。

如果需要继续使用 ServiceStack 客户端

先记录 .NET SDK、目标框架、ServiceStack 包版本和发布目标,再检查发布日志中的裁剪与 AOT 警告。修复或解释这些警告,比单纯隐藏警告更有助于定位问题。

ServiceStack 文档提供了让 JsonApiClient 使用 System.Text.Json 的配置:

ClientConfig.UseSystemJson = UseSystemJson.Always;

切换 JSON 序列化器意味着序列化路径发生了变化,但不能证明整个客户端调用链已兼容 Native AOT。是否能使用源生成上下文,以及是否仍有其他不兼容路径,需要结合具体版本确认。ServiceStack System.Text.Json 文档

如果当前版本没有适用的兼容性保证,而项目又必须保留现有客户端,可以先关闭 Native AOT,使用普通 .NET 发布方式。不要把给 fake 添加成员保留标记当作通用修复:保留反射元数据无法解决所有运行时动态代码生成问题。

备注:内容仅供参考。