繁体   English   中英

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

[英]InvalidCastException when serializing and deserializing

我有这个代码:

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表中?

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

  • 把这个类型放在一个DLL中,并在两个地方引用单个 dll
  • 使用基于合同的序列化程序

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

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

哪个最好取决于场景。

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

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

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

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

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

这意味着未找到除“加载”之外的任何上下文(在异常消息中也称为“默认”)中加载的程序集中存在的任何类型,并且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实现它时,我遇到了完全相同的问题。

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

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

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

并且

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

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

filteredTasks = (List<App.Task.Entity.GetAllTasksResult>)Session["myTaskList"];

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM