2016-11-11 74 views
2

我有這個接口和類(你可以看看here看到所有相關的代碼是可編譯的)。我想只爲了說明問題(非編譯代碼)提供最少的代碼:BinaryFormatter不反序列化IEnumerable上的對象

interface IViz<T> : ISerializable { 
    IEnumerable<SelectedValue> SelectedValues { get; } 
}; 

[Serializable] 
abstract class GroupViz<T, TIn, TOut> : IViz<T> { 
    public IEnumerable<SelectedValue> SelectedValues 
    { 
     get { return selectedValues.Cast<SelectedValue>(); } 
    } 
} 

[Serializable] 
public class EntityValueGroupViz<TEntity, TKey> : ValueGroupViz<TEntity, TKey> 

在第一界面(IViz)我已經聲明,其中存儲在每個SelectedValue對象的屬性IEnumerable<SelectedValue> SelectedValues

SelectedValue有兩種實現方式(通用和非通用的):

[Serializable] 
public abstract class SelectedValue : ISerializable 
{ 
    public SelectedValue(SerializationInfo info, StreamingContext context) 
    { 
     Configuration.SerializationTemplatesEnum serializationTemplateEnum = (Configuration.SerializationTemplatesEnum)context.Context; 

     foreach (SerializationEntry entry in info) 
     { 
      switch (serializationTemplateEnum) 
      { 
       case Configuration.SerializationTemplatesEnum.QUERY: 
        switch (entry.Name) 
        { 
         case "Value": 
          Value = entry.Value; 
          break; 

         case "Operator": 
          Operator = (VizOperatorEnum)entry.Value; 
          break; 
        } 
        break; 

       case Configuration.SerializationTemplatesEnum.TEMPLATE: 
        break; 

      } 

     } 

    } 
} 

[Serializable] 
public class SelectedValue<T> : SelectedValue, ISerializable 
{ 
    public SelectedValue(SerializationInfo info, StreamingContext context) 
     : base(info, context) 
    { 

    } 
} 

以它們序列化我使用BinaryFormatter,他們(上IViz.SelectValues財產SelectedValue對象)上連載文件。

但是,當我試圖反序列化它們時,它們沒有加載。我在SelectedValue(SerializationInfo info, StreamingContext context)構造函數上添加了一個斷點,但未達到。

我也試着在IViz.SelectedValues屬性上添加一個實現,我也嘗試將該屬性設置爲IList而不是IEnumerable。但結果是一樣的:我的SelectedValue對象沒有反序列化。

任何想法?

+0

