.NET Orleans Web应用运行时遭遇CodecNotFoundException问题求助
解答
控制台与Web宿主的核心差异
- 控制台
HostBuilder:默认启用本地调用优化(Local Call Optimization),同一进程内的Grain调用会直接绕过序列化层,直接调用内存中的方法,因此CancellationToken这类不可序列化的参数不会触发序列化检查,自然不会报错。 - Web
WebApplication.CreateBuilder:默认配置下强制启用全流程序列化,即使Grain运行在同一进程内,Orleans也会尝试序列化所有方法参数。而CancellationToken本身是不可序列化的类型,Orleans默认未为其注册编解码器,因此抛出CodecNotFoundException。
解决方法
方案1:启用本地调用优化(推荐)
直接让Web宿主的Orleans行为与控制台一致,跳过同一进程内的序列化步骤:
var builder = WebApplication.CreateBuilder(args); builder.UseOrleans(orleansBuilder => { orleansBuilder.UseLocalhostClustering(); // 启用本地调用优化 orleansBuilder.Configure<ClusterOptions>(options => { options.LocalCallOptimization = LocalCallOptimization.Enabled; }); }); var host = builder.Build(); // 后续启动逻辑 await host.StartAsync(); // ...
方案2:注册CancellationToken编解码器
如果需要保留全序列化流程(比如后续可能扩展到多进程集群),可以手动注册编解码器来处理CancellationToken:
var builder = WebApplication.CreateBuilder(args); builder.UseOrleans(orleansBuilder => { orleansBuilder.UseLocalhostClustering(); orleansBuilder.ConfigureServices(services => { services.AddSerializer(serializerBuilder => { serializerBuilder.AddCodec<CancellationToken>(new CancellationTokenCodec()); serializerBuilder.AddCodec<CancellationToken?>(new NullableCancellationTokenCodec()); }); }); }); var host = builder.Build();
对应的编解码器实现:
using Orleans.Serialization; using Orleans.Serialization.Buffers; using Orleans.Serialization.Codecs; public class CancellationTokenCodec : IFieldCodec<CancellationToken> { public void WriteField<TBufferWriter>(ref Writer<TBufferWriter> writer, uint fieldIdDelta, Type expectedType, CancellationToken value) where TBufferWriter : IBufferWriter<byte> { // 写入空值,因为CancellationToken无法跨进程传递,本地调用也不会用到序列化后的值 writer.WriteNull(fieldIdDelta); } public CancellationToken ReadValue<TInput>(ref Reader<TInput> reader, Field field) { reader.ReadNull(); return CancellationToken.None; } } public class NullableCancellationTokenCodec : IFieldCodec<CancellationToken?> { public void WriteField<TBufferWriter>(ref Writer<TBufferWriter> writer, uint fieldIdDelta, Type expectedType, CancellationToken? value) where TBufferWriter : IBufferWriter<byte> { writer.WriteNull(fieldIdDelta); } public CancellationToken? ReadValue<TInput>(ref Reader<TInput> reader, Field field) { reader.ReadNull(); return null; } }
补充说明
虽然Grain方法中的CancellationToken参数无法真正生效(Grain调用是异步且可能跨进程,取消令牌无法跨边界传递状态),但通过上述配置可以保留API的兼容性,无需修改现有接口定义。
内容的提问来源于stack exchange,提问作者david-1160
相关产品推荐
相关产品推荐

