在最新的.NET中,如何使用PublishAot与 ServiceStack客户端?
我有一个简单的ServiceStack客户端程序:
var client = new ServiceStack.JsonServiceClient
{
BaseUri = "https://jsonplaceholder.typicode.com/"
};
var results = await client.GetAsync<List<fake>>("posts");
Console.WriteLine($"got {results.Result.Count}");
class fake
{
public int userid { get; set; }
public int id { get; set; }
public string title { get; set; }
public string body { get; set; }
}
这按预期工作,输出 "got 100"
当我在csproj文件中加入
Unhandled exception. System.AggregateException: One or more errors occurred. (Value cannot be null. (Parameter 'method'))
---> System.ArgumentNullException: Value cannot be null. (Parameter 'method')
at ServiceStack.AsyncServiceClient.<SendWebRequestAsync>d__159`1.MoveNext() + 0x1b4d
--- End of inner exception stack trace ---
at System.Threading.Tasks.Task`1.GetResultCore(Boolean) + 0x72
at Program.<Main>$(String[] args) + 0xef
ServiceStack支持AOT吗?要让它工作,是否需要做些什么特别的?
解决方案
你遇到的不是你代码的一个bug,而是Native AOT的一个限制。
ServiceStack的客户端栈在底层大量依赖反射和动态方法解析(用于请求执行、反序列化等)。当你启用 <PublishAot>true> 时,.NET工具链会积极裁剪那些它看不到为静态可达的元数据和代码路径。因此,ServiceStack期望通过反射找到的一些方法在运行时已经不存在了——因此在缺失的 method 上出现了 ArgumentNullException。
就实际情况而言,ServiceStack现在并非完全兼容AOT。你有时可以通过添加裁剪提示或保留类型来让简单场景工作,但这往往较为脆弱,因为问题不仅在你的DTO(fake),还在于内部的ServiceStack机制也依赖于可用的反射元数据。
如果AOT是必需的,务实的做法是避免使用ServiceStack的客户端,改用更适合AOT的方案,例如 HttpClient 搭配 System.Text.Json(可选使用源码生成器)。如果你想继续使用ServiceStack,你需要坚持使用常规的JIT运行时,或禁用裁剪/AOT功能。
所以简短来说:没有什么“特别”的你忘记了——这只是Native AOT的约束与ServiceStack目前的工作方式之间的不匹配。
备选方案
这个问题,System.ArgumentNullException: Value cannot be null. (Parameter 'method'),是ServiceStack目前无法适应Native AOT的直接后果。
在微软官方的 Official Microsoft .NET Native AOT Documentation 中,我们了解到Native AOT在编译阶段通过静态分析来优化,移除未使用的代码。
ServiceStack提供的 JsonServiceClient 基于一种“动态”的模型,利用运行时反射和动态方法解析,将你的DTO转换为内部请求处理程序。由于这些方法不能被“静态可达”,AOT编译器就会剥离相关元数据。在运行过程中,当ServiceStack尝试对在编译后二进制中已不存在的方法进行反射时,就会出现参数错误。
ServiceStack已公开表示,他们意识到自己库的高度反射性,这些库并不适合在AOT模式下编译。尽管他们增加了诸如GrpcServiceClient之类的功能,利用代码生成,但他们的基础JSON客户端库与之并不兼容。