views:

43

answers:

1

Scenario: I am using Managed Extensibility Framework to load plugins (exports) at runtime based on an interface contract defined in a separate dll. In my Visual Studio solution, I have 3 different projects: The host application, a class library (defining the interface - "IPlugin") and another class library implementing the interface (the export - "MyPlugin.dll").

The host looks for exports in its own root directory, so during testing, I build the whole solution and copy Plugin.dll from the Plugin class library bin/release folder to the host's debug directory so that the host's DirectoryCatalog will find it and be able to add it to the CompositionContainer. Plugin.dll is not automatically copied after each rebuild, so I do that manually each time I've made changes to the contract/implementation.

However, a couple of times I've run the host application without having copied (an updated) Plugin.dll first, and it has thrown an exception during composition:

Unable to load one or more of the requested types. Retrieve the LoaderExceptions for more information

This is of course due to the fact that the Plugin.dll it's trying to import from implements a different version of IPlugin, where the property/method signatures don't match. Although it's easy to avoid this in a controlled and monitored environment, by simply avoiding (duh) obsolete IPlugin implementations in the plugin folder, I cannot rely on such assumptions in the production environment, where legacy plugins could be encountered.

The problem is that this exception effectively botches the whole Compose action and no exports are imported. I would have preferred that the mismatching IPlugin implementations are simply ignored, so that other exports in the catalog(s), implementing the correct version of IPlugin, are still imported.

Is there a way to accomplish this? I'm thinking either of several potential options:

  • There is a flag to set on the CompositionContainer ("ignore failing imports") prior to or when calling Compose
  • There is a similar flag to specify on the <ImportMany()> attribute
  • There is a way to "hook" on to the iteration process underlying Compose(), and be able to deal with each (failed) import individually
  • Using strong name signing to somehow only look for imports implementing the current version of IPlugin

Ideas?

A: 

I have also run into a similar problem.

If you are sure that you want to ignore such "bad" assemblies, then the solution is to call AssemblyCatalog.Parts right after creating each assembly catalog. This will trigger the ReflectionTypeLoadException which you mention. You then have a chance to catch the exception and ignore the bad assembly.

When you have created AssemblyCatalog objects for all the "good" assemblies, you can aggregate them in an AggregateCatalog and pass that to the CompositionContainer constructor.

Wim Coenen
I'll have a look. And yes, I am sure I want to ignore them - I can't utilize them anyway, and they mess up for all the ones I DO want, so that's a no-brainer, no? :)
d7samurai
@d7samurai: Some people will end up here by googling the exception message. I just wanted to emphasize for them that this doesn't fix the underlying cause of the error. Also, introducing silent failure (aka "on error resume next" behavior) is not exactly a no-brainer; it can vastly complicate bug detection and diagnosis. I recommend to at least give some indication to the user that something bad happened.
Wim Coenen
Of course. By "ignoring" I mean "moving on without letting it stop the whole import process". There is no user, as this is a Windows Service, but everything this (bootstrapper) does is logged, both normal operations and exceptions (including dumping all exceptions in "LoaderExceptions" - when that is applicable).
d7samurai
Also, in this case, the purpose of the import is to detect all available (engine) plugins, to subsequently pick the most eligible one. And there will always be one, since a baseline engine is embedded in the bootstrapper itself, in case no better/newer/compatible engines are found externally.
d7samurai