Универсальное наследование Protobuf-net и замкнутый построенный универсальный тип - PullRequest
3 голосов
/ 28 июня 2011

У меня довольно сложная иерархия наследования, включая дженерики, и мы пытаемся использовать protobuf .net для целей сериализации. К сожалению, похоже, что он не может правильно разобраться с этим делом. Так выглядит иерархия.

    [System.Runtime.Serialization.DataContract]
    [ProtoBuf.ProtoInclude(1000, typeof(GenericBaseClass<object>))]
    [ProtoBuf.ProtoInclude(1001, typeof(GenericBaseClass<string>))]
    public abstract class BaseClass
    {

        public int BaseProperty1 { set; get; }
        public int BaseProperty2 { set; get; }

        public BaseClass()
        {

        }

    }

    [System.Runtime.Serialization.DataContract]
    [ProtoBuf.ProtoInclude(1002, typeof(GenericDerivedClass<object>))]
    [ProtoBuf.ProtoInclude(1003, typeof(GenericDerivedClass<string>))]
    public abstract class GenericBaseClass<T> : BaseClass
    {
        /// <summary>
        /// 
        /// </summary>
        [System.Runtime.Serialization.DataMember(Order = 5)]
        public T ResponseProperty
        {
            get;
            set;
        }

        public GenericBaseClass()
        {
        }
    }

    [System.Runtime.Serialization.DataContract]
    [ProtoBuf.ProtoInclude(1004, typeof(DerivedClass1))]
    [ProtoBuf.ProtoInclude(1005, typeof(DerivedClass2))]
    public abstract class GenericDerivedClass<T> : GenericBaseClass<T>
    {
        public int AdditionalProperty { get; set; }

        public GenericDerivedClass()
        {

        }
    }

Наконец, эти классы реализуются двумя закрытыми построенными неуниверсальными классами

    [System.Runtime.Serialization.DataContract]
    public class DerivedClass1 : GenericDerivedClass<string>             
    {
        [System.Runtime.Serialization.DataMember(Order = 6)]
        public int DerivedClass1Property { set; get; }
    }

    [System.Runtime.Serialization.DataContract]
    public class DerivedClass2 : GenericDerivedClass<object>
    {
        [System.Runtime.Serialization.DataMember(Order = 7)]
        public int DerivedClass2Property { set; get; }
    }

Я написал следующий метод тестирования для их сериализации, и он дает мне ошибку.

    [TestMethod]
    public void SerializeDeserializeAndCompare()
    {            

        DerivedClass2 i = new DerivedClass2() { BaseProperty1 = 1, BaseProperty2 = 2, DerivedClass2Property = 3, ResponseProperty = new Object() };
        using (var file = System.IO.File.Create("test.bin"))
        {
            ProtoBuf.Serializer.Serialize(file, i);
        }

        using (var file = System.IO.File.OpenRead("test.bin"))
        {
            var o = ProtoBuf.Serializer.Deserialize<DerivedClass2>(file);
        }
    }

Я получаю ошибку

ProtoBuf.ProtoException: тип может участвовать только в одной иерархии наследования (CapitalIQ.DataGet.UnitTests.DataSetUnitTest + DerivedClass2) ---> System.InvalidOperationException: тип может участвовать только в одной иерархии наследования

Это ограничение protobuf .net или я делаю что-то неправильно? Я использую версию r282.

Спасибо Shobhit

1 Ответ

4 голосов
/ 29 июня 2011

Как и для всех атрибутов, информация о включенных типах атрибутов применяется ко всем закрытым типам из определения универсального типа.Таким образом, то, что вы на самом деле определили (для protobuf-net):

BaseClass
: GenericBaseClass<object>
 : GenericDerivedClass<object>
  : DerivedClass1
  : DerivedClass2
 : GenericDerivedClass<string>
  : DerivedClass1
  : DerivedClass2
: GenericBaseClass<string>
 : GenericDerivedClass<object>
  : DerivedClass1
  : DerivedClass2
 : GenericDerivedClass<string>
  : DerivedClass1
  : DerivedClass2

Как видите, существует множество дубликатов, что явно сбивает с толку.Так как аргументы атрибута не могут использовать параметры типа, это оставляет возможность добавить какой-то нечетный механизм предикатов, что очень запутанно в IMO.ИМО, было бы лучше смоделировать это вручную (удалив атрибуты ProtoInclude).Я подозреваю Ваша предполагаемая модель:

BaseClass
: GenericBaseClass<object>
 : GenericDerivedClass<object>
  : DerivedClass2
: GenericBaseClass<string>
 : GenericDerivedClass<string>
  : DerivedClass1

protobuf-net может работать с этим, но для объяснения модель требует "v2" и RuntimeTypeModel:

Обратите также внимание, что object является проблемой для protobuf;protobuf-net может подделать его с помощью опции динамического типа, но это ... не идеально.Конечно, он не может сериализовать object, поэтому для теста я подставил строку.Также обратите внимание, что BaseProperty1, BaseProperty2 и AdditionalProperty в настоящее время не помечены для сериализации, но могут быть тривиальными.

В любом случае:

RuntimeTypeModel.Default[typeof(BaseClass)]
    .AddSubType(10, typeof(GenericBaseClass<object>))
    .AddSubType(11, typeof(GenericBaseClass<string>));

RuntimeTypeModel.Default[typeof(GenericBaseClass<object>)]
    .AddSubType(10, typeof(GenericDerivedClass<object>));
RuntimeTypeModel.Default[typeof(GenericBaseClass<object>)][5].DynamicType = true; // object!
RuntimeTypeModel.Default[typeof(GenericDerivedClass<object>)]
    .AddSubType(10, typeof(DerivedClass2));

RuntimeTypeModel.Default[typeof(GenericBaseClass<string>)]
    .AddSubType(10, typeof(GenericDerivedClass<string>));
RuntimeTypeModel.Default[typeof(GenericDerivedClass<string>)]
    .AddSubType(10, typeof(DerivedClass1));

DerivedClass2 i = new DerivedClass2() { BaseProperty1 = 1, BaseProperty2 = 2, DerivedClass2Property = 3, ResponseProperty = "some string" };
using (var file = System.IO.File.Create("test.bin"))
{
    ProtoBuf.Serializer.Serialize(file, i);
}

using (var file = System.IO.File.OpenRead("test.bin"))
{
    var o = ProtoBuf.Serializer.Deserialize<DerivedClass2>(file);
}

Вы не иметь для использования RuntimeTypeModel.Default - на самом деле, я рекомендую использовать (и кэшировать) отдельную модель типов;но Serializer.Serialize указывает на модель по умолчанию.Если вы создаете пользовательскую модель (TypeModel.Create), просто сохраните ее где-нибудь и используйте оттуда Serialize и т. Д.

...