views:

327

answers:

1

I've had a few questions about MEF recently, but here's the big one -- is it really all-or-nothing, as it appears to be?

My basic application structure is simply an app, several shared libraries that are intended to be singletons, and several different plugins (which may implement different interfaces). The app loads the plugins, and both the app and all plugins need to access the shared libraries.

My first go at MEF was fairly successful, although I made some stupid mistakes along the way because I was trying so many different things, I just got confused at times. But in the end, last night I got my smallish test app running with MEF, some shared libraries, and one plugin.

Now I'm moving onto the target app, which I already described. And it's the multiple plugins part that has be a bit worried.

My existing application already supports multiple plugins with different interfaces by using Reflection. I need to be able to uniquely identify each plugin so that the user can select one and get the expected behavior exposed by that plugin. The problem is that I don't know how to do this yet... but that's the topic of a different question.

Ideally, I'd be able to take my existing plugin loader and use it as-is, while relying on MEF to do the shared library resolution. The problem is, I can't seem to get MEF to load them (i.e. I get a CompositionException when calling ComposeParts()) unless I also use MEF to load the plugin. And if I do this, well... then I need to know how to keep track of them as they get loaded so the user can select one from a list of plugins.

What have your experiences been with trying to mix and match these approaches?

+2  A: 

MEF is designed to let you easily load plugin assemblies. If you have control over the plugins (by which I mean that you can add MEF export attributes) then there is no need to keep your own plugin loader which uses reflection. MEF does all that for you.

That being said, "mixing and matching" MEF with other technologies is certainly possible. It sounds like your problem is that if you use your own plugin loader, you don't add those plug-ins to the MEF container. As a result, you get a CompositionException for parts which try to import the selected plug-in.

To add a plugin that you loaded with your own code to the MEF container, you can use the ComposeExportedValue like this:

container.ComposeExportedValue<IPlugin>(selectedPlugin);

edit: I see what you mean now by "all or nothing". Your problem is that in order to be able to import parts with MEF, you also need to construct the object with MEF. This problem then cascades to the object which normally created that object, etc. all the way to the application root.

To avoid this "all or nothing" effect, you can compromise by exposing the MEF container as a global variable (i.e. static field). That way, classes can access the MEF container and pull exports from it, e.g. by calling Program.Container.GetExportedValue<MyDependency>() in the constructor.

edit2: If you have an object that was not constructed by MEF, then there are two ways to add it to the container.

The first is to call container.ComposeExportedValue<IMyContractType>(myObject);.

The second is to return the object in a property getter, and then mark the property itself with an [Export(typeof(SomeType))] attribute.

Wim Coenen
Great, I'm glad that my current implementation matches yours exactly, as far using the names exposed by the plugin interface. It's working great. But still -- this does seem all or nothing. For example, as I convert more and more to MEF, I find more and more null references. And this makes sense because so many modules require the shared libraries, and so obviously those need to also be loaded by MEF in order to resolve the dependencies. I'm not sure why I had expected otherwise -- I guess it's because MEF just seems so magical, and you know how that goes. :)
Dave
Looks like you commented on my answer before I edited it. I tried to roll back to the original, but apparently revisions are not stored if done quick enough after posting the answer :-/
Wim Coenen
@Wim: thanks for the updates to your answer! I am actually going to just use MEF as a plugin loader replacement. And for those that aren't loaded by the catalog, I'll give your method a try, but I thought there was an AddPart method that you use to add into the container?
Dave
@Wim: It's as if we're playing post tag here! :) Thanks for the response, I think you understand what I am getting at here with the "all or nothing" comment! So yes, I guess I could do something like what you propose with the MEF container, but wouldn't you use something else to resolve this, like CSL?
Dave
@Dave: making the MEF container globally accessible essentially turns it into a service locator. Common Service Locator just has the advantage that you put an abstraction between your code and the container, so that you don't get a direct dependency on MEF everywhere. That might be important if your code needs to be usable with other IoC containers and applications.
Wim Coenen
@Wim: I thought that using CSL was one of many possible ways to make MEF globally accessible. Or if I didn't care about tying myself to a particular IoC container, I could use Unity to get the MEF container, right? Must do more reading up on this. It's quite confusing with all of the different options available, but someday I'll get there.
Dave