protobuf-net WCF Сериализация нескольких вложенных универсальных абстрактных объектов v282 - PullRequest
2 голосов
/ 01 июля 2011

это мой первый пост здесь, поэтому, пожалуйста, потерпите меня ...

Я пытаюсь вложить несколько объектов Generic, а затем передать их через WCF с помощью ProtoBuf-net. Есть много (более 10) основных объектов, которые я реализовал, но мой код будет отображать только 2. Все они имеют схожие структуры, но есть несколько, которые используют только один или два универсальных объекта (отсюда их структура наследования)

После проработки всех тегов и ProtoInclude я смог получить один основной объект для сериализации. Когда я начал работать над следующим объектом, я получил сообщение об ошибке:

Known-type mainBase`2 for ProtoIncludeAttribute must be a direct subclass of mainBase`1

После того, как я несколько часов ломал голову (и читал здесь), я впал в отчаяние и начал пробовать что-то случайное. Когда я удалил ProtoInclude для исходного основного объекта и имел только их для второго, он работал нормально!

В приведенном ниже коде все теги все еще реализованы, так что вы можете получить исключение, однако, если вы закомментируете либо aMain, либо bMain во всех 4 классах mainBase, программа сможет для сериализации того, кто в данный момент помечен.

(заранее извиняюсь, код большой, но я не нашел такой сложной проблемы, как моя)

class Program
{
    static void Main(string[] args)
    {
        var vcc = new aMain();
        var vccStream = new MemoryStream();
        ProtoBuf.Serializer.Serialize(vccStream, vcc);
        vccStream.Position = 0;
        var newvcc = ProtoBuf.Serializer.Deserialize<aMain>(vccStream);


        var vtc = new bMain();
        var vtcStream = new MemoryStream();
        ProtoBuf.Serializer.Serialize(vtcStream, vtc);
        vtcStream.Position = 0;
        var newvtc = ProtoBuf.Serializer.Deserialize<bMain>(vtcStream);
    }
}

#region Problem Objects, 'Main Objects' Base

[DataContract, ProtoContract, Serializable]
[ProtoInclude(2, typeof(aMain))]
[ProtoInclude(3, typeof(bMain))]
public abstract class mainBase<TbbBase, TaBase, TcbBase>
    : mainBase<TbbBase, TaBase>
    where TcbBase : cbBase
    where TbbBase : bbBase
    where TaBase : aBase
{
    [DataMember, ProtoMember(1)]
    public TcbBase Value3 { get; set; }

    protected mainBase()
    {
        Value3 = Activator.CreateInstance(typeof(TcbBase)) as TcbBase;
    }
}

[DataContract, ProtoContract, Serializable]
[ProtoInclude(2, typeof(mainBase<aMainSub_bbBase, aMainSub_aBase, aMainSub_cbBase>))]
[ProtoInclude(3, typeof(mainBase<bMainSub_bbBase, bMainSub_aBase, bMainSub_cbBase>))]
public abstract class mainBase<TbbBase, TaBase>
    : mainBase<TbbBase>
    where TbbBase : bbBase
    where TaBase : aBase
{
    [DataMember, ProtoMember(1)]
    public TaBase Value2 { get; set; }

    protected mainBase()
    {
        Value2 = Activator.CreateInstance(typeof(TaBase)) as TaBase;
    }
}

[DataContract, ProtoContract, Serializable]
[ProtoInclude(2, typeof(mainBase<aMainSub_bbBase, aMainSub_aBase>))]
[ProtoInclude(3, typeof(mainBase<bMainSub_bbBase, bMainSub_aBase>))]
public abstract class mainBase<TbbBase> : mainBase
    where TbbBase : bbBase
{
    [DataMember, ProtoMember(1)]
    public TbbBase Value1 { get; set; }

    protected mainBase()
    {
        Value1 = Activator.CreateInstance(typeof(TbbBase)) as TbbBase;
    }
}

[DataContract, ProtoContract, Serializable]
[ProtoInclude(1, typeof(mainBase<aMainSub_bbBase>))]
[ProtoInclude(2, typeof(mainBase<bMainSub_bbBase>))]
public abstract class mainBase
{
    public abstract string MyDefaultNameSpace { get; }
}

#endregion

#region Main Objects

[DataContract, ProtoContract, Serializable]
public class aMain : mainBase<aMainSub_bbBase, aMainSub_aBase, aMainSub_cbBase>
{
    public override string MyDefaultNameSpace { get { return "VideoChunker"; } }
}

[DataContract, ProtoContract, Serializable]
public class aMainSub_bbBase : bbbbBase { }

