How to get MEF to instantiate and compose my Type

Aug 27, 2010 at 8:55 PM
Edited Aug 27, 2010 at 9:00 PM

I am porting a plug-in based architecture to MEF.

In code, I obtain a specific Type (one which implements IPlugin and is derrived from UIElement) which I am supposed to instantiate and then display in my UI (in a new tab).

I know how to instantiate the plug-in instance and then compose it using my MEF container.

I should probably be happy with that and just let it go, but it bothers me that I can't get to the [Import] members from the constructor (since it is called before the compose).  I'd like to be able to give my plug-in developers the option of using an [ImportingConstructor].

It seems like MEF has all the machinery to take my type and make an instance, passing in all the constructor parameters to the [ImportingConstructor], but I can't understand how to get MEF to perform the task.

Thanks in advance for your input :)

Aug 27, 2010 at 9:18 PM

Here's a work-around from another post:

The purpose of IPartImportsSatisfiedNotification is to notify the part after it has had it's imports satisfied. Whenever a part is created by or satisfied by MEF and it implements this interface, MEF will callback into the object and you can preform some kind of initialization code. I have a couple scenarios where I can not use constructor injection or the part satisfies some other import, then, this notification is ideal for that

Some description info on IPartImportsSatisfiedNotification (look near the end)

Still not completely satisfying :)

Aug 29, 2010 at 10:56 AM
Edited Aug 29, 2010 at 12:14 PM

Based on the following plug-in invocation strategy, you have three options as a plug-in developer:

  1. Get full MEF support from the framework (you must Export yourself)
  2. Ignore MEF in your plug-in (you do not have to Export yourself)
  3. Get partial MEF support from the framework (do not have to Export yourself)
  • No [ImportingConstructor]

This seems to do what I want :)

// set up the container
AggregateCatalog aggCat = new AggregateCatalog();
CompositionContainer container = new CompositionContainer(aggCat);
aggCat.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
// add additional assemblies here that [Export] services provided to the plugins
// compose the host
container.ComposeParts(this);

// later on in the plugin launcher ...
// add the plugin type that we want to launch to the aggregate catalog
Type type = typeof(MyPluginType); // I don't actually know the type until runtime, but for illustration... :)
TypeCatalog tc = new TypeCatalog(type);
aggCat.Catalogs.Add(tc);
IEnumerable<Export> matching = container.GetExports(
new ContractBasedImportDefinition(
AttributedModelServices.GetContractName(type),
AttributedModelServices.GetTypeIdentity(type),
null,
ImportCardinality.ZeroOrMore,
false,
false,
CreationPolicy.Any));
 
object oPlugin = null;
if (matching.Count() < 1) { oPlugin = Activator.CreateInstance(type); container.ComposeParts(oPlugin); } else if (matching.Count() == 1) { oPlugin = matching.Single().Value; }
UIElement uiPlugin = oPlugin as UIElement;
if (uiPlugin != null)
ShowPlugin(Guid.NewGuid(), "MyPlugin", uiPlugin);

Aug 29, 2010 at 7:20 PM

Hi Michael

Looking through your code it seems like you are doing a lot of unnecessary work.

If I understand your code what you are trying to do is dynamically retrieve from the container based on a runtime type. Is that correct?

 

Aug 30, 2010 at 9:34 AM

Hi Glenn,

Correct.  The class and assembly (strings) for the type are discovered at runtime from a config database.

Here are some other goals:

  • The type may or may not have MEF attributes.
  • The type may or may not export itself via MEF [Export].  Even if it doesn't export itself, it may have other MEF exports and imports.
  • The discovered types (and their assemblies and referenced assemblies) in this system are loaded "just in time", they are extremely Lazy and the assemblies don't even get loaded into MEF until the time of their first launch.
  • It would be nice if the type could mark a constructor as [ImportingConstructor]