我们正在尝试使用protobuf序列化,以下模型:
[ProtoContract]
private class Container
{
[ProtoMember(2)]
//This does not make much sense in this example, but this is the easiest representation of our biggest model
public ItemCollection<ISomeOtherInterface> ContainedObjects { get; set; }
public Container()
{
ContainedObjects= new ItemCollection<ISomeOtherInterface>();
}
}
[ProtoContract(AsReferenceDefault = true)]
public interface IAnyConfigurationNode
{
int Id { get; set; }
String Name { get; set; }
}
[ProtoContract(AsReferenceDefault = true)]
private interface ISomeOtherInterface : IAnyConfigurationNode
{
int Order { get; set; }
}
[ProtoContract(AsReferenceDefault = true)]
private abstract class SomeBaseType : IAnyConfigurationNode
{
[ProtoMember(1)]
public String Name { get; set; }
[ProtoMember(3)]
public int Id { get; set; }
}
[ProtoContract(AsReferenceDefault = true)]
private class SomeType : SomeBaseType, ISomeOtherInterface
{
[ProtoMember(2)]
public int Order { get; set; }
}
由于我们引用(在这种情况下它确实很有意义,但我们有一些商业案例,我们必须)SomeType
虽然另一个界面不在IAnyConfigurationNode
- &gt; { {1}} - &gt; SomeBaseType
树(我们不能说SomeType
扩展SomeType
和SomeBaseType
,这将是ProtoBuf中的多种类型,我们有制作了一个代理,基本上将对象保存为ISomeOtherInterface
而不是“IAnyConfigurationNode
:
ISomeOtherInterface
容器中的有趣部分:
[ProtoContract]
private class ProtoSurrogate<T> where T: class, IAnyConfigurationNode
{
[ProtoMember(1)]
private IAnyConfigurationNode ContainedObject { get; set; }
[ProtoConverter]
public static ProtoSurrogate<T> To(T toBeSurogated)
{
return toBeSurogated == null ? null : new ProtoSurrogate<T>() { ContainedObject = toBeSurogated};
}
[ProtoConverter]
public static T From(ProtoSurrogate<T> surrogate)
{
return surrogate?.ContainedObject as T;
}
}
是一个自定义条件(在我们的实际情况中,使用此集合的类提供一些委托来进行一些回调操作(比如将自己设置为其列表中新添加项的父级),如下所示:
ItemCollection
我们有这样指定的遗产/代理:
[ProtoContract(AsReferenceDefault = true)]
public class ItemCollection<T> : ICollection<T>
{
[ProtoMember(1)]//Only thing that is serialized here
private readonly List<T> m_items;
public int Count => m_items.Count;
public ItemCollection(){m_items = new List<T>();}
public void Add(T item){m_items.Add(item);}
public bool Contains(T item){return m_items.Contains(item);}
public void Clear(){m_items.Clear();}
public void Sort(){m_items.Sort();}
IEnumerator<T> IEnumerable<T>.GetEnumerator(){return m_items.GetEnumerator();}
IEnumerator IEnumerable.GetEnumerator(){return m_items.GetEnumerator();}
void ICollection<T>.CopyTo(T[] array, int arrayIndex){m_items.CopyTo(array, arrayIndex);}
bool ICollection<T>.IsReadOnly => false;
bool ICollection<T>.Remove(T item){return m_items.Remove(item);}
}
然后在最后,我只有一个执行以下操作的UnitTest:
RuntimeTypeModel.Default.Add(typeof(ISomeOtherInterface), false).SetSurrogate(typeof(ProtoSurrogate<ISomeOtherInterface>));
RuntimeTypeModel.Default[typeof(IAnyConfigurationNode)].AddSubType(1000, typeof(SomeBaseType));
RuntimeTypeModel.Default[typeof(SomeBaseType)].AddSubType(1000000, typeof(SomeType));
执行此操作时,我们遇到此错误:
SomeType node = new SomeType { Name = "Name one", Order = 1 ,Id = 1};
Container container = new Container();
container.ContainedObjects.Add(node);
byte[] bytes;
using (MemoryStream stream = new MemoryStream())
{
Serializer.Serialize(stream, container);
bytes= stream.ToArray();
}
Container container2;
using (MemoryStream stream = new MemoryStream(bytes))
{
container2= Serializer.Deserialize<Container>(stream);
}
有趣的部分是System.ArgumentException : Object of type 'Test.ProtoBufSurrogateTest2+ProtoSurrogate`1[ProtoBufSurrogateTest2+ISomeOtherInterface]' cannot be converted to type 'Test.ProtoBufSurrogateTest2+IAnyConfigurationNode'.
at System.RuntimeType.TryChangeType(Object value, Binder binder, CultureInfo culture, Boolean needsSpecialCast)
at System.Reflection.MethodBase.CheckArguments(Object[] parameters, Binder binder, BindingFlags invokeAttr, CultureInfo culture, Signature sig)
at System.Reflection.RuntimeMethodInfo.InvokeArgumentsCheck(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
at System.Reflection.RuntimePropertyInfo.SetValue(Object obj, Object value, Object[] index)
at ProtoBuf.Serializers.PropertyDecorator.Read(Object value, ProtoReader source) in D:\Dev\BB\Src\External\Protobuf-Net2\Serializers\PropertyDecorator.cs:line 82
at ProtoBuf.Serializers.TypeSerializer.Read(Object value, ProtoReader source) in D:\Dev\BB\Src\External\Protobuf-Net2\Serializers\TypeSerializer.cs:line 227
at ProtoBuf.Serializers.SurrogateSerializer.Read(Object value, ProtoReader source) in D:\Dev\BB\Src\External\Protobuf-Net2\Serializers\SurrogateSerializer.cs:line 129
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source) in D:\Dev\BB\Src\External\Protobuf-Net2\Meta\RuntimeTypeModel.cs:line 832
at ProtoBuf.ProtoReader.ReadTypedObject(Object value, Int32 key, ProtoReader reader, Type type) in D:\Dev\BB\Src\External\Protobuf-Net2\ProtoReader.cs:line 607
at ProtoBuf.BclHelpers.ReadNetObject(Object value, ProtoReader source, Int32 key, Type type, NetObjectOptions options) in D:\Dev\BB\Src\External\Protobuf-Net2\BclHelpers.cs:line 616
at ProtoBuf.Serializers.NetObjectSerializer.Read(Object value, ProtoReader source) in D:\Dev\BB\Src\External\Protobuf-Net2\Serializers\NetObjectSerializer.cs:line 45
at ProtoBuf.Serializers.TagDecorator.Read(Object value, ProtoReader source) in D:\Dev\BB\Src\External\Protobuf-Net2\Serializers\TagDecorator.cs:line 80
at ProtoBuf.Serializers.ListDecorator.Read(Object value, ProtoReader source) in D:\Dev\BB\Src\External\Protobuf-Net2\Serializers\ListDecorator.cs:line 563
at ProtoBuf.Serializers.PropertyDecorator.Read(Object value, ProtoReader source) in D:\Dev\BB\Src\External\Protobuf-Net2\Serializers\PropertyDecorator.cs:line 77
at ProtoBuf.Serializers.TypeSerializer.Read(Object value, ProtoReader source) in D:\Dev\BB\Src\External\Protobuf-Net2\Serializers\TypeSerializer.cs:line 227
at ProtoBuf.Meta.RuntimeTypeModel.Deserialize(Int32 key, Object value, ProtoReader source) in D:\Dev\BB\Src\External\Protobuf-Net2\Meta\RuntimeTypeModel.cs:line 832
at ProtoBuf.Meta.TypeModel.DeserializeCore(ProtoReader reader, Type type, Object value, Boolean noAutoCreate) in D:\Dev\BB\Src\External\Protobuf-Net2\Meta\TypeModel.cs:line 744
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type, SerializationContext context) in D:\Dev\BB\Src\External\Protobuf-Net2\Meta\TypeModel.cs:line 607
at ProtoBuf.Meta.TypeModel.Deserialize(Stream source, Object value, Type type) in D:\Dev\BB\Src\External\Protobuf-Net2\Meta\TypeModel.cs:line 584
at ProtoBuf.Serializer.Deserialize[T](Stream source) in D:\Dev\BB\Src\External\Protobuf-Net2\Serializer.cs:line 84
at Test.ProtoBufSurrogateTest2.TestSurrogateHavingReferenceType() in D:\XXXXXXXTest\ProtoBufSurrogateTest2.cs:line 41
课程中的IF,我只是为简单Container
而不是ContainedObject
更改List
的类型,一切运作良好。< / p>
我已经采用了ProtoBuf代码,进行了一些调查,似乎它与使用的序列化器不同。如果我们有自定义列表,我们会在某些时候使用ItemCollection
,如果我们使用标准列表,则使用TypeSerializer
。在这种情况下,我们快速调用SubItemSerializer
,它试图BclHelper.ReadNetObject
第一个“下一个”对象,在这种情况下是代理。在正常情况下,我们不Trap
代理,而是子项。
我知道它会生成很多代码,所以我已经实现了一个完整的UnitTest来显示问题: https://pastebin.com/auhm35EH
我正在尝试调试3天,我真的希望@MarcGravell能够提供一点帮助。