[DataContract, ProtoContract, Serializable]
public class aMainSub_aBase : aaBase { }

[DataContract, ProtoContract, Serializable]
public class aMainSub_cbBase : cbBase { }


[DataContract, ProtoContract, Serializable]
public class bMain : mainBase<bMainSub_bbBase, bMainSub_aBase, bMainSub_cbBase>
{
    public override string MyDefaultNameSpace { get { return "VideoTranscoder"; } }
}

[DataContract, ProtoContract, Serializable]
public class bMainSub_bbBase : bbbbBase { }

[DataContract, ProtoContract, Serializable]
public class bMainSub_aBase : aaBase { }

[DataContract, ProtoContract, Serializable]
public class bMainSub_cbBase : cbBase { }

#endregion

#region Base Objects

[DataContract, ProtoContract, Serializable]
[ProtoInclude(2, typeof(aMainSub_bbBase))]
[ProtoInclude(3, typeof(bMainSub_bbBase))]
public abstract class bbbbBase : bbbBase { }

[DataContract, ProtoContract, Serializable]
[ProtoInclude(1, typeof(bbbbBase))]
public abstract class bbbBase : bbBase { }

[DataContract, ProtoContract, Serializable]
[ProtoInclude(1, typeof(bbbBase))]
public abstract class bbBase : bBase { public override string GetConfigNamespace { get { return ".Service"; } } }

[DataContract, ProtoContract, Serializable]
[ProtoInclude(1, typeof(bbBase))]
[ProtoInclude(2, typeof(cbBase))]
public abstract class bBase : subBase { }

[DataContract, ProtoContract, Serializable]
[ProtoInclude(1, typeof(aMainSub_cbBase))]
[ProtoInclude(2, typeof(bMainSub_cbBase))]
public class cbBase : bBase { public override string GetConfigNamespace { get { return ".Fabric"; } } }

[DataContract, ProtoContract, Serializable]
[ProtoInclude(1, typeof(bBase))]
[ProtoInclude(4, typeof(aBase))]
public abstract class subBase { public virtual string GetConfigNamespace { get { return string.Empty; } } }

[DataContract, ProtoContract, Serializable]
[ProtoInclude(2, typeof(aMainSub_aBase))]
[ProtoInclude(3, typeof(bMainSub_aBase))]
public abstract class aaBase : aBase { }

[DataContract, ProtoContract, Serializable]
[ProtoInclude(1, typeof(aaBase))]
public abstract class aBase : subBase { public override string GetConfigNamespace { get { return ".Action"; } } }

#endregion

Поскольку я использую старую версию protobuf, я решил найти ее исходный код и посмотреть, смогу ли я что-нибудь выяснить. После небольшой отладки я обнаружил, где выбрасывается исключение, и я просто делаю continue; вместо того, чтобы выдавать исключение.
В файле SerializerT.cs строка 246 выглядит следующим образом:

foreach (ProtoIncludeAttribute pia in Attribute.GetCustomAttributes(typeof(T), typeof(ProtoIncludeAttribute), false))
            {
                Type subclassType = pia.ResolveKnownType(typeof(T).Assembly);
                if (subclassType == null)
                {
                    throw new ProtoException("Unable to identify known-type for ProtoIncludeAttribute: " + pia.KnownTypeName);
                }
                if (subclassType.BaseType != typeof(T))
                {
                    continue;
                    throw new ProtoException(string.Format(
                        "Known-type {0} for ProtoIncludeAttribute must be a direct subclass of {1}",
                        subclassType.Name, typeof(T).Name));
                }
                Property<T, T> prop;
                switch (pia.DataFormat)
                {
                    case DataFormat.Default:
                        prop = (Property<T, T>) PropertyUtil<T>.CreateTypedProperty("CreatePropertyMessageString", typeof(T), typeof(T), subclassType);
                        break;
                    case DataFormat.Group:
                        prop = (Property<T, T>)PropertyUtil<T>.CreateTypedProperty("CreatePropertyMessageGroup", typeof(T), typeof(T), subclassType);
                        break;
                    default:
                        throw new ProtoException("Invalid ProtoIncludeAttribute data-format: " + pia.DataFormat);
                }
                // check for duplicates
                if (tagsInUse.Contains(pia.Tag))
                {
                    throw new InvalidOperationException(
                        string.Format("Duplicate tag {0} detected in sub-type {1}", pia.Tag, subclassType.Name));
                }
                tagsInUse.Add(pia.Tag);
                prop.Init(pia.Tag, pia.DataFormat, PropertyFactory.GetPassThru<T>(), null, true, null);
                subclassList.Add(new KeyValuePair<Type, Property<T, T>>(subclassType, prop));
            }

