Search code examples
c#.netserializationprotobuf-netbackwards-compatibility

Does protobuf-net allow backward compability when a type has moved assemblies or namespaces?


I'm currently having a hard time moving DTOs from one assembly and namespace to another assembly and namespace. This is because I am using binary serialization to send/receive data. In binary serialization the contract is basically the assembly name and type namespace + name. If you move a type to a different assembly you essentially break the contract.

Does protobuf-net behave in the same way? What is the foundation of the protobuf-net contract? Does probuf-net allow me to move a type from one assembly to another without breaking backwards compatibility? What about moving namespaces?


Solution

  • The short version of this is "yes, that will be fine - and to work just like that is one of the main reasons I wrote it in the first place".

    In binary serialization the contract is basically the assembly name and type namespace + name.

    That depends on how you define "binary serialization"; if you mean "serialization to something that happens to be binary, i.e. non-text", then that is unrelated. If you mean "type-based serialization", then: indeed - this is a major pain point.

    Does protobuf-net behave in the same way?

    No, it does not.

    What is the foundation of the protobuf-net contract?

    Types are not described at all; the caller supplies the type of the outermost member, and all other types are implied by the type's layout.

    Members are identified solely via numeric keys; if those keys exist on both sides (and they don't necessarily need to : you can add / remove members), then they must be compatible. For example, these are compatible:

    [ProtoContract]
    public class Foo { // in namespace X
        [ProtoMember(1)]
        public int Id {get;set;}
        [ProtoMember(2)]
        public string Name {get;set;}
    }
    ...
    [ProtoContract]
    public class User { // in namespace Y
        [ProtoMember(2)]
        public string UserName {get;set;}
    }
    

    If you serialize a Foo and then deserialize it as a User, the data from field 2 is pushed into UserName (the data from field 1 is ignored by default, but can be stored if you need that). This, however, is not with Foo:

    [ProtoContract]
    public class BadClass {
        [ProtoMember(2)]
        public double Quantity {get;set;}
    }
    

    (this will fail, as the wire-types for string and double are not remotely comparable)

    Does probuf-net allow me to move a type from one assembly to another without breaking backwards compatibility? What about moving namespaces?

    Yes to both. When you consider that you can be serializing in C# and deserializing in a Java or C++ model, it has to work this way.