views:

701

answers:

3

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:

[A]ActionData cannot be cast to [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'. 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'.

(XXXX.XXXX is there to obscure the client's name)

What gives?

I've now asked a related question here:

http://stackoverflow.com/questions/1141873/how-should-i-serialize-some-simple-auditing-data-for-storing-in-a-sql-table

+2  A: 

It sounds to me like you have the same class in different assemblies (or web applications). BinaryFormatter includes the type metadata in the serialization, which means that only the exact same assembly will do. 2 solutions:

  • put this type in a dll, and reference that single dll in both places
  • 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)
  • DataContractSerializer (xml; serializes marked fields and properties (public or private); "tree" or "graph")
  • protobut-net (binary; serializes marked fields and properties (public or private); "tree" only)

Which is best depends on the scenario.

Marc Gravell
OK, thanks for the advice on other options. I'm not sure which to choose anyway - actually I'll formulate another question. BUT, as I said in a previous comment, those two lines of code are next to each other, IN THE SAME ASSEMBLY!
Craig Shearer
In which case, the first approach (moving this object into a dll) might be the most pragmatic.
Marc Gravell
In this case, it's the same assembly because the full assembly name is the same. But I second XmlSerializer etc. in favour of binary serialization.
Anton Tykhyy
Binary serialization isn't the problem; BinaryFormatter is ;-p There is a subtle difference
Marc Gravell
Oh well, you're right but it's a bit nitpicky :)
Anton Tykhyy
+2  A: 

You have loaded the same assembly twice, in different loader contexts. E.g. you happened to load the XXX.Auditing with Assembly.LoadFrom() first, and then some other (or your) assembly loaded it normally. 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).

Anton Tykhyy
Ah, well actuallly that makes some sense. The Auditing DLL is actually loaded by an ORM dynamically - it's probably using Assembly.LoadFrom() to load it. But that's the assembly that is using the BinaryFormatter to deserialize it, so I don't understand why it would be being loaded again. But then, I don't have much experience with using the BinaryFormatter, so I certainly couldn't be sure.
Craig Shearer
Then there must be someone else who loads this assembly into the 'Default' context. You could try hooking some assembly load events to find out who loads what.
Anton Tykhyy
A: 

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.

The solution was to put the classes I wanted to serialize in a separate assembly so they weren't dynamically loaded.

Craig Shearer