Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

protobuf-net's [ProtoInclude(1, "MyClass")] did not work

I am using protobuf-net v2 beta r431 for C# .net 4.0 application. In my application I have a Dictionary<int, IMyClass> which i need to serialize. A class MyClass implements IMyClass interface. As per protobuf's documentations I wrote the code as under:

[ProtoContract]
[ProtoInclude(1, typeof(MyClass))]
public interface IMyClass
{
    int GetId();
    string GetName();
}

[ProtoContract]
[Serializable]
public class MyClass : IMyClass
{
    [ProtoMember(1)]
    private int m_id = 0;

    [ProtoMember(2)]
    private string m_name = string.Empty;

    public MyClass(int id, string name)
    {
        m_id = id;
        m_name = name;
    }

    public MyClass()
    {
    }

    #region IMyClass Members

    public int GetId()
    {
        return m_id;
    }

    public string GetName()
    {
        return m_name;
    }

    #endregion
}

However, as per my application's design, the interface is defined at a higher level (in a different project than the classes) and it is not possible to determine the class/classes that implements this interface at compile time. Hence, it gives compile time error for [ProtoInclude(1, typeof(MyClass))]. I tried to use [ProtoInclude(int tag, string KownTypeName)] as following:

[ProtoContract]
[ProtoInclude(1, "MyClass")]
public interface IMyClass
{
    int GetId();
    string GetName();
}

But, this threw an "Object reference not set to instance of an object" exception at line

Serializer.Serialize(stream, myDict);

where Dictionary myDict = new Dictionary(int, IMyClass)(); Please let me know how to use ProtoInclude in this case so as to serialize interface type inside dictionary / list.

like image 977
user386527 Avatar asked Sep 28 '11 15:09

user386527


2 Answers

Since it will have no idea where to get your MyClass, you should probably use the Type.AssemblyQualifiedName value for your class.

Here's some example code:

namespace Alpha
{
    [ProtoContract]
    [ProtoInclude(1, "Bravo.Implementation, BravoAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")]
    //[ProtoInclude(1, "Bravo.Implementation")] // this likely only works because they're in the same file
    public class PublicInterface
    {            
    }
}

namespace  Bravo
{
    public class Implementation : Alpha.PublicInterface
    {            
    }

    public class Tests
    {
        [Test]
        public void X()
        {
            // no real tests; just testing that it runs without exceptions

            Console.WriteLine(typeof(Implementation).AssemblyQualifiedName);

            using (var stream = new MemoryStream())
            {
                Serializer.Serialize(stream, new Implementation());
            }
        }
    }
}
like image 55
Austin Salonen Avatar answered Nov 14 '22 22:11

Austin Salonen


Austin is correct (I believe): using an assembly-qualified name (as a string) should resolve this.

In v2, an additional option exists: you can perform the mapping at runtime instead of via attributes:

RuntimeTypeModel.Default[typeof(PulicInterface)]
    .AddSubType(1, typeof(Implementation));

This could be be through static code if your "app" tier knows about both types, or could be done via some custom configuration / reflection process.

like image 45
Marc Gravell Avatar answered Nov 14 '22 21:11

Marc Gravell