Why can't I deserialize my Type?

I'm playing with Types, Interfaces and Serialization and have come up with a situation that I think should work but does not.

I have an Employee Type that implements the IEmployee Interface. I construct an Employee instance and serialize the instance to a file.(App1)

In another application(App2) I then try to read the serialized instance of the Employee Type from the file.

The catch is that I compile App2 WITHOUT any reference to the Employee Type, only to the Interface IEmployee. I am doing this so that App2 can use the IEmployee Interface without knowledge of the actual implementation as found in the Employee Type. App2 compiles cleanly.

Knowing that I need access to the employee class before I can deserialize the class, but wanting to leave that decision (which implementation to use) till run time, I simply load the assembly that I want (contains the Employee class) at runtime using

RemoteAssembly = AppDomain.CurrentDomain.Load(AssName);

where AssName is a library that contains the implementation of the Employee type. The same one that was used to serialize the Employee. I confirm that the assembly is loaded by printing out all kinds of information about the ApplicationDomain and all the assemblies that are loaded. A sample of what I log for loaded assemblies is ......

<SNIP>
Assembly FullName: SerializationTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Assembly Codebase: file:///C:/tmp/SerializationTest.EXE
Escaped Codebase : file:///C:/tmp/SerializationTest.EXE
</SNIP>

Then I make a call to deserialise the Serialized Employee instance using:

string empFileBinary = "C:\\tmp\\EmployeeInfo.ser";//file that was written by App1
IEmployee emp = null;
Stream stream = File.Open(empFileBinary, FileMode.Open);
BinaryFormatter bFormatter = new BinaryFormatter();
try
{
emp = (IEmployee)bFormatter.Deserialize(stream);
}
catch (System.Runtime.Serialization.SerializationException se)
{

Console.WriteLine("SerializationException raised : " + se.Message);
}

Unexpectedly I get an SerializationException with the message:

SerializationException raised : Unable to find assembly 'SerializationTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

From this I gather that the Deserialization is working, knows that it has to look for an Employee type, and is now hunting for the assembly that has the Employee Type in it. What I don't understand is that the assembly is already loaded and I can print/log information about the assembly from this Application (App2) but when it looks for the Employee Type ... it doesn't find it in the loaded assembly! I feel I'm missing something fundamental that only I would miss :( :) Anyone have any ideas as to why App2 is not finding an assembly that has already been loaded?

Thanks!!!!!

Cam

[2914 byte] By [camr] at [2007-12-26]
# 1
Hi Cam,

could it be that you have several differnt application domains inside your App2 where the deserialisation takes place in an appdomain that has not loaded the assembly yet? As far as I know the assembly should be loaded by the serialisation infrastructure automatically. But it seems it was not able to load the assembly for some reason. To go further you should check out the fusion log (fuslogvw.exe) where you can check in which paths your dll was searched (you have to set the registry key HKLM\Software\Microsoft\Fusion\LogFailues DWORD value to 1 or use the fuslogvw tool and set in settings "Log bind failures to disk" ).
Hope this helps a bit.
You can find more about fusion log can with google: assembly enable fusion log

Yours,
Alois Kraus

Alois at 2007-9-4 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 2
Thanks Alois

I did check the fusion logs and it is only checking the the directories directly below the dir that the App2 is running out of. This is expected and I expected that it NOT be able to find the SerializationTest.EXE since I purposefully didn't put it in the path. What I was expecting is that if it wasn't in the path but I explicitly loaded the assembly, that App2 would find it already loaded. Is this assumption wrong somehow?

I wrote a utility class that allows me to output a whole bunch of information about the application domain and the assemblies. (Included at the bottom if this helps someone else). What I see is that when I DumpAssemblies() I get a list of 12 assemblies.(for the Current Application Domain) This includes the system assemblies etc, but I also see that the SerializationTest is loaded in the current Application domain, so my expectation is that the deserialization would find the Employee class in that assembly since it is already loaded.

