更新 List<T> 中的现有字段并重新保存到同一个 protobuf.net 文件中

Update existing field in List<T> and re-save into same protobuf.net file

获得了包含 100 多个字段的 POCO(使用 DataMember(Order=X) 属性将它们标记为 ProtoBuf.Net 序列化)。

我们需要对文件进行一些 post 处理,其中一个字段的值需要计算(我们只能在读完一次后才能执行)更新并再次保存。 Atm 我们正在重新保存到新文件中,这显然有一些 IO/file 开销。

有什么方法可以更新现有文件,其中只有字段 "X" 的值需要更新和保存。希望这应该比从头开始生成新文件更快。

示例 POCO 数据结构(每个文件将包含约 300 万个项目)

[DataContract]
public class DataItem
{
    [DataMember(Order=1)]
    public string ProfitCentre {get; set;}

    [DataMember(Order=2)]
    public string BusinessFunction {get; set;}

    //this is the value that needs to be updated once file is written
    [DataMember(Order=3)]
    public double AdjustedAmount {get; set; } 
}

这在一定程度上取决于模型的结构。 protobuf 格式允许 append-as-update,这意味着:可以在文件的 end 处写入一个值,然后将其应用于代码模型。但是,有一个限制:它不适用于 repeated 元素(包括 map<...> 元素)。它可以应用于根对象或任意数量的 optional / required 子对象,但不适用于 repeated。另一个限制是您需要一个 API 来写入该值,而 该值。有多种方法可以实现这一点,例如,如果我们有:

Foo
 - Bar (field 3 of Foo)
   - Blap (field 7 of Bar)
     - X (string, field 4 of Blap)

然后其中一种这样做的方法是拥有第二个模型只是为了这个目的更新X 字段,如:

[ProtoContract]
class FooUpdate {
    [ProtoMember(3)]
    public BarUpdate Bar {get;set;} = new BarUpdate();
}
[ProtoContract]
class BarUpdate {
    [ProtoMember(7)]
    public BlapUpdate Blap {get;set;} = new BlapUpdate();
}
[ProtoContract]
class BlapUpdate {
    [ProtoMember(4)]
    public string X {get;set;}
}

现在;假设我们之前写了一个 regular Foo 到一个文件,我们可以构造:

var obj = new FooUpdate { Bar = { Blap = { X = "abcdef" } } };
Serializer.Serialize(existingFile, obj);

仅附加 此数据 (我在这里假设 existingFile 位于末尾,以便附加目的)。

请注意,使用 "conditional serialization" 也可以实现类似的功能,但要关闭 一切 似乎需要做很多工作,而我们只能...没有任何要关闭的东西。

但是,如果您要写入的数据位于 repeated / map<...> 字段中:您只需重写整个文件。在这种情况下,追加将导致额外的列表元素,或来自地图元素的数据的损失