Protobuf .NET serialization for inheritance classe

2019-07-24 16:21发布

问题:

I'm trying to migrate my code serializer from NetDataContract to Protobuf.Net.

Let's consider the following class example to help the understanding:

[DataContract(Name "a", IsReference = true)]
class Test
{
    [DataMember(Name = "a")]
    public int Id { get; set; }

    [DataMember(Name = "b")]
    public string Name { get; set; }
}

To be able to use Protobuf .NET using DataContract, I'm using the following options:

RuntimeTypeModel.Default.InferTagFromNameDefault = true;            
RuntimeTypeModel.Default.AutoAddProtoContractTypesOnly = false;

Using those options, the serialization of the example showed above works, but when adding an inheritance of it, the complexity grows. Let's improve our example with this class:

[DataContract(Name = "b", IsReference = true)]
class InheritanceTest : Test
{
    [DataMember(Name = "c")]
    public string Text { get; set; }
}

Now, to be able to serialize the class "InheritanceTest" that inherits from "Test", I must add the ProtoInclude parameter (already tried to use only the KnownType but it didn't work). Class "Test" attributes should be like that:

[DataContract(Name "a", IsReference = true)]
[KnownType(typeof(InheritanceTest)]
[ProtoInclude(<TAG>, typeof(InheritanceTest)]
class Test { ... }

The complexity IMHO is when you have to fill the "TAG" with a number that is not automatically used from the members (DataMembers) auto-assigned order. In this example, if I use TAG=1 it gets error because property Id already uses it. The same with TAG=2 and property Name. So I need to put at least 3.

That's okay as this class is too simple, but what should I do when the class has several properties? And should I change the TAG whenever I add a property to it? Seems terrible for maintenance.

How can I do it in a simpler way? Am I missing something?

Considering it is automatically assigned, it should be done only once and cached. Even better, it should be done in compile time.

Additionally... why can't I use [KnownType] Attribute and the serializer auto-assigns the TAG based on the Name of the DataContract of the class type defined? Note that something similar happens with DataMember using the Name to auto-assign Order.

回答1:

but when adding an inheritance of it, the complexity grows.

Yes, it does.

And should I change the TAG whenever I add a property to it?

You should never change a tag. Ever.

Seems terrible for maintenance.

Exactly.

Considering it is automatically assigned, it should be done only once and cached.

It is, at runtime.

Even better, it should be done in compile time.

Well, I'm not focusing on the "infer" aspects, but in general that is an ongoing discussion I'm having with the compiler team.

Am I missing something?

I think so, yes; in particular, you should not be using "infer by name" options on models that are ever going to change. That option was added as a pragmatic way to just make things work on existing fixed models, but it is very brittle - and in many ways dangerous. There should be warnings that appear in your intellisense about this, but frankly the recommended option is: always be explicit. Add [ProtoMember(42)] (or whatever) to each property. Then there's no guessing, and no risk of adding new members that break things. You can see everything, and you can understand everything.