感謝您的意見。我試着添加每個最小的de /序列化相關代碼,以便能夠適合您的請求。它在[this](https://dotnetfiddle.net/bPYdJU)上。 – Jordi

+0

好吧,如何反序列化'EntityValueGroupViz'中的'SelectedValues'有問題,但是[你的小提琴](https://dotnetfiddle.net/bPYdJU)中的測試代碼不會構造和存儲這種類型的實例它構造了一個'EntityQueryContext '的實例。在基類中似乎有一個'IList > vizs',但它不是公有的,也不會被填充。 – dbc

+0

謝謝。據我所知,你提出反序列化是有效的,但是我沒有在任何地方達到我的'vizs'的內容(我認爲這可能是一種代碼錯寫)。我想建議詢問爲什麼在序列化時到達SelectedValue.GetObjectData()時爲什麼在反序列化時到達了SelectedValue(SerializationInfo info,StreamingContext context)構造函數。我希望我解釋得很好。 – Jordi

回答

2

我能夠通過構建一個EntityValueGroupViz<BOEntity, BOEntity>的實例,爲它添加一個SelectedValue<BOEntity>(new BOEntity(), "hello")並序列化來重現您的問題。有關mcve,請參閱this fiddle

然而,簡單地去重現問題的地步,我只好:

  • 馬克BOEntity[Serializable]

  • 將默認和流式構造函數添加到GroupViz<T, TIn, TOut>ValueGroupViz<T, TIn>

  • 在構造函數中分配GroupViz<T, TIn, TOut>裏面的selectedValues列表。

  • 將默認構造函數添加到EntityValueGroupViz<TEntity, TKey>

一旦這些初步修復製成,問題的EntityValueGroupViz<TEntity, TKey>成爲流構造明顯:

protected EntityValueGroupViz(SerializationInfo info, StreamingContext context) 
    { 
     foreach (SerializationEntry entry in info) 
     { 
      switch (entry.Name) 
      { 
       case "SelectedValues": 
        foreach (SelectedValue sv in (IEnumerable<SelectedValue>)entry.Value) 
         this.Value(sv); 
        break; 
      } 
     } 
    } 

在這個被調用的時候,該(IEnumerable<SelectedValue>)entry.Value具有空條目。但是,爲什麼呢? BinaryFormatter是一個圖形串行器。不是將對象存儲在純樹中,而是分配臨時對象標識並在遇到時進行存儲。當一個對象被反序列化時,並不能保證所有被引用的對象都被預先反序列化。因此,在您調用流式構造函數時,您的entry.Value中的條目可能尚未填充。作爲確認,微軟writes

對象是由內而外的重建和反序列化過程中調用的方法可以有不良的副作用,因爲調用的方法可能是指對象沒有被反序列化的通話是時間基準製作。

遍歷一個List<T>實際上涉及調用方法。

那麼,該如何處理?有幾個可能的解決方法:

  1. 實施IDeserializationCallbackEntityValueGroupViz<TEntity, TKey>,暫時緩存在流構造的entry.Value,後來把它添加到基類IDeserializationCallback.OnDeserialization()

    [Serializable] 
    public class EntityValueGroupViz<TEntity, TKey> : ValueGroupViz<TEntity, TKey>, IDeserializationCallback 
    { 
        IEnumerable<SelectedValue> cachedEntry; 
    
        // Added necessary default constructor. 
        public EntityValueGroupViz() : base() { } 
    
        protected EntityValueGroupViz(SerializationInfo info, StreamingContext context) : base(info, context) 
        { 
         foreach (SerializationEntry entry in info) 
         { 
          switch (entry.Name) 
          { 
           case "SelectedValues": 
            cachedEntry = (IEnumerable<SelectedValue>)entry.Value; 
            break; 
          } 
         } 
        } 
    
        public override void GetObjectData(SerializationInfo info, StreamingContext context) 
        { 
         info.AddValue("SelectedValues", SelectedValues); 
        } 
    
        #region IDeserializationCallback Members 
    
        public void OnDeserialization(object sender) 
        { 
         if (cachedEntry != null) 
         { 
          foreach (SelectedValue sv in cachedEntry) 
           this.Value(sv); 
          cachedEntry = null; 
         } 
        } 
    
        #endregion 
    } 
    

    樣品fiddle

  2. 只需將該字段所在基類中的選定值列表序列化即可。 BinaryFormatter系列化流完全類型的,所以selectedValues場可以存儲在那裏,即使基類不知道集合裏面的亞型:

    [Serializable] 
    public abstract class GroupViz<T, TIn, TOut> : IViz<T> 
    { 
        // Added necessary default and streaming constructors 
        public GroupViz() 
        { 
         selectedValues = new List<SelectedValue>(); 
        } 
    
        protected GroupViz(SerializationInfo info, StreamingContext context) 
        { 
         selectedValues = (IList<SelectedValue>)info.GetValue("SelectedValues", typeof(IList<SelectedValue>)); 
        } 
    
        // Allocated the list 
        private IList<SelectedValue> selectedValues; 
    
        public virtual void GetObjectData(SerializationInfo info, StreamingContext context) 
        { 
         info.AddValue("SelectedValues", selectedValues); 
        } 
    
        public IEnumerable<SelectedValue> SelectedValues 
        { 
         get { return selectedValues.Cast<SelectedValue>(); } 
        } 
    
        public void Value(SelectedValue @value) 
        { 
         this.AddValue(@value.Value, @value.Operator); 
        } 
    
        private void AddValue(object @value, object vizOperator) 
        { 
         SelectedValue<TOut> selectedValue = new SelectedValue<TOut>((TOut)value, vizOperator); 
         if (!this.selectedValues.Any(sv => sv.Equals(selectedValue))) 
          this.selectedValues.Add(selectedValue); 
        } 
    } 
    
    public abstract class ValueGroupViz<T, TIn> : GroupViz<T, TIn, TIn> 
    { 
        // Added necessary default and streaming constructors 
        public ValueGroupViz() : base() { } 
    
        protected ValueGroupViz(SerializationInfo info, StreamingContext context) : base(info, context) { } 
    } 
    
    [Serializable] 
    public class EntityValueGroupViz<TEntity, TKey> : ValueGroupViz<TEntity, TKey> 
    { 
        // Added necessary default constructor. 
        public EntityValueGroupViz() : base() { } 
    
        protected EntityValueGroupViz(SerializationInfo info, StreamingContext context) : base(info, context) { } 
    } 
    

    正如你所看到的,該解決方案更簡單,因此建議。

    樣本fiddle #2

+0

親愛的。我試着用第二個選項寫下來。不客氣地說,它一直在失敗。我的意思是,正如你所猜測的:當case「SelectedValues」:foreach(SelectedValue sv in(IEnumerable )entry.Value)this.Value(sv);'is reached'sv is null'。我在'GroupViz','ValueGroupViz'和'EntityValueGroupViz'上添加了一個默認的公共構造函數。我剛剛意識到'SelectedValue'沒有默認的空構造函數... – Jordi

+0

@Jordi - 在第二個選項中,代碼被完全刪除。相反,在基類中有'selectedValues =(IList )info.GetValue(「SelectedValues」,typeof(IList ));' – dbc