2012-04-09 65 views
4

我得到一个异常,试图序列化对象图(不是很深)。它有意义的部分是这样的:protobuf-net:检测到可能的递归

[ERROR]致命未处理的异常:ProtoBuf.ProtoException:可能 递归d etected(偏移量:5级(S)):红色在 ProtoBuf.ProtoWriter.CheckRecursionStackAndPush(对象)< 0x00127>在 ProtoBuf.ProtoWriter.StartSubItem(对象,ProtoBuf.ProtoWriter,布尔) < 0x0002f>

该图表示的文件/目录结构和我的模型(简化的)是这样的:

[ProtoContract] 
[ProtoInclude(100, typeof(PackageDirectory))] 
[ProtoInclude(200, typeof(PackageFile))] 
public abstract class PackageMember 
{ 
    [ProtoMember(1)] 
    public virtual string Name { get; protected set; } 

    [ProtoMember(2, AsReference=true)] 
    public PackageDirectory ParentDirectory { get; protected set; } 
} 

[ProtoContract] 
public class PackageDirectory : PackageMember 
{ 
    [ProtoMember(3)] 
    private Dictionary<string, PackageMember> _children; 

    public PackageDirectory() 
    { 
     _children = new Dictionary<string, PackageMember>(); 
    } 

    public PackageDirectory (string name, PackageDirectory parentDirectory) 
     : this() 
    { 
     this.ParentDirectory = parentDirectory; 
     this.Name = name;   
    } 

    public void Add (PackageMember member) 
    { 
     _children.Add(member.Name, member); 
    } 
} 

[ProtoContract] 
public class PackageFile : PackageMember 
{ 
    private Stream _file; 
    private BinaryReader _reader; 

    private PackageFile() 
    {} 

    public PackageFile (string name, int offset, int length, PackageDirectory directory, Stream file) 
    { 
     this.Name = name; 
     this.Length = length; 
     this.Offset = offset; 
     this.ParentDirectory = directory; 

     _file = file; 
     _reader = new BinaryReader(_file); 
    } 

    [OnDeserialized] 
    protected virtual void OnDeserialized(SerializationContext context) 
    { 
     var deserializationContext = context.Context as DeserializationContext; 

     if (deserializationContext != null) 
     { 
     _file = deserializationContext.FileStream; 
     _reader = new BinaryReader(_file); 
     } 
    } 

    [ProtoMember(3)] 
    public int Offset { get; private set; } 

    [ProtoMember(4)] 
    public int Length { get; private set; } 
} 

该树的深度接近10-15级,小于ProtoBuf.ProtoWriter.RecursionCheckDepth值(25)。 (所以也许这是一个错误?) 版本的protobuf网使用是一个编译从中继v2转491)。

其实,我解决了它与修改protobuf网络代码。我将ProtoBuf.ProtoWriter.RecursionCheckDepth的值更改为100,一切似乎都没问题。

问题是如果有任何“真正”的方式来序列化这种类型的图,而不修改protobuf代码?这样的行为是正确的还是错误?

我的平台是单-2.10-8在Windows 7专业版64位

附:我还发现,如果我deserizlie与thw下面的代码,我应该有PackageDirectory无参数构造函数公开。

var value = new PackageDirectory(); 
RuntimeTypeModel.Default.Deserialize(ms, value, typeof(PackageDirectory), new SerializationContext { 
    Context = new DeserializationContext { 
    FileStream = _file, 
}}); 

这是另一个话题,但它提供的代码很好地说明。我认为在这种情况下应该允许声明私有构造函数,因为现在行为与Serializer.Deserialize(...)的行为不同。

+0

该构造函数的问题听起来很奇怪和意外 - 再次,将需要repro;这个运行的平台是什么?正规的.NET?要么...?(由于平台的限制,SL和CF在这里有一些细微的差别) – 2012-04-09 09:03:22

+0

@MarcGravell,对不完整的信息抱歉,我忘了指定我的平台。它是单声道的。我会编辑一个问题。 – ILya 2012-04-09 15:51:09

+0

单声道...? Linux呢? iOS版?另外:您使用的是哪个**确切的**版本号? – 2012-04-09 17:23:23

回答

6

此异常仅当相同的参考中的数据看出(在同一路径两次)时引发和跟踪是才会启用当深度为至少RecursionCheckDepth。这立即让我怀疑引用的10-15深度限制,虽然protobuf处理相当于的情况不一定与您计算的相同。对我来说,将这个数字提高到100是没有意义的,事实上,这个RecursionCheckDepth的存在纯粹是一种优化,用于限制“典型”图形中涉及的工作量,只有在这种情况下才能进行更严格的检查开始看起来很深。

但是,我注意到这可能也暗示了基于继承的处理中的一些细微的错误,也许也与AsReference有关。我广泛而持续地使用protobuf-net,并且我没有看到这样的问题。如果你有一个可复制的repro,我非常想看到它。

+0

感谢您的回复!改变RecursionCheckDepth只是一个临时解决方案,让我可以进一步移动我的项目,所以这不是一个真正的选择......我将准备明天再现的错误示例,并将它分享到某处。 – ILya 2012-04-09 15:56:55

+0

你实际上试图检测*周期*,对吗?递归看起来完全正常,但如果深度足够,它也可能是有害的。 (链接列表任何人?) – 2012-04-09 18:09:13

+0

@BenVoigt是的,这是周期是这里的问题(因为这是一个基于树的序列化程序,有限的可选参考跟踪支持) – 2012-04-09 18:17:43