序列化和反序列化时出现InvalidCastException

我有这个代码:

public byte[] SerializeToBlob() { using (var buffer = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(buffer, this); buffer.Position = 0; return buffer.ToArray(); } } public static ActionData DeserializeFromBlob(byte[] state) { using (var buffer = new MemoryStream(state)) { var formatter = new BinaryFormatter(); var result = formatter.Deserialize(buffer); return (ActionData) result; } } 

我打电话给它如下:

 byte[] actionDataBlob = ad.SerializeToBlob(); var ad1 = ActionData.DeserializeFromBlob(actionDataBlob); 

但是,当我尝试将反序列化对象强制转换为其类型时,我得到一个InvalidCastException:

[A] ActionData无法强制转换为[B] ActionData。 类型A源自位于’C:\ Users \ Craig \ AppData \ Local \ Temp \ Temporary ASP’的上下文’Default’中的’XXXX.XXXX.Auditing,Version = 1.0.76.0,Culture = neutral,PublicKeyToken = null’。 NET Files \ root \ 5d978e5b \ ffc57fe1 \ assembly \ dl3 \ 2b1e5f8f \ 102c846e_9506ca01 \ XXXX.XXXX.Auditing.DLL’。 类型B源自位于’F:\ Visual Studio Projects \ XXXXXXXXX \ source \ XXXX.XXXX.SilverlightClient的上下文’LoadNeither’中的’XXXX.XXXX.Auditing,Version = 1.0.76.0,Culture = neutral,PublicKeyToken = null’名.web \ BIN \ XXXX.XXXX.Auditing.dll”。

(XXXX.XXXX会掩盖客户的名字)

是什么赋予了?

我现在在这里问了一个相关的问题:

我应该如何序列化一些简单的审计数据以存储在SQL表中?

您已在不同的加载程序上下文中加载了相同的程序集两次。 例如,您碰巧首先使用Assembly.LoadFrom()加载XXX.Auditing,然后其他一些(或您的)程序集正常加载它。 事实上,二进制反序列化器可能是第二次加载程序集的人,虽然我不知道为什么(没有使用ASP.NET的经验)。

听起来像你在不同的程序集(或Web应用程序)中有相同的类。 BinaryFormatter包含序列化中的类型元数据,这意味着只有完全相同的程序集才能执行。 2解决方案:

  • 将此类型放入dll中,并在两个位置引用该单个 dll
  • 使用基于合同的序列化程序

就个人而言,我会选择第二种,原因不仅限于此。 可能的选择:

  • XmlSerializer(xml;序列化公共字段和属性;仅限“树”)
  • DataContractSerializer(xml;序列化标记的字段和属性(公共或私有);“树”或“图形”)
  • protobuf-net(二进制;序列化标记的字段和属性(公共或私有);仅“树”)

哪个最好取决于场景。

好。 我刚遇到同样的问题。

在我的情况下,问题是因为我从一个字节数组加载一个程序集(我使用插件模型,所以这是相当常见的用途)并且反序列化一个对象,但它不会使用与原始问题。

起初我以为这只是因为序列化器和dll版本控制等…所以我写了自己的序列化器并再次遇到了同样的问题。

问题确实来自类型创建。 在我的反序列化例程中,我使用熟悉的Type.GetType(string)方法并传递AssemblyQualifiedName,它可以正常工作,并且对于位于mscorlib之外的所有类型,它是必需的。 好吧,事实certificateGetType不会遍历加载的程序集列表来尝试找到匹配但是将它留给融合解析器。

这意味着未找到除“加载”之外的任何上下文(在exception消息中也称为“默认”)中加载的程序集中存在的任何类型,并且GetType会尝试正常加载程序集。

在我的例子中,这种行为导致组件的2个实例被加载到appdomain中:从我的字节数组开始,另一个从fusion找到的磁盘。

我通过枚举我的appdomain中加载的程序集,寻找匹配的程序集名称(从我的AssemblyQualifiedName解析)解决了这个问题。 一旦找到,我使用该特定程序集(Assembly.GetType(String))创建了我的类型(减去程序集信息)。

这是允许我克服这个问题的代码:

 Dim ot As System.Type Dim tname = "MyType" Dim aname = "MyPlugin" '// The following lambda expression returns only assemblies that match my assembly name '// Your assembly name could be a fully qualified name or just the simple assembly name '// I chose to use the simple name so that i didn't have to store unnecessary data and because i didn't want version specific info Dim asms = AppDomain.CurrentDomain.GetAssemblies().Where(Function(__) __.GetName.Name.Equals(aname)) 'If there is only one assembly loaded...use it If asms.Count = 1 Then ot = asms(0).GetType(tname) '// If there are multiple assemblies loaded (with the same name), i'm picking the one that is loaded from disk, if such is available, otherwise default back to the first one that was loaded into the appdomain '// If you do have multiple assemblies loaded, it's because you (or .NET) has loaded them in different contexts. You might need to adjust for which context you want. I suppose you could pass the desired context in as a parameter and look for it ElseIf asms.Count > 1 Then Dim asm = asms.FirstOrDefault(Function(__) Not String.IsNullOrEmpty(__.Location)) If asm IsNot Nothing Then ot = asm.GetType(tname) Else ot = asms(0).GetType(tname) End If Else '// not yet loaded...use default type resolution from Type.GetType ot = Type.GetType(tname & "," & aname) End If Dim obj '// Note that the method here is using the already resolved System.Type from above. '// This is important because it causes Activator to create an instance from the assembly '// that we really want and not one from fusion's resolver. obj = Activator.CreateInstance(ot) 

希望这对其他人有帮助。

-Eriq

最后,我认为我的问题在于动态加载。 当我使用XmlSerializer实现它时,我有完全相同的问题。

解决方案是将我想要序列化的类放在一个单独的程序集中,这样它们就不会动态加载。

我有同样的问题和完全相同的错误,但不是一切,但偶尔。 即时通讯使用linqtoSQL,数据列表被序列化,然后通过访问

 filteredTasks = (List)Session["myTaskList"]; 

我使用.net代码与Microsoft Office InfoPath表单有同样的问题。 甚至微软自己的代码也在这个问题上绊倒了。

是的我可以看到它同时从两个不同的位置加载.. \ Appdata \ Local \ assembly \ dl3 …

并且

\应用程序数据\本地\的Micorosoft \的InfoPath \ FormCache4 …