Deserializing data into a dynamically loaded Assembly

What does scripting languages like php, Perl, python offer over compiled languages? Well if you ask me i would say the ease of development. Make a change in the code and then see the change reflected quickly. This is one of the main reason why scripting is preferred in the web environment. Majority of the websites these days are powered by php in the LAMP stack. Performance of scripted language is far lower than any compiled binary. Compare that with the ASP.NET. The scripts are compiled into MSIL and run on the .NET runtime. Even without caching the ASP framework is almost 2-3 times faster than the Apache on php with mysql. I am not a MS fan-boy nor an open source evangelist.  I had the privilege on doing extensive development on both platforms. There are times when i wished for a platform which had the best of both worlds. When php scores on flexibility for advanced developers. ASP.NET scores on its default ability to separate UI from Logic. Enough of this Web programming. Let me get into what i started out to do.

I had been working on a .NET project where  a DLL A.dll is being used by two different applications. On one application i create an object of a class C inside A.dll, fill its members and serialize it into a binary stream using the standard .NET serializer  and save it into a file. On the second application when i get a request to load the data from the file, I dynamically load the Assembly A.dll. Then i open the file that i saved the byte dump then deserialized it back into an object of the class. Sounds easy, Not so… I was getting an Exception.
System.Runtime.Serialization.SerializationException: Unable to find assembly ‘A’. 

Then i compiled the dll with the application and checked whether its working.  I then got another exception while casting the Deserialized object into a variable of type A.C

Unable to cast type A.C into A.C. 

What the hell!!!  That was my first impression. This was one of the least helpful messages the Visual Studio debugger threw at me. I then tried searching for related resources online in vain.  That’s when i came across this elegant solution involving AppDomains and Serialization Binders. Needless to say my deserialization worked on the dynamically loaded assembly. You can find more details about creating AppDomains here.

I am attaching my C# code below. Hope this proves helpful to you. Look at the serialization binder class in the Deserialize function. The binder class allows you to change the type to which you want to deserialize the byte array to.


public static byte[] Serialize(Object o){           
MemoryStream stream = new MemoryStream(); BinaryFormatter formatter = new BinaryFormatter(); formatter.AssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple; formatter.Serialize(stream, o); return stream.ToArray(); }
public static Object BinaryDeSerialize(byte[] bytes){ MemoryStream stream = new MemoryStream(bytes); BinaryFormatter formatter = new BinaryFormatter(); formatter.AssemblyFormat
= System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple; formatter.Binder
= new VersionConfigToNamespaceAssemblyObjectBinder(); Object obj = (Object)formatter.Deserialize(stream); return obj; }
internal sealed class VersionConfigToNamespaceAssemblyObjectBinder : SerializationBinder { public override Type BindToType(string assemblyName, string typeName) { Type typeToDeserialize = null; try{ string ToAssemblyName = assemblyName.Split(',')[0]; Assembly[] Assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly ass in Assemblies){ if (ass.FullName.Split(',')[0] == ToAssemblyName){ typeToDeserialize = ass.GetType(typeName); break; } } } catch (System.Exception exception){ throw exception; } return typeToDeserialize; } }

6 Responses to “Deserializing data into a dynamically loaded Assembly”

  1. ActiveEngine Sensei Says:

    CLSA.Net has a nice DataPortal concept that allows you to copy objects via Web Services, remoting, and the MS Enterprise Services. The solution is similar to what you have worked out here.

    Here’s a link to his site:

    http://www.lhotka.net

  2. ActiveEngine Sensei Says:

    On a different note, TheCodeslinger has two posts related to Dynamic instantiation of objects that you might find interesting. I posted these two links on my blog:

    http://activeengine.wordpress.com/2007/12/04/new-blood-in-deadwood/

    Enjoy!

  3. gops Says:

    Simply brilliant!

  4. Mohit Gupta Says:

    Excellent approach, Thanks.

    But the DeSerialization fails if Generic Types are used. I posted some changes to the code that makes it work for Generic Types.

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=2816458&SiteID=1&mode=1

  5. Alexandre Says:

    This is great, very useful, exactly what I was looking for.

  6. Alan Balkany Says:

    Wow, it worked! For three days I’ve been struggling to deserialize from a managed DLL. I kept getting the error “Parse Error, no assembly associated with Xml key”. Thanks!

    Substituting your code for the SoapFormatter worked the first time. I’ve rearranged the code slightly to use any Stream:

    public static void serialize(Object o, Stream stream)
    {
    //MemoryStream stream = new MemoryStream();
    BinaryFormatter formatter = new BinaryFormatter();
    formatter.AssemblyFormat
    = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
    formatter.Serialize(stream, o);
    //return stream.ToArray();
    }

    public static Object binaryDeserialize(Stream stream)
    {
    //MemoryStream stream = new MemoryStream(bytes);
    BinaryFormatter formatter = new BinaryFormatter();
    formatter.AssemblyFormat
    = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple;
    formatter.Binder
    = new versionConfigToNamespaceAssemblyObjectBinder();
    Object obj = (Object)formatter.Deserialize(stream);
    return obj;
    }

    internal sealed class versionConfigToNamespaceAssemblyObjectBinder : SerializationBinder
    {
    public override Type BindToType(string assemblyName, string typeName)
    {
    Type typeToDeserialize = null;
    try
    {
    string ToAssemblyName = assemblyName.Split(’,')[0];
    Assembly[] Assemblies = AppDomain.CurrentDomain.GetAssemblies();
    foreach (Assembly ass in Assemblies)
    {
    if (ass.FullName.Split(’,')[0] == ToAssemblyName)
    {
    typeToDeserialize = ass.GetType(typeName);
    break;
    }
    }
    }

    catch (System.Exception exception)
    {
    throw exception;
    }
    return typeToDeserialize;
    }
    }

Leave a Reply