Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MEF's CompositionContainer.ComposeParts -- loading whatever can be resolved, and ignoring errors

The biggest problem I'm having so far with MEF is that when I compose parts in my plugin loader wrapper, loading completely bails when it finds an import resolution problem with one of the assemblies. Ideally, I'd like ComposeParts to exhibit some sort of "ignore and continue" behavior, because the ideal user experience would entail loading as many plugins as possible, and simply logging an event when a specific plugin fails to load. I haven't been able to find information about this in the documentation anywhere.

If you have any other suggestions for how to solve this, I'm listening!

like image 348
Dave Avatar asked Jun 04 '10 13:06

Dave


2 Answers

Wim's example has the basic ideas but instead of pulling on the container directly I would suggest you do a Lazy ImportMany like such:

[Export]
public class MyApplication
{
   [ImportMany(typeof(IPlugin))]
   public IEnumerable<Lazy<IPlugin>> Plugins { get; set; }
}

Then you can initialize the plugins one by one and catch any errors from them like:

void InitializePlugins()
{
   foreach (Lazy<IPlugin> plugin in Plugins)
   {
       try
       {
          plugin.Value.Initialize();
       }
       catch (CompositionException e)
       {
          // Handle the error accordingly
       }
   }   
}

The actual plugin will not be created until you pull on .Value the first time and that is when errors will occur if the plugin has bugs in the constructor or property setters of the imports. Also note that I'm catch CompositionException which is what will come out of the .Value call if the plugin does something wrong.

like image 128
Wes Haggard Avatar answered Oct 07 '22 16:10

Wes Haggard


You can use the AllowDefault parameter. Setting it to true on an import will cause the dependency to be null if no available part can satisfy the import.

public class MyComponent
{
    [Import(AllowDefault=true)]
    public IMyDependency MyDependency { get; set; }
}

To load all available plugins but ignore those which cannot be loaded because of missing parts, [ImportMany] will already do what you want by default:

[Export]
public class MyApplication
{
   [ImportMany(typeof(IPlugin))]
   public IEnumerable<IPlugin> Plugins { get; set; }
}

Note that the above techniques only eliminate composition errors that are caused by missing parts. If a part and its imports are actually available, but it then throws an unexpected exceptions when the constructor is called, then you will still get an exception. To ignore such problems which are not composition-related, you can invoke the container directly like this:

IEnumerable<IPlugin> GetPluginsFromContainer(CompositionContainer container)
{
   foreach (Lazy<IPlugin> pluginExport in container.GetExports<IPlugin>())
   {
       try
       {
          yield return pluginExport.Value;
       }
       catch (Exception e)
       {
          // report failure to instantiate plugin somewhere
       }
   }   
}
like image 39
Wim Coenen Avatar answered Oct 07 '22 14:10

Wim Coenen