Friday, April 8, 2011

Problems loading assembly dependencies dynamically at run-time

Hi,

let me try to explain my problem. I'm currently trying to develop a small "plugin-framework" written in .Net (mainly for experimenting a bit). So the idea is to have a main application to which "plugins" can be added by deploying dlls in a specific folder "plugins" of the main application. Everything works fine, the plugins are instantiated properly, however now I'm facing a problem. I have deployed now a plugin "X" that uses additional 3rd-party plugins and so now I have the problem that these additional 3rd-party plugins required by "X" are not found at run-time. My idea is therefore now to add an additional directory "dependencies" where I also deploy all of the needed plugins.

So my first question: How can I load the assemblies into the application domain (given that I know the path to them) s.t. they can be used by my application?

I tried to approach that by doing something like:

AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

System.Reflection.Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    //find the path to the assembly and then load and return it by
    //return Assembly.Load("pathToDependencies/failedAssembly.dll");
}

The problem is that this event-handler now gets activated having the "Presentation.Zune.dll" in the args variable (I'm using a WPF app). It seems as if that assembly failed loading but the actual problem is another dll.

Can someone suggest me a better way to solve my problem? I hope that I was able to explain my situation sufficiently, otherwise just ask for further clarification.

Thanks, Juri

From stackoverflow
  • The AssemblyResolve event happens when the framework tries to load an assembly, and fails.

    What this means is that if it's giving you Presentation.Zune.dll in the args, that the framework can't find that assembly, and this is your chance to intercept it and do other things, like load it from a directory that the framework might not know about - eg your plugins\dependencies folder...

    Off the top of my head, I'd try something like this:

    Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
    {
        if( File.Exists(".\\Plugins\\"+args.Name) )  // it's a plugin
            return Assembly.Load(".\\Plugins\\"+args.Name);
        else if( File.Exists(".\\Plugins\\Dependencies\\"+args.Name) )  // it's a dependency OF a plugin
            return Assembly.Load(".\\Plugins\\Dependencies\\"+args.Name);
        else
            throw new Exception();
    }
    
    Jakob Christensen : Is this correct? I believe that AssemblyResolve is called only when the runtime fails to resolve an assembly.
  • You can set the probing path of the runtime so it can find the assemblies. Set the probing element in your apps config file.

    <configuration>
      <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
          <probing privatePath="plugins;dependencies"/>
        </assemblyBinding>
      </runtime>
    </configuration>
    
  • BTW: You can speed up loading assemblies considerably if you cache your assemblies that you already resolved in a Dictionary. If A depends on B, C and B depends on C and you load A, AssemblyResolve will be called twice for C, and loading the assembly only once is faster :)

    (I am not sure if it is always the case that AssemblyResolve is called more than once, but I noticed it when debugging a project once. And it does not hurt to cache the assemblies...)

0 comments:

Post a Comment