Workaround for Protobuf-NET IExtensible Inheritance Not Supported

1

I am deserializing protocol buffer data using Protobuf-NET and am dealing with multiple Proto files that are identical but have slightly different extensions. Basically each Proto feed is 99% identical and then has 1 or 2 fields different from each other provided as extensions for each. So I ended up with multiple Proto classes that are 99% identical.

Logically I want to add inheritance to generated C# proto files in order to avoid 99% of redundant code parsing each feed. The problem is that I am getting the following error upon deserialization:

System.NotSupportedException
  HResult=0x80131515
  Message=IExtensible is not supported in structs or classes with inheritance
  Source=protobuf-net
  StackTrace:
   at ProtoBuf.Serializers.TypeSerializer..ctor(TypeModel model, Type forType, Int32[] fieldNumbers, IProtoSerializer[] serializers, MethodInfo[] baseCtorCallbacks, Boolean isRootType, Boolean useConstructor, CallbackSet callbacks, Type constructType, MethodInfo factory) in C:\code\protobuf-net\src\protobuf-net\Serializers\TypeSerializer.cs:line 97
   at ProtoBuf.Meta.MetaType.BuildSerializer() in C:\code\protobuf-net\src\protobuf-net\Meta\MetaType.cs:line 480
   at ProtoBuf.Meta.MetaType.get_Serializer() in C:\code\protobuf-net\src\protobuf-net\Meta\MetaType.cs:line 372
   at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source) in C:\code\protobuf-net\src\protobuf-net\Meta\RuntimeTypeModel.cs:line 802
   at ProtoBuf.Meta.TypeModel.DeserializeCore(ProtoReader reader, Type type, Object value, Boolean noAutoCreate) in C:\code\protobuf-net\src\protobuf-net\Meta\TypeModel.cs:line 718
   at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type, SerializationContext context) in C:\code\protobuf-net\src\protobuf-net\Meta\TypeModel.cs:line 590
   at ProtoBuf.Serializer.Deserialize[T](Stream source) in C:\code\protobuf-net\src\protobuf-net\Serializer.cs:line 68

So it would seem that this scenario is not supported. Now what's not clear is if this applies to IExtensible usage in the Base class or the derived class. Base class must remain extensible since I'm applying multiple extensions, but the extension / derived classes do not.

I have code similar to the one below taken from this post. Now before anyone labels it as "Duplicate" - it is not because my question is completely different. While it's OP is asking about the purpose of IExtensible and consequences of not implementing it, I am asking about any possible workaround or solution for the above described use case.

[ProtoBuf.ProtoInclude(1000, typeof(DerivedClass))]
public partial class BaseClass : ProtoBuf.IExtensible 
{ 
    ... 
    private IExtension extensionObject;
    IExtension IExtensible.GetExtensionObject(bool createIfMissing)
    {
        return Extensible.GetExtensionObject(ref extensionObject, createIfMissing);        
    }
}    

public partial class DerivedClass : BaseClass, ProtoBuf.IExtensible
{
    ... 
    private IExtension extensionObject;
    IExtension IExtensible.GetExtensionObject(bool createIfMissing)
    {
        return Extensible.GetExtensionObject(ref extensionObject, createIfMissing);        
    }
}   

var baseObject = new MyClass { ... };    
DerivedClass derivedObject;
using (var stream = new MemoryStream())
{
    Serializer.Serialize(stream, baseObject);  // throws runtime exception
    stream.Seek(0, SeekOrigin.Begin);
    derivedObject = Serializer.Deserialize<DerivedClass>(stream);
}

Question: how can I achieve inheritance with above described scenario? I am open to modifying the derived class, any other work around you could suggest, or even using another .NET Protobuf library.

c#
inheritance
protocol-buffers
protobuf-net
asked on Stack Overflow Apr 23, 2020 by AlexVPerl

1 Answer

1

The fundamental problem here is that there is a collision:

  • protobuf-net implements inheritance in protobuf (which does not have inheritance) by modelling it as multiple messages (one per type) and a oneof at each decision point
  • in protobuf, each message could have extensions
  • but right now, the implementation of extensions in protobuf-net is per object
  • so it would be unclear to which message you want to apply the extension

I have thought about extending the API to make it track the extensions against the logical type (which would map to the message), but this is complex and would take me time to add.

Most other protobuf tools do not support inheritance at all, so I doubt you'd find that a viable workaround.

So: that's why it isn't there; that doesn't really.givr you a workaround, sorry. The only "fix" here is "take the time to implement the complexity of per-type extension storage and retrieval"

answered on Stack Overflow Apr 23, 2020 by Marc Gravell

User contributions licensed under CC BY-SA 3.0