2011-06-28 60 views
3

我有一個相當複雜的繼承層次結構,包括泛型,我們試圖使用protobuf .net進行序列化。不幸的是,它似乎無法正確處理這種情況。這就是層次結構的樣子。Protobuf網絡通用繼承和封閉構造通用類型

[System.Runtime.Serialization.DataContract] 
    [ProtoBuf.ProtoInclude(1000, typeof(GenericBaseClass<object>))] 
    [ProtoBuf.ProtoInclude(1001, typeof(GenericBaseClass<string>))] 
    public abstract class BaseClass 
    { 

     public int BaseProperty1 { set; get; } 
     public int BaseProperty2 { set; get; } 

     public BaseClass() 
     { 

     } 

    } 

    [System.Runtime.Serialization.DataContract] 
    [ProtoBuf.ProtoInclude(1002, typeof(GenericDerivedClass<object>))] 
    [ProtoBuf.ProtoInclude(1003, typeof(GenericDerivedClass<string>))] 
    public abstract class GenericBaseClass<T> : BaseClass 
    { 
     /// <summary> 
     /// 
     /// </summary> 
     [System.Runtime.Serialization.DataMember(Order = 5)] 
     public T ResponseProperty 
     { 
      get; 
      set; 
     } 

     public GenericBaseClass() 
     { 
     } 
    } 

    [System.Runtime.Serialization.DataContract] 
    [ProtoBuf.ProtoInclude(1004, typeof(DerivedClass1))] 
    [ProtoBuf.ProtoInclude(1005, typeof(DerivedClass2))] 
    public abstract class GenericDerivedClass<T> : GenericBaseClass<T> 
    { 
     public int AdditionalProperty { get; set; } 

     public GenericDerivedClass() 
     { 

     } 
    } 

最後這些類是由兩個封閉構造非通用類

[System.Runtime.Serialization.DataContract] 
    public class DerivedClass1 : GenericDerivedClass<string>    
    { 
     [System.Runtime.Serialization.DataMember(Order = 6)] 
     public int DerivedClass1Property { set; get; } 
    } 

    [System.Runtime.Serialization.DataContract] 
    public class DerivedClass2 : GenericDerivedClass<object> 
    { 
     [System.Runtime.Serialization.DataMember(Order = 7)] 
     public int DerivedClass2Property { set; get; } 
    } 

我寫了下面的測試方法,這些序列化和它給我的錯誤來實現。

[TestMethod] 
    public void SerializeDeserializeAndCompare() 
    {    

     DerivedClass2 i = new DerivedClass2() { BaseProperty1 = 1, BaseProperty2 = 2, DerivedClass2Property = 3, ResponseProperty = new Object() }; 
     using (var file = System.IO.File.Create("test.bin")) 
     { 
      ProtoBuf.Serializer.Serialize(file, i); 
     } 

     using (var file = System.IO.File.OpenRead("test.bin")) 
     { 
      var o = ProtoBuf.Serializer.Deserialize<DerivedClass2>(file); 
     } 
    } 

我得到的錯誤是

ProtoBuf.ProtoException:A型只能參加一個繼承層次(CapitalIQ.DataGet.UnitTests.DataSetUnitTest + DerivedClass2)---> System.InvalidOperationException:一類型只能參與一個繼承層次結構

這是protobuf .net的限制還是我做了一些不正確的事情。我正在使用r282版本。

感謝 Shobhit

+0

像這樣的東西應該工作。我現在在「孩子的責任」,但會看起來後面 –

回答

4

與所有的屬性,在屬性包含的類型信息適用於所有封閉類型的泛型類型定義。因此,你實際上已經定義了什麼(以protobuf網)是:

BaseClass 
: GenericBaseClass<object> 
: GenericDerivedClass<object> 
    : DerivedClass1 
    : DerivedClass2 
: GenericDerivedClass<string> 
    : DerivedClass1 
    : DerivedClass2 
: GenericBaseClass<string> 
: GenericDerivedClass<object> 
    : DerivedClass1 
    : DerivedClass2 
: GenericDerivedClass<string> 
    : DerivedClass1 
    : DerivedClass2 

正如你所看到的,有很多重複的 - 這顯然是令人困惑的。由於屬性參數不能使用類型參數,因此會添加某種奇怪的謂詞機制,這是IMO相當混亂的選項。國際海事組織,這是手動模擬這是一個更好的主意(刪除ProtoInclude屬性)。我懷疑你的意圖模式是:

BaseClass 
: GenericBaseClass<object> 
: GenericDerivedClass<object> 
    : DerivedClass2 
: GenericBaseClass<string> 
: GenericDerivedClass<string> 
    : DerivedClass1 

protobuf網可以與工作,但解釋模型需要「V2」和RuntimeTypeModel

還要注意object有點對protobuf的問題; protobuf-net可以使用動態類型選項來僞造它,但那不是理想的。它當然不能序列化一個object,所以爲了測試我已經替換了一個字符串。還要注意的是,BaseProperty1,BaseProperty2AdditionalProperty當前未標記爲序列化,但可以是平凡的。

總之:

RuntimeTypeModel.Default[typeof(BaseClass)] 
    .AddSubType(10, typeof(GenericBaseClass<object>)) 
    .AddSubType(11, typeof(GenericBaseClass<string>)); 

RuntimeTypeModel.Default[typeof(GenericBaseClass<object>)] 
    .AddSubType(10, typeof(GenericDerivedClass<object>)); 
RuntimeTypeModel.Default[typeof(GenericBaseClass<object>)][5].DynamicType = true; // object! 
RuntimeTypeModel.Default[typeof(GenericDerivedClass<object>)] 
    .AddSubType(10, typeof(DerivedClass2)); 

RuntimeTypeModel.Default[typeof(GenericBaseClass<string>)] 
    .AddSubType(10, typeof(GenericDerivedClass<string>)); 
RuntimeTypeModel.Default[typeof(GenericDerivedClass<string>)] 
    .AddSubType(10, typeof(DerivedClass1)); 

DerivedClass2 i = new DerivedClass2() { BaseProperty1 = 1, BaseProperty2 = 2, DerivedClass2Property = 3, ResponseProperty = "some string" }; 
using (var file = System.IO.File.Create("test.bin")) 
{ 
    ProtoBuf.Serializer.Serialize(file, i); 
} 

using (var file = System.IO.File.OpenRead("test.bin")) 
{ 
    var o = ProtoBuf.Serializer.Deserialize<DerivedClass2>(file); 
} 

你不使用RuntimeTypeModel.Default - 事實上,我建議使用(和緩存)分離型的模式;但Serializer.Serialize指向默認模型。如果您創建一個自定義模型(TypeModel.Create),只需將其存儲在某處並使用Serialize等。

+0

非常感謝您的快速回復!鑑於該示例只是更復雜的類層次結構的分支,手動創建typeModel可能會變得非常複雜。而且我的不好,我只是用對象作爲類型參數作爲例子。相反,我們有其他的對象填充T參數。我會給它一個鏡頭,看看我是否可以自動使用反射。但除此之外,我認爲必須有一些方法可以用屬性表示這樣的層次結構。 – shomat

+1

我複製了一個用於子類的子類的不正確的KnownType屬性後,碰到了這個異常。如果可能的話,如果異常文本包含重複的類,將會很有用。 – Shaun