I am having a problem deserializing objects from a library when the
following conditions exist:
1- The library is strongly named
2- The serialized file was created with version 1.0 of the assembly
3- I am trying to deserialize from an EXE that references version 2.0 of
the assembly
4- Both version 1.0 and 2.0 of the assembly reside in the GAC (no policy
redirects exist).
Note that this is not the AssemblyFormat = FormatterAssemblyStyle.Simple
problem. It is a different issue that arises because the old DLL can
still be loaded from the GAC.
object o = null;
Foo foo = null; // Foo is in the strong named assembly
BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
using(FileStream stream = File.OpenRead("foo.bin"))
{
o = formatter.Deserialize(stream);
}
foo = o as Foo;
// At this point, o is not null, but foo is null
Deserialization succeeds. The problem is that the deserialized object is
a version 1 object. During deserialization, .NET finds the old version
of the library in the GAC, even though I have compiled against version
2.0. The deserialized object is essentially unusable. The cast from
object to Foo returns a null object, since the returned object is not a
2.0 Foo. The modules view in the debugger shows that both versions of
the library are loaded.
If I remove the 1.0 version of the library from the GAC, deserialization
correctly returns a 2.0 Foo object. Is there a way to make this work
that doesn't involve removing the old library from the GAC? Having the
old version available solves a different set of problems for us.
Here is a complete set of steps + code to reproduce
1- Create a simple, serializable class
//---------- MyLib.cs-----------
using System;
using System.Collections.Generic;
using System.Reflection;
[assembly: AssemblyTitle("MyLib")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace MyLib
{
[Serializable]
public class Foo
{
public Foo()
{
for(int i=0;i<10; ++i)
{
Values.Add(i);
}
}
public List<intValues = new List<int>();
}
}
2- Compile MyLib.cs into a strongly named library, and add it to the GAC:
sn -k key.snk
csc /t:library /keyfile:key.snk MyLib.cs
gacutil /i MyLib.dll
3- Create an app that serializes an instance of Foo from version 1.0 of
the library. Compile and execute
//-------Create.cs-------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using MyLib;
static public class Program
{
[STAThread]
public static void Main()
{
Foo foo = new Foo();
BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
using(FileStream stream = File.Create("foo.bin"))
{
formatter.Serialize(stream, foo);
}
}
}
csc /r:MyLib.dll Create.cs
Create.exe
4- Add a new field to Foo, bump the version, rebuild, and install the
new version into the GAC
//---------- MyLib.cs-----------
....<snip>
[assembly: AssemblyVersion("2.0.0.0")]
....<snip>
public class Foo
{
.... <snip>
public string Text = "hello";
}
csc /t:library /keyfile:key.snk MyLib.cs5- Create an app that tries to load the file that was serialized from
gacutil /i MyLib.dll
version 1.0 of the DLL. Compile and execute
//-------Load.cs-------
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters;
using System.Runtime.Serialization.Formatters.Binary;
using MyLib;
static public class Program
{
[STAThread]
public static void Main()
{
object o = null;
Foo foo = null;
BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = FormatterAssemblyStyle.Simple;
using(FileStream stream = File.OpenRead("foo.bin"))
{
o = formatter.Deserialize(stream);
}
Console.WriteLine(o.GetType().AssemblyQualifiedNam e);
foo = o as Foo;
if(foo == null)
Console.WriteLine("foo is null");
else
Console.WriteLine("foo is not null. Loaded correctly");
}
}
csc /r:MyLib.dll Load.csThe output from Load.exe is
Load.exe
MyLib.Foo, MyLib, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=0b5ef2fdd6494a50
foo is null
If I remove the 1.0 version of MyLib from the GAC, deserialization
succeeds. The output is:
MyLib.Foo, MyLib, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=0b5ef2fdd6494a50
foo is not null. Loaded correctly
H^2