Вы можете видеть, где мой continue' прямо над оригиналом throw. Я не совсем уверен, каковы последствия этого действия; это настоящая ошибка или я открываюсь перед каким-то катастрофическим сумасшествием?

Спасибо за ваше время.

1 Ответ

0 голосов
/ 01 июля 2011

Хорошо, я думаю, что теперь понимаю модель (очень рад, что прошлой ночью не выглядел слишком усердно; p) - у вас есть:

aMain
 : mainBase<aMainSub_bbBase, aMainSub_aBase, aMainSub_cbBase>
  : mainBase<aMainSub_bbBase, aMainSub_aBase>
   : mainBase<aMainSub_bbBase> : mainBase

bMain
 : mainBase<bMainSub_bbBase, bMainSub_aBase, bMainSub_cbBase>
  : mainBase<bMainSub_bbBase, bMainSub_aBase>
   : mainBase<bMainSub_bbBase> : mainBase

aMainSub_bbBase, bMainSub_bbBase
 : bbbbBase : bbbBase : bbBase : bBase : subBase
aMainSub_aBase, bMainSub_aBase
 : aaBase : aBase : subBase
aMainSub_cbBase, bMainSub_cbBase
 : cbBase : bBase : subBase

Эта проблема на самом деле аналогична этому вопросу и относится к атрибутам , как правило, , в том смысле, что атрибуты применяются к всем закрытым типам, а не только к тот, о котором ты думал. В частности, вы в настоящее время говорите, что aMain относится к и mainBase<aMainSub_bbBase, aMainSub_aBase, aMainSub_cbBase> и mainBase<bMainSub_bbBase, bMainSub_aBase, bMainSub_cbBase> (и то же самое для всех кроссоверов) .

С двумя похожими вопросами за короткий промежуток времени я попытаюсь взглянуть на это, но в краткосрочной перспективе я думаю, что модельер v2 - это способ исправить это; Я удалил атрибуты ProtoInclude из 3 универсальных типов (mainBase<>, mainBase<,> и mainBase<,,>), затем:

var model = RuntimeTypeModel.Default;

model[typeof(mainBase<aMainSub_bbBase>)].AddSubType(2, typeof(mainBase<aMainSub_bbBase, aMainSub_aBase>));
model[typeof(mainBase<bMainSub_bbBase>)].AddSubType(2, typeof(mainBase<bMainSub_bbBase, bMainSub_aBase>));

model[typeof(mainBase<aMainSub_bbBase, aMainSub_aBase>)].AddSubType(2, typeof(mainBase<aMainSub_bbBase, aMainSub_aBase, aMainSub_cbBase>));
model[typeof(mainBase<bMainSub_bbBase, bMainSub_aBase>)].AddSubType(2, typeof(mainBase<bMainSub_bbBase, bMainSub_aBase, bMainSub_cbBase>));

model[typeof(mainBase<aMainSub_bbBase, aMainSub_aBase, aMainSub_cbBase>)].AddSubType(2, typeof(aMain));
model[typeof(mainBase<bMainSub_bbBase, bMainSub_aBase, bMainSub_cbBase>)].AddSubType(2, typeof(bMain));

(обычные атрибуты обрабатывают большинство случаев)

Обратите внимание, как небольшая «особенность», что, поскольку мы находимся в параллельных ветвях, вам не нужны отдельные теги 2 / 3, поскольку это не то, что вы ожидаете или или aMain или bMain из mainBase<aMainSub_bbBase, aMainSub_aBase, aMainSub_cbBase> - возможен только aMain.

Мне придется изучить варианты, как сделать этот очиститель - но, по крайней мере, в v2 он может работать!

Повторное удаление существующего исключения; Я, честно говоря, не верю, что код v1 обладает тонкостью для правильной обработки этого сценария. Удаление этого исключения может привести к его сбою другими интересными способами, в частности, когда он пытается работать по иерархии типов. Во что бы то ни стало внесите любые изменения, которые вы выберете для своей локальной копии, но: на свой страх и риск - я не могу сказать «да, это безопасно», так как я не верю, что это так. Здесь я рекомендую использовать более сложное моделирование в v2, в то время как я исследую способы обработки этого типа параллельной универсальной модели. Любые изменения здесь будут применяться только к v2, так как v1 просто не имеет нормального способа хранения этой информации, без существенного введения моделировщика типов v2, то есть превращения v1 в v2.

...