I just think I must be missing something fundementatal because it makes logical sense - to me anyway - that this should work quite easily.

Here is the code that I use to dump information about the Appdomain and the assemblies.

using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

namespace ApplicationUtilities
{
public class ApplicationInformation
{
public static void AssemblyInformation(Assembly assem)
{
Console.WriteLine("Assembly FullName : " + assem.FullName);
Console.WriteLine("Assembly Codebase : " + assem.CodeBase);
Console.WriteLine("Escaped Codebase : " + assem.EscapedCodeBase);
Console.WriteLine("Entry Point : " + assem.EntryPoint);
Console.WriteLine("Host Context : " + assem.HostContext);
Console.WriteLine("Assembly Location : " + assem.Location);
Console.WriteLine("Loaded for Reflect: " + assem.ReflectionOnly);
Console.WriteLine("Assembly ToString : " + assem.ToString());
}

/** Get and display information about the current Application Domain
*
*/
public static void AppdomainInformation()
{
AppDomain appDomain = AppDomain.CurrentDomain;
Console.WriteLine("Application Identity :" + appDomain.ApplicationIdentity);
Console.WriteLine("Appdomain Activation Context:" + appDomain.ActivationContext);
Console.WriteLine("Appdomain Base Directory :" + appDomain.BaseDirectory);
Console.WriteLine("Appdomain Dynamic Directory :" + appDomain.DynamicDirectory);
Console.WriteLine("Appdomain Friendly Name :" + appDomain.FriendlyName);
Console.WriteLine("Relative Search Path :" + appDomain.RelativeSearchPath);
AppDomainSetup setUp = appDomain.SetupInformation;
Console.WriteLine("S E T U P I N F O R M A T I O N ");
Console.WriteLine("Activation Arguments :" + setUp.ActivationArguments);
Console.WriteLine("Application Base :" + setUp.ApplicationBase);
Console.WriteLine("Application Name :" + setUp.ApplicationName);
Console.WriteLine("Configuration File :" + setUp.ConfigurationFile);
Console.WriteLine("Dissallow base probing? :" + setUp.DisallowApplicationBaseProbing);
Console.WriteLine("Dissallow binding redirects?:" + setUp.DisallowBindingRedirects);
Console.WriteLine("Dissallow Code Download? :" + setUp.DisallowCodeDownload);
Console.WriteLine("Dynamic Base directory :" + setUp.DynamicBase);
Console.WriteLine();
}
public static void DumpAllAssemblies()
{
Assembly[] assems = AppDomain.CurrentDomain.GetAssemblies();
Console.WriteLine("******* A P P L I C A T I O N D O M A I N A S S E M B L I E S *******");
Console.WriteLine("There are " + assems.GetLength(0) + " Assemblies loaded in this AppDomain");
Console.WriteLine(AppDomain.CurrentDomain.FriendlyName);
for (int i = 0; i < assems.GetLength(0); i++)
{
Console.WriteLine("[" + (i + 1) + "]");
ApplicationInformation.AssemblyInformation(assemsIdea);
Console.WriteLine();

}
Console.WriteLine("******* A P P L I C A T I O N D O M A I N A S S E M B L I E S E N D *******");
Console.WriteLine();

}
}
}

camr at 2007-9-4 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 3
Just for the heck of it, this is what I get when I dump the assemblies from the current application domain just before I try to deserialize the Employee. Note that Assembly 12 is the SerializationTest assembly that has the Employee class in it. Also note that it is in a directory that is not related to the directory that the App2 is being run out of .

Assembly 10 is the Deserialize appliction assembly (App2 in these posts) and Assembly 11 is the ApplicationInformation assembly that I use to output the appdomain and assembly information.

See the code snippet in the previous post for how this is output.

Thanks

Cam

