Welcome to EMC Consulting Blogs Sign in | Join | Help

mister mann

every day is just a game of give and take... [ follow me on twitter: @mark_mann ]

Loading and resolving assemblies

We've been writing a tool on our project that requires late-binding to classes/types in our service layer. It's typical plugin style coding, similar to how much of the various Enterprise Library stacks are meant to be loaded when needed.

One of the questions raised was what happens if the assemblies containing our classes/types are not in the execution path? This usually occurs when the requested assembly has a dependency on another assembly.

After some playing about with explicitly loading assemblies into the current AppDomain, I found a couple of useful events; AssemblyLoad and AssemblyResolve. The real saviour is AssemblyResolve - hook this up up in your plugin loading code and they it gives you a chance to handle when the requested assembly fails to load. Both these events hang off the AppDomain.CurrentDomain and so catch any assembly load/failure that occurs within execution scope.

In the sample below, I've assumed that we know what the path to the assemblies are by setting a constant (if you don't know this you'll either have to set some predetermined hint paths or ask for user input). It's here that the AssemblyResolve event can piece together the missing assembly path, load it and continue.

   1: const string assemblyPath = @"C:\LoadingAssemblies\Model\ParentAssembly\bin\Debug\";
   2:  
   3: private void LoadTest()
   4: {
   5:  
   6:     AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
   7:     AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
   8:  
   9:     Assembly pluginAssembly = Assembly.LoadFrom(string.Format(@"{0}\{1}", assemblyPath, "PluginAssembly.dll"));
  10:  
  11:     Type myType = pluginAssembly.GetType("PluginAssembly.ParentClass");
  12:     if (myType != null)
  13:         Debug.WriteLine(string.Format("{0} -- {1}", myType.FullName, myType.AssemblyQualifiedName));
  14:  
  15:     //fails since it is not defined in this assembly and does not have a full assembly definition
  16:     myType = Type.GetType("ChildAssembly.ChildClass");
  17:     if (myType != null)
  18:         Debug.WriteLine(string.Format("{0} -- {1}", myType.FullName, myType.AssemblyQualifiedName));
  19:  
  20:     //succeeds since we have the full definition - will fire the AssemblyResolve event which points to the assembly location
  21:     myType = Type.GetType("ChildAssembly.ChildClass, ChildAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
  22:     if (myType != null)
  23:         Debug.WriteLine(string.Format("{0} -- {1}", myType.FullName, myType.AssemblyQualifiedName));
  24:  
  25:     //fails since it is not defined in this assembly and does not have a full assembly definition
  26:     myType = Type.GetType("SubAssembly.SubClass");
  27:     if (myType != null)
  28:         Debug.WriteLine(string.Format("{0} -- {1}", myType.FullName, myType.AssemblyQualifiedName));
  29:  
  30:     //succeeds since we have the full definition - since the assembly is in the app path it will automatically load
  31:     myType = Type.GetType("SubAssembly.SubClass, SubAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
  32:     if (myType != null)
  33:         Debug.WriteLine(string.Format("{0} -- {1}", myType.FullName, myType.AssemblyQualifiedName));
  34:  
  35:     //fails because we need to fully qualify the namespace of the class
  36:     myType = Type.GetType("SubClass, SubAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
  37:     if (myType != null)
  38:         Debug.WriteLine(string.Format("{0} -- {1}", myType.FullName, myType.AssemblyQualifiedName));
  39:  
  40: }
  41:  
  42: static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
  43: {
  44:     Debug.WriteLine(string.Format("CurrentDomain_AssemblyResolve reports that '{0}' was unresolved.", args.Name));
  45:  
  46:     // args.Name == "ChildAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
  47:     string[] parts = args.Name.Split(',');
  48:     if (parts.Length > 0)
  49:     {
  50:         Assembly assembly = Assembly.LoadFrom(string.Format(@"{0}\{1}.dll", assemblyPath, parts[0].Trim()));
  51:         return assembly;
  52:     }
  53:  
  54:     return null;
  55: }
  56:  
  57: static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
  58: {
  59:     Debug.WriteLine(string.Format("CurrentDomain_AssemblyLoad reports that '{0}' was loaded.", args.LoadedAssembly.FullName));
  60: }
Published 01 September 2008 11:16 by Mark.Mann
Filed under:

Comments

No Comments
Anonymous comments are disabled
Powered by Community Server (Personal Edition), by Telligent Systems