【问题标题】:Serialization across multiple Namespaces with Protobuf-net使用 Protobuf-net 跨多个命名空间进行序列化
【发布时间】:2012-01-30 17:36:11
【问题描述】:

我一直在使用 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 应该能够支持从完全不同的命名空间添加的类?

【问题讨论】:

  • 只要您不使用“DynamicType”选项,protobuf-net 就不会关心命名空间根本;存储的数据中没有类型名称、程序集名称或字段名称。 ProtoException 中的完整信息是什么?
  • 啊...我想知道... 2秒

标签: c# serialization protobuf-net cqrs


【解决方案1】:

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

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

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

那么重要的是,在配置反序列化模型时,兼容的密钥;例如,您可以反序列化为:

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

我的猜测是您添加子类型的机制无法确保数字匹配。它需要一些线索。实际上,查看您的代码,我想知道如果:

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

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

【讨论】:

  • 如果事件被持久化到磁盘并在应用程序的后续运行中读回,我认为这会是一个问题。情况并非如此,应用程序已初始化,RuntimeTypeModel 已创建,事件已发送。当重新创建 RuntimeTypeModel 并尝试从商店读取先前序列化的消息时,上述情况不仅会发生在应用程序的后续运行中吗?
  • @SyntaxC4 那么这里只涉及一个 AppDomain 吗?如果是这样,是的,它应该可以正常工作;你能提供 ProtoException 的详细信息吗? (.Message、.InnerException、.StackTrace 等?)
  • 想一想,抛出异常的消息将被加载到单独的 AppDomain 中。这很可能是问题的原因(考虑到 RuntimeTypeModel 仅在其中一个 AppDomain 中声明)。
猜你喜欢
  • 1970-01-01
  • 2013-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-23
相关资源
最近更新 更多