【发布时间】:2020-06-16 14:55:55
【问题描述】:
我已经搜索了一段时间,并找到了表明 protobuf-net 可以处理可为空类型的各种线程、关于诸如空集合之类的各种线程(此处不相关)以及以前的默认值行为在proto2中一直是“可选”字段,但我还没有找到以下问题的具体答案。这将是我第一次使用 protobuf-net 或协议缓冲区作为序列化格式。
想象一下,我正在多个不同的应用程序之间使用事件承载状态传输模式发送消息 - 因此消息的发布者和订阅者包含状态 - 以及该状态的某种共享模型。任何参与的应用程序都有一些“相同”实体的表示(因此是 ECST),但并非所有系统都理解所有属性。就应用程序的 SQL 数据库中的持久性而言,可能如下所示(为简洁起见,省略了比例):
table App1Products { productKey int, productName varchar null }
table App2Products { productKey int, productName varchar null, productWeightKg decimal null }
table App3Products { productKey int }
为了这个例子,假设普通共享模型是所有不同属性的联合:{ productKey, productName, productWeightKg }
现在假设有人在App1Products 中更新了productName。我们想发布改变的状态。当我们这样做时,我们无法填充整个共享模型,因为 App1 在其架构中不包含 productWeightKg。我们需要以某种方式“忽略”这个元素的任何值,让潜在消费者明白它没有被填充。
我们不能仅仅发送一个默认值 (0)(或让订阅者将缺失的元素反序列化为默认值)来传达“不更新”语义,因为这会导致 productWeightKg 值设置为 0当 App2 收到消息时,在App2Products 表中。我们不能发送 null 来传达“不更新”语义,因为 null 也是该列的合法值。
最终,我们需要 App2 的订阅者代码来构造一个更新语句,这样要么不引用 productWeightKg 列,要么只是将其设置为自身,我们需要某种方式告诉 App2 的订阅者代码来执行此操作.
一种解决方案似乎是在消息中为每个字段创建一个附加元素,指示该字段是否已设置。在消息内容方面,我们可能会这样使用:
[ProtoContract]
public class Product
{
[ProtoMember(1)]
public int ProductKey { get; set; }
[ProtoMember(2)]
public string productName { get; set; }
[ProtoMember(3)]
public decimal? productWeightKg { get; private set; }
[ProtoMember(4)]
public bool productWeightKgSet { get; private set; }
public void SetProductWeight(decimal? weight)
{
productWeightKg = weight;
productWeightKgSet = true;
}
public void ClearProductWeight()
{
productWeightKgSet = false;
}
}
如果这是一种合理的使用模式,那么下一个“显而易见”的想法是为这种行为创建某种模板类,我们可以将其重用于我们所有的消息类...
public class Optional<T>
{
public T Value { get; private set; }
public bool HasValue { get; private set; }
public void Set(T val) { Value = val; HasValue = true; }
public void Clear() { HasValue = false; Value = default; }
}
这是解决这个问题的一种合理的方法,还是我错过了一些其他“已知的好模式”,或者这个模式在 protobuf-net 中不能按预期工作?
目前我的有限理解是,这可能需要使用 ProtoInclude 属性使用其所有可能的子实现来装饰 Optional 类,对吗?
【问题讨论】:
-
听起来您正在考虑将“条件序列化”与“合并”(而不是反序列化)相结合——这里的答案涵盖了很多类似的领域——有什么用吗? stackoverflow.com/questions/61931285/…
-
@MarcGravell 是的,在增量方面也有类似的想法。我表达了点对点消息,但这里的上下文是通过代理和公共数据模型发布/订阅,所有 App1...AppN 都可以使用它来共享状态,而无需明确了解彼此。一个简单的通用模型将是所有系统中所有语义上不同的属性的联合。任何单个发布者都可能无法填充整个共享模型,但任何订阅者都需要知道发布者没有填充哪些字段,以便它们不会被视为“真实”更改(例如,变为 null)。
-
据我了解链接线程,我可以看到潜在有用的想法是让每条消息包含一个表示所有字段的单个位掩码,并以某种方式读取掩码,而不是解决方案其中每个“真实”字段都包含自己的伴随布尔值。这消除了将 Optional
作为消息中任何元素的基本类型的需要,这似乎有助于 protobuf-net 实现、删除继承和嵌套,尽管在添加新元素时确实需要更加小心字段。 -
我已更新问题以更好地反映 pub/sub 通用模型状态转移语义。
标签: c# protobuf-net