sharafi
sharafi

Reputation: 531

C# Serialize inherited class with proto-buf without use ProtoInclude

I have project that user can develop plugin for it. In their plugins, they must extend a abstract base class. In core of my project, load plugins (dlls) dynamically and new these classes.

base class in core of code:

[ProtoContract]
public abstract class BaseOutput
{
    public string OutputName {get;}
    [ProtoMember(1)]
    public int X { get; set; }
}

sample of plugin (in other project):

[ProtoContract]
public class MyOutput : BaseOutput
{
    public override OutputName { get { return "MyOutput";} }
    [ProtoMember(11)]
    public double A { get; set; }
    [ProtoMember(12)]
    public double B { get; set; }
    [ProtoMember(13)]
    public double C { get; set; }
}

I know that over the BaseOutput I must add [ProtoInclude(10, typeof(MyOutput ))] but when I develop core of my application don't know the plugins that user will be add to program. But I want can to serialize all classes extend the BaseOutput with protobuf.

What is the solution?

Upvotes: 2

Views: 543

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062502

It is possible to configure this type of relationship at runtime with protobuf-net via the RuntimeTypeModel API, as per the example shown below. However: it is vitally important that the key used (42 in the example) is reliable and deterministic every time your app runs. Meaning: you would need some way of reliably getting 42 for your specific plugin type every time, regardless of whether somebody adds/removes other plugins (which means: just ordering them alphabetically may not be enough). Also: the 42 must be unique among the different plugins that are loaded.


using ProtoBuf;
using ProtoBuf.Meta;

[ProtoContract]
public abstract class BaseOutput
{
    public abstract string OutputName { get; }
    [ProtoMember(1)]
    public int X { get; set; }
}

[ProtoContract]
public class MyOutput : BaseOutput
{
    public override string OutputName { get { return "MyOutput"; } }
    [ProtoMember(11)]
    public double A { get; set; }
    [ProtoMember(12)]
    public double B { get; set; }
    [ProtoMember(13)]
    public double C { get; set; }

    public override string ToString()
        => $"A={A}, B={B}, C={C}"; // to show working
}

class Program
{
    static void Main()
    {
        var pluginType = typeof(MyOutput); // after loading the plugin etc
        var baseType = RuntimeTypeModel.Default[typeof(BaseOutput)];
        baseType.AddSubType(42, pluginType);

        BaseOutput obj = new MyOutput { A = 1, B = 2, C = 3 };
        var clone = Serializer.DeepClone(obj);

        // outputs: A=1, B=2, C=3 - so: working (yay!)
        System.Console.WriteLine(clone.ToString());
    }
}

Upvotes: 4

Related Questions