******* A P P L I C A T I O N D O M A I N A S S E M B L I E S *******
There are 12 Assemblies loaded in this AppDomain
Deserialize2.vshost.exe
[1]
Assembly FullName: mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b7
7a5c561934e089
Assembly Codebase: file:///C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/mscorli
b.dll
Escaped Codebase : file:///C:/WINDOWS/Microsoft.NET/Framework/v2.0.50727/mscorli
b.dll
Entry Point :
Host Context : 0
Assembly Location: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\mscorlib.dll
Loaded for Reflec: False
Assembly ToString: mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b7
7a5c561934e089

[2]
Assembly FullName: Microsoft.VisualStudio.HostingProcess.Utilities, Version=8.0.
0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Assembly Codebase: file:///C:/WINDOWS/assembly/GAC_MSIL/Microsoft.VisualStudio.H
ostingProcess.Utilities/8.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.Hosting
Process.Utilities.dll
Escaped Codebase : file:///C:/WINDOWS/assembly/GAC_MSIL/Microsoft.VisualStudio.H
ostingProcess.Utilities/8.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.Hosting
Process.Utilities.dll
Entry Point :
Host Context : 0
Assembly Location: C:\WINDOWS\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingPr
ocess.Utilities\8.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingProcess.
Utilities.dll
Loaded for Reflec: False
Assembly ToString: Microsoft.VisualStudio.HostingProcess.Utilities, Version=8.0.
0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

[3]
Assembly FullName: System.Windows.Forms, Version=2.0.0.0, Culture=neutral, Publi
cKeyToken=b77a5c561934e089
Assembly Codebase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Windows.Forms/2.0
.0.0__b77a5c561934e089/System.Windows.Forms.dll
Escaped Codebase : file:///C:/WINDOWS/assembly/GAC_MSIL/System.Windows.Forms/2.0
.0.0__b77a5c561934e089/System.Windows.Forms.dll
Entry Point :
Host Context : 0
Assembly Location: C:\WINDOWS\assembly\GAC_MSIL\System.Windows.Forms\2.0.0.0__b7
7a5c561934e089\System.Windows.Forms.dll
Loaded for Reflec: False
Assembly ToString: System.Windows.Forms, Version=2.0.0.0, Culture=neutral, Publi
cKeyToken=b77a5c561934e089

[4]
Assembly FullName: System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a
5c561934e089
Assembly Codebase: file:///C:/WINDOWS/assembly/GAC_MSIL/System/2.0.0.0__b77a5c56
1934e089/System.dll
Escaped Codebase : file:///C:/WINDOWS/assembly/GAC_MSIL/System/2.0.0.0__b77a5c56
1934e089/System.dll
Entry Point :
Host Context : 0
Assembly Location: C:\WINDOWS\assembly\GAC_MSIL\System\2.0.0.0__b77a5c561934e089
\System.dll
Loaded for Reflec: False
Assembly ToString: System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a
5c561934e089

[5]
Assembly FullName: System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyTo
ken=b03f5f7f11d50a3a
Assembly Codebase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Drawing/2.0.0.0__
b03f5f7f11d50a3a/System.Drawing.dll
Escaped Codebase : file:///C:/WINDOWS/assembly/GAC_MSIL/System.Drawing/2.0.0.0__
b03f5f7f11d50a3a/System.Drawing.dll
Entry Point :
Host Context : 0
Assembly Location: C:\WINDOWS\assembly\GAC_MSIL\System.Drawing\2.0.0.0__b03f5f7f
11d50a3a\System.Drawing.dll
Loaded for Reflec: False
Assembly ToString: System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyTo
ken=b03f5f7f11d50a3a

