【问题标题】:protobuf-net WCF Multiple Nested Generic Abstract Objects Serialization v282protobuf-net WCF 多嵌套通用抽象对象序列化 v282
【发布时间】:2011-06-30 22:14:27
【问题描述】:

这是我在这里的第一篇文章,所以请多多包涵……

我正在尝试嵌套多个通用对象,然后使用 ProtoBuf-net 将它们传递给 WCF。我已经实现了许多(10+)个主要对象,但我列出的代码只会显示 2 个。它们都有相似的结构,但是有一些只使用一两个泛型(因此它们的继承结构)

在处理完所有标签和 ProtoIncludes 之后,我已经能够获得一个要序列化的主要对象。当我开始处理下一个对象时,我收到了错误:

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

绞尽脑汁几个小时(并在此处阅读)后,我感到绝望并开始尝试一些随机的东西。当我删除原始主要对象的 ProtoInclude 并只将它们用于第二个时,它工作正常!

在下面的代码中,我仍然实现了所有标签,因此您可以获得异常,但是如果您在所有 4 个 mainBase 类中注释掉 aMainbMain,程序将能够序列化当前标记的任何一个。

(提前致歉,代码很大,但我还没有发现像我这样复杂的问题)

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 的正上方。我不确定这个行动的后果是什么;这是一个真正的错误还是我让自己面临一些灾难性的疯狂?

感谢您的宝贵时间。

【问题讨论】:

  • 有趣 - 这里是午夜,但我会在早上看看这个
  • 对于信息,在 v2 中失败并显示“一个类型只能参与一个继承层次结构 (aMain)” - 我仍在调查

标签: .net wcf protobuf-net


【解决方案1】:

好的,我想我现在理解了这个模型(很高兴我昨晚没有看得太难;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

这实际上是与this question 类似的问题,并且与属性一般有关,因为这些属性适用于所有封闭类型,而不仅仅是你的那个想着。特别是,您目前告诉它 aMainboth mainBase&lt;aMainSub_bbBase, aMainSub_aBase, aMainSub_cbBase&gt; and mainBase&lt;bMainSub_bbBase, bMainSub_aBase, bMainSub_cbBase&gt; 相关(对于所有那里有交叉)。

在短时间内有两个类似的问题,我会尝试看看这个,但短期内我认为 v2 建模器是解决这个问题的方法;我已经从 3 个泛型类型(mainBase&lt;&gt;mainBase&lt;,&gt;mainBase&lt;,,&gt;)中删除了 ProtoInclude 属性,然后:

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));

(常规属性处理大多数情况)

请注意,由于我们在 parallel 分支中,因此您不需要单独的 2 / 3 标签,因为这不是您所期望的任一aMainbMain 来自mainBase&lt;aMainSub_bbBase, aMainSub_aBase, aMainSub_cbBase&gt; - 只有aMain 是可能的。

我将不得不研究使这个更清洁的选项 - 但至少在 v2 中它可以工作!

重新删除现有异常;老实说,我不相信 v1 代码具有正确处理这种情况的微妙之处。删除该异常可能会导致它以其他有趣的方式失败,特别是当它试图向下处理类型层次结构时。无论如何都要对您的本地副本进行任何更改,但是:风险自负 - 我不能说“是的,那是安全的”,因为我不相信它是。我的建议是使用 v2 中更复杂的建模,同时我研究处理这种类型的并行通用模型的方法。此处的任何更改都将仅适用于 v2,因为 v1 根本没有理智的方式来存储此信息,而无需本质上引入 v2 类型建模器,即将 v1 变为 v2。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-09
    • 1970-01-01
    • 2023-03-08
    • 2020-01-26
    相关资源
    最近更新 更多