简体   繁体   English

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

[英]InvalidCastException when serializing and deserializing

I have this code: 我有这个代码:

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;
    }
}

And am calling it as follows: 我打电话给它如下:

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

However, I get an InvalidCastException when it tries to cast the deserialized object to its type: 但是,当我尝试将反序列化对象强制转换为其类型时,我得到一个InvalidCastException:

[A]ActionData cannot be cast to [B]ActionData. [A] ActionData无法强制转换为[B] ActionData。 Type A originates from 'XXXX.XXXX.Auditing, Version=1.0.76.0, Culture=neutral, PublicKeyToken=null' in the context 'Default' at location 'C:\\Users\\Craig\\AppData\\Local\\Temp\\Temporary ASP.NET Files\\root\\5d978e5b\\ffc57fe1\\assembly\\dl3\\2b1e5f8f\\102c846e_9506ca01\\XXXX.XXXX.Auditing.DLL'. 类型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'。 Type B originates from 'XXXX.XXXX.Auditing, Version=1.0.76.0, Culture=neutral, PublicKeyToken=null' in the context 'LoadNeither' at location 'F:\\Visual Studio Projects\\XXXXXXXXX\\source\\XXXX.XXXX.SilverlightClient.Web\\bin\\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 is there to obscure the client's name) (XXXX.XXXX会隐藏客户的名字)

What gives? 是什么赋予了?

I've now asked a related question here: 我现在在这里问了一个相关的问题:

How should I serialize some simple auditing data for storing in a SQL table? 我应该如何序列化一些简单的审计数据以存储在SQL表中?

It sounds to me like you have the same class in different assemblies (or web applications). 听起来像你在不同的程序集(或Web应用程序)中有相同的类。 BinaryFormatter includes the type metadata in the serialization, which means that only the exact same assembly will do. BinaryFormatter包含序列化中的类型元数据,这意味着只有完全相同的程序集才能执行。 2 solutions: 2解决方案:

  • put this type in a dll, and reference that single dll in both places 把这个类型放在一个DLL中,并在两个地方引用单个 dll
  • use a contract-based serializer 使用基于合同的序列化程序

Personally I would choose the second for a huge number of reasons not just limited to this one. 就个人而言,我会选择第二种,原因不仅限于此。 Likely choices: 可能的选择:

  • XmlSerializer (xml; serializes public fields and properties; "tree" only) XmlSerializer(xml;序列化公共字段和属性;仅限“树”)
  • DataContractSerializer (xml; serializes marked fields and properties (public or private); "tree" or "graph") DataContractSerializer(xml;序列化标记的字段和属性(公共或私有);“树”或“图形”)
  • protobuf-net (binary; serializes marked fields and properties (public or private); "tree" only) protobuf-net(二进制;序列化标记的字段和属性(公共或私有);仅“树”)

Which is best depends on the scenario. 哪个最好取决于场景。

You have loaded the same assembly twice, in different loader contexts . 您已在两个不同的加载器上下文中加载了相同的程序集。 Eg you happened to load the XXX.Auditing with Assembly.LoadFrom() first, and then some other (or your) assembly loaded it normally. 例如,您碰巧首先使用Assembly.LoadFrom()加载XXX.Auditing,然后其他一些(或您的)程序集正常加载它。 In fact, the binary deserializer could be the one who loaded the assembly a second time, though I wouldn't know why (no experience with ASP.NET). 事实上,二进制反序列化器可能是第二次加载程序集的人,虽然我不知道为什么(没有使用ASP.NET的经验)。

Ok. 好。 I have just run into this same problem. 我刚遇到同样的问题。