Devil
Assembly FullName: Microsoft.VisualStudio.HostingProcess.Utilities.Sync, Version
=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
Assembly Codebase: file:///C:/WINDOWS/assembly/GAC_MSIL/Microsoft.VisualStudio.H
ostingProcess.Utilities.Sync/8.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.Ho
stingProcess.Utilities.Sync.dll
Escaped Codebase : file:///C:/WINDOWS/assembly/GAC_MSIL/Microsoft.VisualStudio.H
ostingProcess.Utilities.Sync/8.0.0.0__b03f5f7f11d50a3a/Microsoft.VisualStudio.Ho
stingProcess.Utilities.Sync.dll
Entry Point :
Host Context : 0
Assembly Location: C:\WINDOWS\assembly\GAC_MSIL\Microsoft.VisualStudio.HostingPr
ocess.Utilities.Sync\8.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.HostingPro
cess.Utilities.Sync.dll
Loaded for Reflec: False
Assembly ToString: Microsoft.VisualStudio.HostingProcess.Utilities.Sync, Version
=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a

[7]
Assembly FullName: vshost, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f
5f7f11d50a3a
Assembly Codebase: file:///C:/Documents and Settings/Cameron Roe/My Documents/Vi
sual Studio 2005/Projects/Deserialize2/Deserialize2/bin/Debug/Deserialize2.vshos
t.exe
Escaped Codebase : file:///C:/Documents%20and%20Settings/Cameron%20Roe/My%20Docu
ments/Visual%20Studio%202005/Projects/Deserialize2/Deserialize2/bin/Debug/Deseri
alize2.vshost.exe
Entry Point : Void Main()
Host Context : 0
Assembly Location: C:\Documents and Settings\Cameron Roe\My Documents\Visual Stu
dio 2005\Projects\Deserialize2\Deserialize2\bin\Debug\Deserialize2.vshost.exe
Loaded for Reflec: False
Assembly ToString: vshost, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f
5f7f11d50a3a

Music
Assembly FullName: System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken
=b77a5c561934e089
Assembly Codebase: file:///C:/WINDOWS/assembly/GAC_32/System.Data/2.0.0.0__b77a5
c561934e089/System.Data.dll
Escaped Codebase : file:///C:/WINDOWS/assembly/GAC_32/System.Data/2.0.0.0__b77a5
c561934e089/System.Data.dll
Entry Point :
Host Context : 0
Assembly Location: C:\WINDOWS\assembly\GAC_32\System.Data\2.0.0.0__b77a5c561934e
089\System.Data.dll
Loaded for Reflec: False
Assembly ToString: System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken
=b77a5c561934e089

[9]
Assembly FullName: System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=
b77a5c561934e089
Assembly Codebase: file:///C:/WINDOWS/assembly/GAC_MSIL/System.Xml/2.0.0.0__b77a
5c561934e089/System.Xml.dll
Escaped Codebase : file:///C:/WINDOWS/assembly/GAC_MSIL/System.Xml/2.0.0.0__b77a
5c561934e089/System.Xml.dll
Entry Point :
Host Context : 0
Assembly Location: C:\WINDOWS\assembly\GAC_MSIL\System.Xml\2.0.0.0__b77a5c561934
e089\System.Xml.dll
Loaded for Reflec: False
Assembly ToString: System.Xml, Version=2.0.0.0, Culture=neutral, PublicKeyToken=
b77a5c561934e089

[10]
Assembly FullName: Deserialize2, Version=1.0.0.0, Culture=neutral, PublicKeyToke
n=null
Assembly Codebase: file:///C:/Documents and Settings/Cameron Roe/My Documents/Vi
sual Studio 2005/Projects/Deserialize2/Deserialize2/bin/Debug/Deserialize2.EXE
Escaped Codebase : file:///C:/Documents%20and%20Settings/Cameron%20Roe/My%20Docu
ments/Visual%20Studio%202005/Projects/Deserialize2/Deserialize2/bin/Debug/Deseri
alize2.EXE
Entry Point : Void Main(System.String[])
Host Context : 0
Assembly Location: C:\Documents and Settings\Cameron Roe\My Documents\Visual Stu
dio 2005\Projects\Deserialize2\Deserialize2\bin\Debug\Deserialize2.exe
Loaded for Reflec: False
Assembly ToString: Deserialize2, Version=1.0.0.0, Culture=neutral, PublicKeyToke
n=null

