2016-11-11 97 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