2014-10-06 55 views
1

我有數十億個對象,我試圖在B +樹中將它們構造爲序列化到HDD。我使用數據結構的BPlusTree庫和序列化/反序列化的protobuf-net。在這方面,我定義我類爲:Protobuf-net與泛型進行反序列化時要求TypeModel.CS

[ProtoContract] 
    public class B<C, M> 
     where C : IComparable<C> 
     where M : IData<C> 
    { 
     internal B() 
     { 
      lambda = new List<Lambda<C, M>>(); 
      omega = 0; 
     } 

     internal B(C coordinate) 
     { 
      lambda = new List<Lambda<C, M>>(); 
      e = coordinate; 
      omega = 0; 
     } 

     [ProtoMember(1)] 
     internal C e { set; get; } 

     [ProtoMember(2)] 
     internal List<Lambda<C, M>> lambda { private set; get; } 

     [ProtoMember(3)] 
     internal int omega { set; get; } 
    } 


[ProtoContract] 
public class Lambda<C, M> 
    where C : IComparable<C> 
    where M : IData<C> 
{ 
    internal Lambda() { } 

    internal Lambda(char tau, M atI) 
    { 
     this.tau = tau; 
     this.atI = atI; 
    } 

    [ProtoMember(1)] 
    internal char tau { private set; get; } 

    [ProtoMember(2)] 
    internal M atI { private set; get; } 
} 

和我定義我串行/解串器如下:

public class BSerializer<C, M> : ISerializer<B<C, M>> 
     where C : IComparable<C> 
     where M : IData<C> 
    { 
     public B<C, M> ReadFrom(System.IO.Stream stream) 
     { 
      return Serializer.Deserialize<B<C, M>>(stream); 
     } 

     public void WriteTo(B<C, M> value, System.IO.Stream stream) 
     { 
      Serializer.Serialize<B<C, M>>(stream, value); 
     } 
    } 

然後我使用它們都在一個B +樹(This library)數據結構,其定義爲:

var options = new BPlusTree<C, B<C, M>>.OptionsV2(CSerializer, BSerializer); 
var myTree = new BPlusTree<C, B<C, M>>(options); 

B +樹被定義爲鍵值對的字典。我的key(即,C)是一個整數,串行器是默認的串行器BPlusTree庫。我的Value是使用protobuf-net序列化的自定義對象B<C,M>

我的問題肯定會發生,但幾乎在隨機時間;總是搜索Keys,它突然開始反序列化Value,並在第一次調用B<C, M> ReadFrom(System.IO.Stream stream)時要求輸入TypeModel.CSProtoReader.CS文件。我從NuGet獲得兩個包。

+0

要求輸入文件只是意味着它捕獲的異常,你必須調試符號;什麼是實際的異常消息? – 2014-10-06 13:02:35

+0

@MarcGravell例外是:'源數據中的無效字段:0' – Hamed 2014-10-06 13:12:13

+0

*通常*表示實際數據無效 - 這裏的更廣泛的上下文是什麼?你是從什麼序列化/反序列化? – 2014-10-06 13:25:07

回答

2

檢查代碼,它看起來像調用代碼假定序列化知道他們自己的長度;從源:

foreach (T i in items) 
    _serializer.WriteTo(i, io); 

的Protobuf消息不是自終止 - 谷歌的protobuf的規範定義了附加===合併。因此,你需要給消息加上前綴。幸運的是,您應該可以切換到SerializeWithLengthPrefixDeserializeWithLengthPrefix。如果這不起作用,那麼將一個完全可重現的例子放在一起是值得研究的。

+0

非常感謝@MarcGravell,在我的地獄代碼上來回差不多10天之後,我救了我;懷疑幾乎所有的東西(你不會相信我,我也在懷疑繼承和多態性;-))謝謝。 – Hamed 2014-10-06 14:09:41

+0

@哈利哇,是否解決了它?我知道調用代碼是有問題的,但我很高興,有些驚訝,如果這是唯一的痛點... – 2014-10-06 14:26:13

+0

是啊,解決了「大」的問題:)這一次它不斷給'試圖讀過去的結尾這大概是下一個噩夢(我希望不是):D – Hamed 2014-10-06 14:35:48

2

作爲一種替代的方法來解決這個問題,你也可以聚集內置serailizers的行爲:

class BSerializer<C, M> : ISerializer<B<C, M>> 
     where C : IComparable<C> 
     where M : IData<C> 
    { 
     public B<C, M> ReadFrom(System.IO.Stream stream) 
     { 
      byte[] value = CSharpTest.Net.Serialization.PrimitiveSerializer.Bytes.ReadFrom(stream); 
      return Serializer.Deserialize<B<C, M>>(new MemoryStream(value)); 
     } 

     public void WriteTo(B<C, M> value, System.IO.Stream stream) 
     { 
      using (var memory = new MemoryStream()) 
      { 
       Serializer.Serialize<B<C, M>>(memory, value); 
       CSharpTest.Net.Serialization.PrimitiveSerializer.Bytes.WriteTo(memory.ToArray(), stream); 
      } 
     } 
    } 

注:此方法可以是一個性能問題,由於數據的不必要的副本;但是,它可以幫助解決問題。

另一種可能性是簡單地將樹定義爲BPlusTree<TKey, byte[]>並提供PrimitiveSerializer.Bytes作爲值串行器。這將對象序列化的負擔放在調用者身上,這可能是一件非常好的事情。這可能是有益的原因有兩個:

  1. 您的對象模型不再需要是不可變的。
  2. 如果對象的反序列化很昂貴,那麼在隨機訪問使用中可能會更好。

對於其他常見的序列化問題和一些例子,請閱讀下面的文章:

http://csharptest.net/1230/bplustree-and-custom-iserializer-implementations/

+0

我同意你對第一個解決方案「不必要的副本」的觀點,我也認爲第二個想法是好的。然而,我有一個小問題:我可以稱這是一個兩步(de)序列化,它是(1)我的值對象爲byte []和(2)序列化byte []'? – Hamed 2014-10-09 06:57:48

+0

@Hamed在讀取數據時,您將序列化爲byte []放入,並從byte []反序列化。 – 2014-10-09 23:40:14

+0

我提前道歉......但你能幫我理解我如何在不使用'Formatter'的情況下將序列化到'byte []'「? – Hamed 2014-10-27 00:50:56

相關問題