While testing a SQL CLR procedure recently I came across an issue with the binary formatter deserialization failing because it could not find the assembly for the type (even though the type is within the same assembly as the deserialization code).
The database application I have been working on has an initial request object (from a web service or web site) that is serialized using the binary formatter and stored in a Request table. It is then added into a Queue to be processed by one or more Service Broker services at a later time.
The serialization and deserialization of the request object is performed by a SQL CLR procedure, utilising the “System.Runtime.Serialization.Formatters.Binary.BinaryFormatter” formatter.
While testing that the Service Broker was configured correctly I was reusing the same Request table data, where the request object had previously been serialized and saved to the table. However I quickly noticed that after re-deploying the SQL CLR project I was running into an error while deserializing the object (note: the type being serialized/deserialized is in the same assembly as the code that serializes it):
Unable to find assembly 'MyAssembly; Version=1.0.3463.18923; Culture=neutral; PublicKeyToken=40e3171cc8066fe6'.
After a little googling it became apparent that the binary formatter stores the type information within the serialized object so that it knows what type to deserialize it to (that makes sense), however this becomes a big issue if you are storing data for periods of time and need to maintain backwards compatibility with new versions of the assembly containing the type information.
Luckily the guys at MS had thought of this and provided us with the “System.Runtime.Serialization.SerializationBinder” class. By inheriting from this class it is possible to redirect all the requests for types from the binary formatter to the types of your choice.
Here is a sample that will allow the types to be found in the current assembly regardless of which version of the assembly originally created the serialized stream:
sealed class AllowAllAssemblyVersionsDeserializationBinder : System.Runtime.Serialization.SerializationBinder { public override Type BindToType(string assemblyName, string typeName) { Type typeToDeserialize = null; String currentAssembly = Assembly.GetExecutingAssembly().FullName; // In this case we are always using the current assembly assemblyName = currentAssembly; // Get the type using the typeName and assemblyName typeToDeserialize = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName)); return typeToDeserialize; } } public static MyRequestObject Deserialize(byte[] b) { MyRequestObject mro = null; System.Runtime.Serialization.Formatters.Binary.BinaryFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); System.IO.MemoryStream ms = new System.IO.MemoryStream(b); // To prevent errors serializing between version number differences (e.g. Version 1 serializes, and Version 2 deserializes) formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder(); // Allow the exceptions to bubble up // System.ArgumentNullException // System.Runtime.Serialization.SerializationException // System.Security.SecurityException mro = (MyRequestObject)formatter.Deserialize(ms); ms.Close(); return mro; }
In my case the type itself had not changed between assembly versions, only the assembly version information. However the MSDN link below shows an example of allowing backwards compatibility between different versions of the type where the type itself may have changed significantly.
(http://msdn.microsoft.com/en-us/library/system.runtime.serialization.serializationbinder(VS.71).aspx)
P.S. this would also be a great solution if you are attempting to deserialize an object that was created in a different assembly that you cannot reference in your own project for some reason – providing you know the structure of the object and create a similar class in your own assembly.
very helpfull. thanks.
Great Post mate, Thank you
A similar issues has been causing me headaches – thanks for the good explanation of how to do this.
Thanks a lot man.
1 Day of breaking head relaxed with your simple code.
very helpful. I think there’s a typo:
AllowAllAssemblyVersionsDeserializationBinder
doesn’t match
formatter.Binder = new AllowAllVersionDeserializationBinder();
But my problem is fixed, thanks much!
Thanks for pointing that out – fixed.
J
Very helpful, thanks!
Exactly what I was looking desperatly for. I love you, man.
actually this reason can be avoided by generating a dll library for the common DTO object from another VS project and adding the built dll file as a reference to both serialization and deserialization classes!
else you have to use a manual versionning process like in the given link to the references object types to avoid the casting problems!
regards!
Very helpful! Thanks!
Thanks!!!
Thanks, bro … it worked!, uff!!!
using System.Reflection;
public sealed class MessageSerializers : System.Runtime.Serialization.SerializationBinder
{
….
public override Type BindToType(string assemblyName, string typeName)
{ … as you said …
}
public static IMessage DeserializeMessage(byte[] bMessage)
{
…
formatter.Binder = new MessageSerializers();
Object message = formatter.Deserialize(ms);
return (IMessage)message;
}
…
Very helpful! Thanks!
Thanks a lot. Helped me out of a really tough situation.. A minor variation of this by just using the typename worked in my case..
what if you have multiple assemblies? I having difficulty with implementation :(
Hi earlslick,
Play around with line 10, debug it and check what the deserializer passes in as “assemblyName”. You will need to change it to the appropriate value for the assembly that contains the type you intend to use as the deserialized type.
Cheers,
J
Excellent, exactly what I needed, this saved me a LOT of trouble I was wondering what would be an alternative to this binary formatter because of this error. Thanks a ton!
PS: my situation was that in the same assembly and by the same version as well, I don’t know why it didn’t work. But your class AllowAllAssemblyVersionsDeserializationBinder worked like magic.
than’s what i know.. -_-??
than? that! hhh -_-;;
Awsome – having now to play with the XML seraliser… I am just starting to appreciate how powerful this is and (in my context) much more managable code.
It’s fantastic. Thank you so much!!!! I was so desperate because I didn’t know how to solve this. But it finally works!!!
this helped me, thanks so much for posting the solution :)
This is a wonderful article, but unfortunately, it will never work with nested types. Try it and you’ll see what I mean.
Where exactly do u put this code? In the form code file or the Assembly file?
Put it where you are doing your deserialisation – most likely in your main application/form code, but could be anywhere you are experiencing the problem.
this is for custom class which have list and non list object
sealed class VersionDeserializationBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
Type typeToDeserialize = null;
string currentAssemblyInfo = Assembly.GetExecutingAssembly().FullName;
//my modification
string currentAssemblyName = currentAssemblyInfo.Split(‘,’)[0];
if (assemblyName.StartsWith(currentAssemblyName))assemblyName = currentAssemblyInfo;
typeToDeserialize = Type.GetType(string.Format(“{0}, {1}”, typeName, assemblyName));
return typeToDeserialize;
}
I have problem with List
Error when deserializing about System.Collections.Generic.List`1[[
Using a SerializationBinder.
MfaContainer and MfaFolder type are in the same assembly.
Anyways, System.Collections.Generic.List is in mscorlib.dll. and then Assembly.GetExecutingAssembly().FullName not valid
My class
[Serializable]
public class MfaContainer
{
public MfaContainer()
{
_mfaFolder = new List();
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(MyResolveEventHandler);
}
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args)
{
return typeof(MfaContainer).Assembly;
}
private string _label;
public string Label
{
get { return _label; }
set { _label = value; }
}
private string _version;
public string Version
{
get { return _version; }
set { _version = value; }
}
private List _mfaFolder;
public List MfaFolders
{
get { return _mfaFolder; }
}
}
public sealed class PreMergeToMergedDeserializationBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
Type typeToDeserialize = null;
// For each assemblyName/typeName that you want to deserialize to
// a different type, set typeToDeserialize to the desired type.
String exeAssembly = Assembly.GetExecutingAssembly().FullName;
// The following line of code returns the type.
typeToDeserialize = Type.GetType(String.Format(“{0}, {1}”, typeName, exeAssembly));
return typeToDeserialize;
}
}
}
I am facing the same problem. Did you get any solution?
I was also getting this issue:
Error when deserializing about System.Collections.Generic.List`1[[
Solution:
Assembly name will be same as passed from other side like
mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
and typeName contains the version e.g.:
System.Collections.Generic.List`1[[Products.Package_SR, Products, Version=1.0.4.3, Culture=neutral, PublicKeyToken=null]]
make it like this:
System.Collections.Generic.List`1[[Products.Package_SR]]
Sample code below:
sealed class AllowAllAssemblyVersionsDeserializationBinder : System.Runtime.Serialization.SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
Type typeToDeserialize = null;
String currentAssembly = System.Reflection.Assembly.GetExecutingAssembly().FullName;
if (assemblyName.Contains(“mscorlib”) && typeName.Contains(“System.Collections.Generic.List`1[[Products.Package_SR”))
{
String newTypeName = “System.Collections.Generic.List`1[[Products.Package_SR]]”;
typeName = newTypeName;
}
else
{
// In this case we are always using the current assembly
assemblyName = currentAssembly;
}
// Get the type using the typeName and assemblyName
typeToDeserialize = Type.GetType(String.Format(“{0}, {1}”, typeName, assemblyName));
return typeToDeserialize;
}
}
hello
I’d like some info on your comment…
“P.S. this would also be a great solution if you are attempting to deserialize an object that was created in a different assembly that you cannot reference in your own project for some reason – providing you know the structure of the object and create a similar class in your own assembly.”
This is actually what i’m having issues right now. A separate client application serialize an object type from a different project class (assembly)…actually it is a list of the object type…and stores it in a table. I wanted to use a CLR to deserialize the same data, so I can use some of the property data in it. I have the structure of the object since I built the the client application. But i’m getting the “Unable to find assembly” error. I don’t want reference that assembly that has the object type, since I know the object type and can create the structure for the class in my CLR. But just can’t get it to work. I used your example and still nothing. Do have no choice but to use the actual assembly? Which would mean I would have to load the dll onto the sql server that will host the CLR.
Any input would be appreciated.
thanks
Hello Ranjit,
I see what you are doing. My problem is the assemblyname aspect of this. I don’t have the actual have assembly referenced in my project. Like i stated, the data is a serialized list of an object type that is serialized from different client app. And I just want to deserialize and pull the data. I do know the object class structure, and can just add it to my CLR class/assembly.
Here’s the error I’m getting:
System.Runtime.Serialization.SerializationException: Unable to find assembly ‘MPPBusinessObjects, Version=1.0.4.3, Culture=neutral, PublicKeyToken=a7ca4f60ea1fbe70’.
Here’s the method that is deserializing:
public static List BinaryDeserialize(byte[] buffer) {
BinaryFormatter formatter = new BinaryFormatter();
using (MemoryStream stream = new MemoryStream(buffer))
{
// To prevent errors serializing between version number differences (e.g. Version 1 serializes, and Version 2 deserializes)
formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder();
return (List) formatter.Deserialize(stream);
}
}
I mimic your example of the overload as follows:
public override Type BindToType(string assemblyName, string typeName)
{
Type typeToDeserialize = null;
String currentAssembly = Assembly.GetExecutingAssembly().FullName;
// In this case we are always using the current assembly
if (assemblyName.Contains(“mscorlib”) && typeName.Contains(“System.Collections.Generic.List`1[[MPPBusinessObjects.TokenRolesSettings”))
{
String newTypeName = “System.Collections.Generic.List1[[MPPBusinessObjects.TokenRolesSettings]]”;
typeName = newTypeName;
}
else
{
// In this case we are always using the current assembly
assemblyName = currentAssembly;
}
// Get the type using the typeName and assemblyName
typeToDeserialize = Type.GetType(String.Format(“{0}, {1}”,
typeName, assemblyName));
return typeToDeserialize;
}
Things to note:
This bind overload when debugging is hit twice.
The return to this overload is always null.
The initial assemblyname passed is refering to the original assembly, which again is not referenced in my project.
When debugging, here are what values of the parameters are at first pass:
assemblyname = mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
typename = System.Collections.Generic.List`1[[MPPBusinessObjects.TokenRolesSettings, MPPBusinessObjects, Version=1.0.4.3, Culture=neutral, PublicKeyToken=a7ca4f60ea1fbe70]]
Here are the parameters at second pass of the debugging:
assemblyname = MPPBusinessObjects, Version=1.0.4.3, Culture=neutral, PublicKeyToken=a7ca4f60ea1fbe70
typename = MPPBusinessObjects.TokenRolesSettings
I’m not sure why its doing two passes, but my guess is the binary data itself causing this.
Here is the structured class that is both the application that is serializing and what I want to deserialize:
public sealed class TokenRolesSettings
{
public TokenRolesSettings()
{
}
public int RoleID { get; set; }
public string RoleName { get; set; }
public bool Default { get; set; }
public bool Enabled { get; set; }
}
Any thoughts on this?
Thanks.