【问题标题】:Why would BinaryFormatter attempt to cast an object of type marked [Serializable] to IConvertible?为什么 BinaryFormatter 会尝试将标记为 [Serializable] 类型的对象转换为 IConvertible?
【发布时间】:2012-02-24 16:53:12
【问题描述】:

我意识到,序列化 NHibernate 域对象通常是一个坏主意,这在其他地方已经很成熟了。我的问题是试图了解BinaryFormatter 是如何工作的,以及为什么下面的场景会产生InvalidCastException。

类结构大致如下:

[Serializable]
public class Parent
{
    public virtual Child child{get; set;} 
}

[Serializable]
public class Child
{
    public virtual ICollection<GrandChild> GrandChildren { get; set; }
}

[Serializable]
public class GrandChild
{
    public virtual Pet pet{get; set;} 
}

[Serializable]
public class Pet
{
    public virtual IList<Toy> Toys { get; set; }
}

[Serializable]
public class Toy
{
    public string ToyName { get; set; }
}

序列化方法如下:

public static byte[] Serialize(this object t)
{
    using (var ms = new MemoryStream())
    {
        BinarySerializer.Serialize(ms, t);
        return ms.ToArray();
    }
}

有时在调用序列化时,例如

 Parent p = new Parent() ....;
 p.Serialize();

我会得到的

无法将“NHibernate.Collection.Generic.PersistentGenericBag`1[Toy]”类型的对象转换为“System.IConvertible”类型。

(所有集合都用包语义映射)。

即使NHibernate.Collection.Generic.PersistentGenericBag&lt;T&gt; 被标记为[Serializable]

既然这里的所有内容都标记为[Serializable],为什么BinaryFormatter 首先会尝试将PersistentGenericBag 转换为IConvertible?

编辑:如果相关,这是在 .NET 3.5 和 NHibernate 3.1.0 下

【问题讨论】:

  • 你说这在序列化时“有时”会发生——这段代码是在多线程应用程序中使用的吗?
  • 它用于网络应用程序,但在我看到的场景下,我认为这可能不是并发问题。似乎它更有可能与延迟加载有关。

标签: c# .net nhibernate serialization exception-handling


【解决方案1】:

通过让 Pet 类继承自 System.Runtime.Serialization.ISerializable,我们现在可以完全控制 Pet 类及其成员(在本例中为 Toy)如何序列化和反序列化。请参阅System.Runtime.Serialization.ISerializable,了解有关实施System.Runtime.Serialization.ISerializable 的更多信息。

下面的示例会将 Parent 类的实例序列化然后反序列化为字节数组,然后再返回。

公共方法public GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context),在Pet类型被序列化时调用;首先,我们添加一个 Int32 值,指示玩具列表中的项目数。然后我们从列表中添加每个玩具。

在反序列化此类型时调用受保护的构造函数,protected Pet(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)。首先我们读取存储在 Toys 列表中的项目数,然后使用它从序列化流中读取每个 Toy 实例。

请注意,对于添加到序列化流中的每个 Toy 实例,我们给它一个不同的名称;在这种情况下,我们只是将索引的值附加到单词 Toy 上;即“Toy1”、“Toy2”、...这是因为序列化流中的每个项目都需要一个唯一的名称。见:System.Runtime.Serialization.ISerializable。

并且通过控制Pet中Toys列表的序列化/反序列化,我们可以消除无法根据类型对列表进行序列化/反序列化的问题:NHibernate.Collection.Generic。 PersistentGenericBag.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

static class Program
{
    static void Main(string[] args)
    {
        Parent p = new Parent();
        p.child = new Child();
        p.child.GrandChildren = new List<GrandChild>();
        p.child.GrandChildren.Add(new GrandChild { pet = new Pet() });
        p.child.GrandChildren.First().pet.Toys = new List<Toy>();
        p.child.GrandChildren.First().pet.Toys.Add(new Toy { ToyName = "Test" });
        byte[] result = Serialize(p);
        Parent backAgain = Deserialize(result);
    }
    public static System.Runtime.Serialization.Formatters.Binary.BinaryFormatter BinarySerializer = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
    public static byte[] Serialize(Parent p) 
    { 
        using (var ms = new System.IO.MemoryStream()) 
        {
            BinarySerializer.Serialize(ms, p); 
            return ms.ToArray(); 
        } 
    }
    public static Parent Deserialize( byte[] data)
    {
        using (var ms = new System.IO.MemoryStream(data))
        {
            return (Parent)BinarySerializer.Deserialize(ms);
        }
    }
}

[Serializable]
public class Parent
{
    public virtual Child child { get; set; }
}

[Serializable]
public class Child
{
    public virtual ICollection<GrandChild> GrandChildren { get; set; }
}

[Serializable]
public class GrandChild
{
    public virtual Pet pet { get; set; }
}

[Serializable]
public class Pet : System.Runtime.Serialization.ISerializable
{
    public Pet() { }

    // called when de-serializing (binary)
    protected Pet(System.Runtime.Serialization.SerializationInfo info,
                  System.Runtime.Serialization.StreamingContext context) 
    {
        Toys = new List<Toy>(); 
        int counter = info.GetInt32("ListCount");
        for (int index = 0; index < counter; index++)
        {
            Toys.Add((Toy)info.GetValue(string.Format("Toy{0}",index.ToString()),typeof(Toy)));
        }
    }

    // called when serializing (binary)
    public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, 
                              System.Runtime.Serialization.StreamingContext context)
    {
        info.AddValue("ListCount", Toys.Count);
        for (int index = 0; index < Toys.Count; index++)
        {
            info.AddValue(string.Format("Toy{0}", index.ToString()), Toys[index], typeof(Toy));
        }
    }

    public virtual IList<Toy> Toys { get; set; }
}

[Serializable]
public class Toy
{
    public string ToyName { get; set; }
}

【讨论】:

  • 这可能会提供一种解决问题的机制,但并不能解释为什么首先会出现奇怪的演员阵容,这确实是问题的重点。
猜你喜欢
  • 2016-04-30
  • 2011-01-19
  • 2017-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多