In my case, the issue was because i was loading an assembly from a byte array (i'm using a plugin model, so this is fairly common use) and was deserializing an object but it wouldn't cast with the same message as the original question. 在我的情况下,问题是因为我从一个字节数组加载一个程序集(我使用插件模型,所以这是相当常见的用途)并且反序列化一个对象,但它不会使用与原始问题。

At first i thought it was just because of the serializer and dll versioning and such...so i wrote my own serializer and ran into the same issue again. 起初我认为这只是因为序列化器和DLL版本控制等...所以我写了自己的序列化器并再次遇到同样的问题。

The issue really came from type creation. 问题确实来自类型创建。 In my deserialization routines i was using the familiar Type.GetType(string) method and passing an AssemblyQualifiedName, which does work and for all types that reside outside of mscorlib, it's required. 在我的反序列化例程中,我使用熟悉的Type.GetType(string)方法并传递AssemblyQualifiedName,它确实有效,并且对于所有位于mscorlib之外的类型,它都是必需的。 Well, turns out that GetType does not walk the list of loaded assemblies to try and find a match but leaves it up to the fusion resolver. 好吧,事实证明GetType不会遍历加载的程序集列表来尝试找到匹配但是将它留给融合解析器。

This means that any type that exists in an assembly that was loaded in any context other than 'Load' (aka 'Default' in the exception message) is not found and GetType attempts to load the assembly normally. 这意味着未找到除“加载”之外的任何上下文(在异常消息中也称为“默认”)中加载的程序集中存在的任何类型,并且GetType会尝试正常加载程序集。

In my case, this behavior caused 2 instances of the assembly to be loaded in the appdomain: on from my byte array, and the other from disk as found by fusion. 在我的例子中,这种行为导致组件的2个实例被加载到appdomain中:从我的字节数组开始,另一个从fusion找到的磁盘。

I solved this problem by enumerating the loaded assemblies in my appdomain, looking for a matching assembly name (parsed from my AssemblyQualifiedName). 我通过枚举我的appdomain中加载的程序集来寻找匹配的程序集名称(从我的AssemblyQualifiedName解析)来解决这个问题。 Once found, i created my type (minus the assembly info) using that specific assembly (Assembly.GetType(String)). 一旦找到,我使用该特定程序集(Assembly.GetType(String))创建了我的类型(减去程序集信息)。

Here's the code that allowed me to overcome this issue: 这是允许我克服这个问题的代码:

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)

Hopefully this is helpful to someone else. 希望这对其他人有帮助。

-Eriq -Eriq

In the end, my problem was with the dynamic loading, I think. 最后,我认为我的问题在于动态加载。 When I implemented it using the XmlSerializer I had exactly the same problem. 当我使用XmlSerializer实现它时,我遇到了完全相同的问题。

The solution was to put the classes I wanted to serialize in a separate assembly so they weren't dynamically loaded. 解决方案是将我想要序列化的类放在一个单独的程序集中,这样它们就不会动态加载。

I am having this same problem with an Microsoft Office InfoPath form using .net code. 我使用.net代码的Microsoft Office InfoPath表单遇到同样的问题。 Even Microsofts own code has tripped up on this issue. 甚至微软自己的代码也在这个问题上绊倒了。

Yes I can see it is loading from two different locations at the same time.. \\Appdata\\Local\\assembly\\dl3... 是的我可以看到它同时从两个不同的位置加载.. \\ Appdata \\ Local \\ assembly \\ dl3 ...

and also 并且

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

sigh

Im having the same problem and exactly the same error, but not everything, but occasionally. 我有同样的问题和完全相同的错误,但不是一切,但偶尔。 im using linqtoSQL and the data list is serialized and then access via 即时通讯使用linqtoSQL,数据列表被序列化,然后通过访问

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

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

相关问题 问题:序列化和反序列化,重要内容以及何时使用 - Question: Serializing and deserializing, the significants and when to use it 序列化/反序列化时保留类型信息 - Keeping type information when serializing / deserializing 反序列化然后序列化时保留 Xml Xsd schemaLocation - Preserve Xml Xsd schemaLocation when deserializing then serializing 序列化/反序列化 XML 时去掉根 - Remove the root when serializing/deserializing XML 反序列化 JSON 时,我收到 `System.InvalidCastException` - When deserializing JSON I'm getting `System.InvalidCastException` 序列化JSON时:如何确定导致InvalidCastException的原因? - When serializing JSON: how to determine what caused InvalidCastException? 使用 Utf8Json 序列化/反序列化时指定 Epoch TimeDate 格式 - Specify Epoch TimeDate Format when serializing/deserializing with Utf8Json 当类具有隐式运算符时使用 BinaryFormatter 进行序列化/反序列化 - Serializing/deserializing with BinaryFormatter when class has implicit operator 使用WCF服务和DataTable进行序列化/反序列化时,停止DateTime的自动本地化 - STOP Auto Localization of DateTime when Serializing/Deserializing with WCF Service and DataTable 调用API时反复进行反序列化和序列化-有更好的方法吗? - Repeated deserializing and serializing when calling API - is there a better way?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM