protobuf-net 是不是适合序列化任意对象/域模型?

Posted

技术标签:

【中文标题】protobuf-net 是不是适合序列化任意对象/域模型?【英文标题】:Is protobuf-net suited for serializing arbitrary object/domain models?protobuf-net 是否适合序列化任意对象/域模型? 【发布时间】:2013-05-22 13:28:30 【问题描述】:

我一直在探索 CQRS/DDD 原则和模式一段时间,并开始实施一个示例项目,在该项目中我将存储模型拆分为 WriteModel 和 ReadModel。 WriteModel 将使用一个简单的类似 NoSQL 的数据库,其中聚合以键值样式存储,值只是聚合的序列化版本。

我现在正在使用 ProtoBuf-Net 对我的域模型聚合进出存储进行序列化和反序列化。 Other than this post我还没有找到在这方面使用 ProtoBuf-Net 的任何指导或技巧。关键是聚合的序列化和反序列化的(理想)要求是域模型应该尽可能少地了解这个基础设施问题,这意味着以下几点:

    类没有属性 没有构造函数、getter、setter 或任何其他仅用于序列化的代码。 能够使用任何(自定义)类型并对其进行序列化/反序列化。

到目前为止,我只实现了聚合的第一个版本的序列化,效果非常好。我使用RuntimeTypeModel.Default-instance 在运行时配置 MetaModel,并且到处都有UseConstructor = false,这使我能够将序列化机制与我的域程序集完全分开。我什至实现了一个自定义的后反序列化机制,使我能够在 ProtoBuf-Net 将字段反序列化为有效实例后及时初始化字段。所以假设我有这样的 AggregateA 类:

[Version(1)]
public sealed class AggregateA

    private readonly int _x;
    private readonly string _y;

    ...

然后在我的序列化库中,我编写了如下代码:

var metaType = RuntimeTypeModel.Default.Add(typeof(AggregateA), false);
metaType.UseConstructor = false;
metaType.AddField(1, "_x");
metaType.AddField(2, "_y");
...

但是,我意识到到目前为止我只实现了基本场景,我现在开始考虑如何处理我的模型的版本控制。我对更大的重构场景特别感兴趣,其中类型 A 已拆分为类型 A1 和 A2,例如:

[Version(2)]
public sealed class AggregateA1

    private readonly int _x;

    ...


[Version(2)]
public sealed class AggregateA2

    private readonly string _y;

    ...

假设我有一堆 AggregateA 的序列化实例,但现在我的域模型只知道 AggregateA1 和 AggregateA2,你将如何使用 ProtoBuf-Net 处理这种情况?

第二个问题涉及第 3 点:如果您愿意投入一些额外的配置工作,ProtoBuf-Net 是否能够处理任意类型?我已经阅读了使用DateTimeOffset-type 时引发的异常,这让我认为并非所有类型都可以由框架开箱即用地序列化,但是我可以通过在 RuntimeTypeModel 中注册这些类型来序列化它们吗?我是否应该想要去那里?还是最好忘记序列化除简单类型之外的常见 .NET 类型?

【问题讨论】:

【参考方案1】:

protobuf-net 旨在与可预测的已知模型一起工作。的确,一切都可以在运行时进行配置,但我没有考虑如何处理您的A1/A2 场景,正是因为这不是支持的场景(在我的辩护,我看不出它与大多数序列化程序一起工作得很好)。想了想,如果你在某处有配置/映射数据,那么你可以简单地反序列化两次;也就是说,只要我们仍然告诉它 AggregateA1._x 映射到 1AggregateA2._y 映射到 2,你就可以这样做:

object a1 = model.Deserialize(source, null, typeof(AggregateA1));
source.Position = 0; // rewind
object a2 = model.Deserialize(source, null, typeof(AggregateA2));

但是,更复杂的调整需要额外的思考。

关于“任意类型”...定义“任意类型”;p 特别是,支持“代理”类型,这对某些转换很有用 - 但如果没有非常具体的“问题陈述”,很难回答完全。

总结:

protobuf-net 有一个预期用途,其中包括序列化感知(属性等)和非感知场景(运行时配置等) - 但它也适用于一系列更定制的场景(让你下降到如果您愿意,可以使用原始读取器/写入器 API)。它不也不能保证直接适合可以想象的每个序列化场景,它的表现如何取决于你离那个场景有多远。

【讨论】:

关于 A1 / A2 场景:是的,您可能会假设旧类中的 x 和 y 现在将直接映射到 A1.x 和 A2.y,因此实际上需要反序列化两次无论如何(感谢您展示如何做到这一点)。我所说的任意是“你提供给它的任何类型”,只要你已经用 TypeModel 注册了它。例如,我有时在我的域模型中使用 Lazy 类。 protobuf-net 可以通过执行 'RuntimeTypeModel.Default.Add(typeof(Lazy), false)' 来序列化这个吗? @Wim.van.Gool "just by" 可能会产生误导——你可能需要告诉它你想要序列化什么,等等。它保留了一些内置类型;但在一般中你可以这样做,当然

以上是关于protobuf-net 是不是适合序列化任意对象/域模型?的主要内容,如果未能解决你的问题,请参考以下文章

使用 ProtoBuf-Net 进行两组不同的序列化

Protobuf-Net:如何序列化 guid?

为啥我不能使用 ProtoBuf-Net 正确反序列化我的对象?

Protobuf-net“反序列化期间引用跟踪对象更改引用”错误(2)

使用知道架构的 protobuf-net 反序列化未知对象

使用 protobuf-net 反序列化具有某些字段的派生类型的对象