Dynamically resolve assemblies at runtime

I came across this recently. I must admit I’ve not personally had a need for it, but it’s an interesting “trick”, so I wrote a little reusable framework around it.

So let’s say you deploy an application without all of it’s dependencies as part of the expected dependency resolution path. For example, in this case I’ve embedded all the assemblies as resources into the EXE so I can deploy a single file, but the technique should allow us to also resolve the dependencies via some other technique, such as from a remote location.

Create an EXE application and add the DLL(s) or EXE(s) and optionally their PDB(s) to the application as embedded resources. As your application depends on one (or more) of these assemblies then one (or more) of them will also be referenced. Mark the referenced assemblies which you’ve embedded as Copy Local=false (obviously we’re not talking about the GAC assemblies here) so they’re not copied to the output folder, obvious if you run the application now it will fail to locate these required assembles as we’ve embedded them as resources.

So, when the application starts up it attempts to resolve any missing dependencies and if it has any problem resolving the dependencies it fires the AssemblyResolve event on the AppDomain.CurrentDomain, it’s at this point that we want to dynamically load the assemblies ourselves into the app domain.

What we need to do is attach an event handler to the AppDomain.CurrentDomain.AssemblyResolve, as per

AppDomain.CurrentDomain.AssemblyResolve +=
       DynamicAssemblyResolver.Resolve(args, new ResourceAssemblyResolver());

I now try to resolve the missing dependency using an IAssemblyResolver (in this instance that is the ResourceAssemblyResolver class).

Below is a helper class named DynamicAssemblyResolver which simple (potentially) loops through any supplied IAssemblyResolver implementations passing the AssemblyName that we’re trying to resolve

public class DynamicAssemblyResolver
{
   public static Assembly Resolve(ResolveEventArgs e, 
            IAssemblyResolver resolver, params IAssemblyResolver[] alternateResolvers)
   {
      return Resolve(e.Name, resolver, alternateResolvers);
   }

   public static Assembly Resolve(string nameToResolve, 
            IAssemblyResolver resolver, params IAssemblyResolver[] alternateResolvers)
   {
      return Resolve(new AssemblyName(nameToResolve), resolver, alternateResolvers);
   }

   public static Assembly Resolve(AssemblyName assemblyToResolve, 
            IAssemblyResolver resolver, params IAssemblyResolver[] alternateResolvers)
   {
      Assembly assembly = resolver.Resolve(assemblyToResolve.Name);
      if (assembly != null)
      {
         return assembly;
      }

      if (alternateResolvers != null)
      {
         foreach (IAssemblyResolver alternateResolver in alternateResolvers)
         {
            assembly = alternateResolver.Resolve(assemblyToResolve.Name);
            if (assembly != null)
            {
               return assembly;
            }
         }
      }
      return null;
   }
}

The interface for a resolver class looks like this

public interface IAssemblyResolver
{
   Assembly Resolve(string assemblyName);
}

and finally the implementation that resolves dependencies using the files embedded within the resource looks like

public class ResourceAssemblyResolver : IAssemblyResolver
{
   public Assembly Resolve(string assemblyName)
   {
      Assembly executingAssembly = Assembly.GetExecutingAssembly();
      return (from name in executingAssembly.GetManifestResourceNames() 
		where name.Contains(assemblyName) 
		select Resolve(executingAssembly, name)).FirstOrDefault();
   }

   private static Assembly Resolve(Assembly executingAssembly, string assemblyName)
   {
      try
      {
         using(Stream manifestResourceStream = 
                      executingAssembly.GetManifestResourceStream(assemblyName))
         {
            if (manifestResourceStream == null)
   	    {
               return null;
            }

            string pdb = Path.ChangeExtension(assemblyName, ".pdb");
	    using (Stream pdbResourceStream =                
                     executingAssembly.GetManifestResourceStream(pdb))
	    {
	       return Assembly.Load(Read(manifestResourceStream),
			(pdbResourceStream != null) ?
			Read(pdbResourceStream) : null, 								                
                        SecurityContextSource.CurrentAppDomain);
 	    }
         }
      }
      catch (Exception)
      {
         throw;
      }
   }

   private static byte[] Read(Stream stream)
   {
      byte[] array = new byte[stream.Length];
      stream.Read(array, 0, array.Length);
      return array;
   }
}

The above code tries to match the supplied assembly name with the resources found embedded in the calling assembly.

Note: A failing of the above approach is that if more than one file exists with the same name, i.e. MyLib.dll existing in multiple resources but in different folders (obviously), then the first one only is ever resolved.