使用Protobuf-net在多个命名空间中进行序列化

时间:2012-01-30 17:36:12

标签: c# serialization protobuf-net cqrs

我一直在研究一个系统,我正在使用protobuf-net(版本2.0.0.480)来序列化消息。此应用程序使用CQRS方法,其中命令和事件已分为不同的名称空间[和程序集]。

代码将在运行时为从MessageBase继承的任何类动态添加类型。这是使用以下代码完成的:

    // Used as a unique reference for each type in a member
    private static int _sequence = 1000; 
    public static void RegisterAll()
    {
        RegisterAllDerivedFrom<MessageBase>();
    }
    public static void RegisterAllDerivedFrom<T>(params Assembly[] assemblies)
    {
        if (assemblies == null || assemblies.Length == 0)
        {
            assemblies = AppDomain.CurrentDomain.GetAssemblies();
        }

        var type = typeof(T);
        var model = RuntimeTypeModel.Default;
        var metaModel = model.Add(type, true);

        RegisterAllBaseTypes(type, metaModel, model, assemblies);
    }
    private static void RegisterAllBaseTypes(Type type, MetaType metaModel, RuntimeTypeModel model, params Assembly[] assemblies)
    {
        foreach (var t in assemblies.SelectMany(a => a.GetTypes().Where(t => t.BaseType != null && t.BaseType == type)))
        {
            var subModel = model.Add(t, true);
            metaModel.AddSubType(_sequence, t);
            _sequence++;

            RegisterAllBaseTypes(t, subModel, model, assemblies);
        }
    }

手动将一些类型添加到Default RuntimeTypeModel:

RuntimeTypeModel.Default.Add(typeof(ReferenceNumber), true)
            .AddSubType(100, typeof(Product))
            .AddSubType(110, typeof(ProductGroup));

当所有消息都在:

时,以上所有内容似乎都能正常工作
  

LogicalGrouping.Events

项目向前推进,并添加了新的命名空间:

  

ReferenceGrouping.Commands

添加 ReferenceGrouping.Commands 并尝试发送消息时,会抛出 ProtoException 。我发现此行为的唯一解决方法是将 ReferenceGrouping.Commands 中的命令添加到 LogicalGrouping.Events

这是预期的行为还是RuntimeTypeModel能否支持从完全不同的命名空间添加的类?

1 个答案:

答案 0 :(得分:3)

根据我的评论,Protobuf-net根本不关心命名空间,类型名称或成员名称,因为它使用数字键作为标识符(根据protobuf规范)。这意味着只要数字有意义,您就可以在不同的程序集中对完全不同的模型进行反序列化。

查看代码,我强烈怀疑问题是您没有可靠(可重复)的子类型标识符。如果你有,在序列化时:

    • SubFoo1(key = 2)
    • SubFoo2(key = 5)

然后,在配置用于反序列化的模型时,兼容的键是非常重要的;例如,您可以反序列化为:

  • 酒吧
    • MegaBar(key = 2)
    • UltraBar(key = 5)

我的猜测是你添加子类型的机制并不能确保数字匹配。它需要一些线索。实际上,看看你的代码,我想知道它是否也可能在当前打破:

  • 添加类型
  • 删除类型
  • 重命名类型
  • 重新定位类型
  • 只是......任何时候(类型的顺序都不能保证,IIRC)

我的建议是:在每个键的子类型方面保留一个外部注册。或者:使用ProtoIncludeAttribute在代码中执行相同操作。