You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Orleans Web应用运行时遭遇CodecNotFoundException问题求助

解答

控制台与Web宿主的核心差异

  • 控制台HostBuilder:默认启用本地调用优化(Local Call Optimization),同一进程内的Grain调用会直接绕过序列化层,直接调用内存中的方法,因此CancellationToken这类不可序列化的参数不会触发序列化检查,自然不会报错。
  • WebWebApplication.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 02:24:57