[11]
Assembly FullName: ApplicationUtilities, Version=1.0.0.0, Culture=neutral, Publi
cKeyToken=null
Assembly Codebase: file:///C:/Documents and Settings/Cameron Roe/My Documents/Vi
sual Studio 2005/Projects/Deserialize2/Deserialize2/bin/Debug/ApplicationUtiliti
es.DLL
Escaped Codebase : file:///C:/Documents%20and%20Settings/Cameron%20Roe/My%20Docu
ments/Visual%20Studio%202005/Projects/Deserialize2/Deserialize2/bin/Debug/Applic
ationUtilities.DLL
Entry Point :
Host Context : 0
Assembly Location: C:\Documents and Settings\Cameron Roe\My Documents\Visual Stu
dio 2005\Projects\Deserialize2\Deserialize2\bin\Debug\ApplicationUtilities.dll
Loaded for Reflec: False
Assembly ToString: ApplicationUtilities, Version=1.0.0.0, Culture=neutral, Publi
cKeyToken=null

[12]
Assembly FullName: SerializationTest, Version=1.0.0.0, Culture=neutral, PublicKe
yToken=null
Assembly Codebase: file:///C:/tmp/SerializationTest.EXE
Escaped Codebase : file:///C:/tmp/SerializationTest.EXE
Entry Point : Void Main(System.String[])
Host Context : 0
Assembly Location: C:\tmp\SerializationTest.exe
Loaded for Reflec: False
Assembly ToString: SerializationTest, Version=1.0.0.0, Culture=neutral, PublicKe
yToken=null

******* A P P L I C A T I O N D O M A I N A S S E M B L I E S E N D ******
*

SerializationException raised : Unable to find assembly 'SerializationTest, Vers
ion=1.0.0.0, Culture=neutral, PublicKeyToken=null'.

camr at 2007-9-4 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 4
Hi Cam,

you said that you did load the assembly from a location which is not in the probing path. You did load it thereform not with Assembly.Load but with LoadFile or LoadFrom. The loading of an assembly this way could make it necessary to releoad it because LoadFrom does different things and load the assembly in the LoadFrom context. I strongly suspect that you did load the assembly in a way which made the serialization infrastructure think that it has it to load again in the right load context.
A little more background what happens there is documented at my blog: http://geekswithblogs.net/akraus1/articles/74319.aspx

Yours,
Alois Kraus

Alois at 2007-9-4 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 5
Thanks Alois.

All this info is hugely helpful because I think you're right, the loader must think it has to load it again .... for some reason. BUT, I did do the load with the following line and did not use LoadFile or LoadFrom. I'm going to read the link you sent and see if I can glean some info from there. :)

Thanks Again

Cam

RemoteAssembly = AppDomain.CurrentDomain.Load(AssName);

camr at 2007-9-4 > top of Msdn Tech,.NET Development,Common Language Runtime...
# 6
I never did find out what exactly cause the difference but if I encounter a type which I did reference from my assembly it is loaded and the type system does bind this very assembly to this compile time type to it. If I load the same assembly again and create the same type via Activator.CreateInstance I am not able to cast this very same type to my type. It seems that once the type information has been loaded from one assembly you cannot cast it to the same type which does come from another assembly instance. This brings me back to my original suspect why you do not add the assembly to a location where it can be probed. You can set from your application base directory (where your exectuable is located) relative paths below this directory inside your App.Config.
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<probing privatePath="bin;bin2\subbin;bin3"/>
</assemblyBinding>
</runtime>
</configuration>
Yours,
Alois Kraus
Alois at 2007-9-4 > top of Msdn Tech,.NET Development,Common Language Runtime...

.NET Development

Site Classified