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

发布于 2024-12-29 17:43:13 字数 1948 浏览 1 评论 0原文

我一直在开发一个使用 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);
        }
    }

还将一些类型手动添加到默认运行时类型模型:

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

当所有消息都在时,以上所有内容似乎都工作正常:

逻辑分组.事件

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

ReferenceGrouping.Commands

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

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

I have been working on a system which I'm using protobuf-net (version 2.0.0.480) for serializing messages. This application uses a CQRS approach in which commands and events have been separated into different namespaces [and assemblies].

The code will dynamically add the types at runtime for any class that inherits from MessageBase. This is done using the code below:

    // 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);
        }
    }

A few types are added manually to the Default RuntimeTypeModel as well:

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

All the above seems to work fine when all messages were in:

LogicalGrouping.Events

The project moved forward and a new namespace was added:

ReferenceGrouping.Commands

Once ReferenceGrouping.Commands is added and attempts to send a message a ProtoException is thrown. The only workaround that I found for this behaviour is to add the Commands from ReferenceGrouping.Commands into LogicalGrouping.Events.

Is this the anticipated behaviour or should the RuntimeTypeModel be able to support classes being added from completely different namespaces?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

吖咩 2025-01-05 17:43:13

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

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

  • Foo
    • SubFoo1(键=2)
    • SubFoo2(键=5)

然后,在配置模型进行反序列化时,兼容的键至关重要;例如,您可以反序列化为:

  • Bar
    • MegaBar(键=2)
    • UltraBar(键=5)

我的猜测是,您添加子类型的机制并不能确保数字匹配。它需要一些线索。实际上,看看你的代码,我想知道它目前是否也会中断,如果:

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

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

Protobuf-net, as per my comment, simply does not care about namespaces, type names or member-names, since it uses numeric keys as identifiers (in accordance with the protobuf spec). This means you can deserialize on a completely different model in dfferent assemblies, as long as the numbers make sense.

Looking at the code, I strongly suspect the problem is that you don't have reliable (repeatable) sub-type identifiers. If you have, when serializing:

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

Then it is vitally important, when configuring the models for deserialization, keys that are compatible; for example you could deserialize into:

  • Bar
    • MegaBar (key=2)
    • UltraBar (key=5)

My guess is that your mechanism for adding sub-types is not ensuring the numbers match. It needs some clue. Actually, looking at your code I wonder if it could also break currently if:

  • adding types
  • removing types
  • renaming types
  • relocating types
  • just ... Any time (the order of types is not guaranteed, IIRC)

My advice would be: keep an external registration somewhere of what each key means in terms of sub-types. Or: do the same in code using ProtoIncludeAttribute.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文