我已经搜索了一段时间,并且发现了各种线程表明protobuf-net可以处理可空类型,各种关于空集合(此处不相关)和默认值行为的线程,对于proto2中以前可能是“optional”字段的内容,但我没有找到以下问题的具体答案。这将是我第一次使用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)(或让订阅者将缺失的元素反序列化为默认值)来传达“没有更新”的语义,因为当消息被App2接收时,这将导致
App2Products
表中的productWeightKg
值设置为0。我们也不能发送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类及其所有可能的子实现进行装饰,是吗?