Monday, September 08, 2008

Managed Extensibility Framework: Why?

Sometimes... no often... I'm quite dumb. I've been skimming through posts about the MEF thinking that people were talking about the Entity Framework (Microsoft Entity Framework :). I only realised that it was something new today. So what is it. Well it looks like an IoC container, but the spin is that it's different from other IoC containers because it's primarily aimed at providing an extensibility model; a common way for applications and frameworks to load types from dropped-in assemblies. This is from the CodePlex MEF site:

"MEF presents a simple solution for the runtime extensibility problem. Until now, any application that wanted to support a plugin model needed to create its own infrastructure from scratch. Those plug-ins would often be application-specific and could not be reused across multiple implementations.

MEF provides a standard way for the host application to expose itself and consume external extensions. Extensions, by their nature, can be reused amongst different applications. However, an extension could still be implemented in a way that is application-specific. Extensions themselves can depend on one another and MEF will make sure they are wired together in the correct order (another thing you won't have to worry about).

MEF offers a set of discovery approaches for your application to locate and load available extensions.

MEF allows tagging extensions with additional metadata which facilitates rich querying and filtering"

The thing is, although they're selling it as a plug-in framework, it does look very much like any other IoC container,  so it's odd that they're making the plug-in pitch when they could be providing a generic IoC framework. There doesn't seem to be anything about it which precludes using it in the general IoC role, except that they say it's not optimised for that.

So it begs the question: are the MEF team suggesting that we support two IoC frameworks in our applications? One for plug-in extensibility, the other to support internal component based architecture?

It seems to me that it would not be very hard to provide an IoC container that can successfully cover both these requirements. I haven't tried it, but I imagine it would be quite simple to provide a facility for Windsor to load assemblies from a file location, for example.

This one is going to run and run...


Chad Myers said...

What about when you want to unload a plugin? MEF manages app domains and such. I see as MEF overlapping a little with IoC containers, but it does have functionality that most (all?) .NET IoC containers do not have.

It's possible that some of the containers will lean on MEF to provide remote load/unload capabilities (in another app domain)

Anonymous said...

Chad, MAF has app-domain facilities, MEF doesn't. The acronyms are close. :)

MEF does have features similar to DI containers. This is because we've seen DI from a pattern perspective as a fine way to solve a specifc problem. It is that problem that we are optimized for which is discovery of extensions. Really what that boils down is our implementation. MEF includes catalogs and such for discovery. These are first class citizens. That discovery mechanism (at least the default ones) use attributes as the mechanism for how parts are discoved.

This discovery mechanism is focused on extensions for the system that are not predetermined. Additionally MEF a layer of metadata above and beyond the parts themselves which helps facilitate this discovery. This allows upstream querying of the container not just based on type but based on the metadata as well.

Why is that important? Because MEF applications adapt themselves based on what is available at deployment time. A further difference between a traditional IoC is the notion of exports, our exports are very different than a simple type registration in the container.

All of these differences are specifically based on supporting app-plugins / non-determinant scenarios. That is allowing a vendor to ship a 3rd party dll that you just drop in the bin folder and your app "lights" up. In these cases the person who owns the app is not the person who wrote the plugin. Furthermore in many cases the person who added the plugin is a third person..the user.

Is this the reason generally that people use IoC today? Not really. The primary reason (at least in my experience) is for the internal decoupling [like for supporting TDD] that you mentioned, as well as the framework aspects like interception, etc. In these cases the pieces getting thrown in the container are not indeterminate but determinate. Meaning when I do a unit test, I know what I am throwing in the container. When I run my app, I know what is being registered. Normally I am the person who owns the code of the app as well as the pieces being plugged in. For these reasons having pure convention based configuration and such make a lot of sense.

Because of this, we think the two actually can play well together. At the ALT.NET meeting in Seattle, we recently discussed this and it seemed to have legs. To make it possible we would need some kind of bridge both ways between the two. An IoC can talk to MEF for MEF things, and MEF can talk to IoC for all other IoC things.

Let's take an example. Imagine I have a part that imports a logger in its constructor. That part also exports itself. Now I could have MEF import my logger (i.e. I mark the logger as an export). However, when my app is deployed, that's not likely the kind of thing that I just want you to drop the logger in and it works. So if I have a bridge, MEF can ask the container as its building up the part if it can supply an ILogger. If it can then MEF uses it. If no container is present than MEF will rely on its own facility. You could also imagine going the other way. So Windsor has a component that requires a pluggable authentication service that "might" be in MEF, depending on the configuration. So the container is hardwired to ask MEF if it can provide an IAuthenticationProvider. If it can, the containerr uses it, otherwise it reverts to its internal registry.

This is just an illustration to start the conversation going.

Anonymous said...

I forgot to mention additional support that we have for the scenario which is our contract adapters. You'll see an illustration of this in the MEFTris example. Essentailly it allows adapting one contract to another. One place this is important is in supporting multiple versions of a plugin in the same app. A second is for doing adapting to a completely different type, which we do in MEFTris with shapes.

Mike Hadlow said...

Glen, thanks for the comment.

You're right of course that the primary reason that people use IoC containers is not as a plugin framework. But that doesn't mean you can't use them like that. In fact I think the kind of discoverability that you describe would be a nice addition for something like Windsor. From my naive point of view I still don't see the reason for having two separate frameworks that do pretty much the same thing: dependency resolution and object lifetime/lifestyle management. Sure you can provide a bridge between MEF and a 'traditional' IoC container, but surely it would be better for MS to provide a generic IoC container with the discoverability functionality added. Or is there some architectural reason why a generic IoC container isn't a good starting point for the plugin features?

Just for the sake of argument, what is there to stop me from using MEF in a traditional IoC role? You say that non-attribute based configuration will also be available, so couldn't I just ditch my current IoC container and just use MEF as the one-container-to-rule-them-all?

Glenn Block said...

You "could", techincally however some things will behave differently than you think.

For one thing our lifetime / instancing story is not what you would expect with a traditional IOC today.

There's no way to register a keyed instance at runtime. You can add multiple instances to the container that have the same contract. But the only way to find a specific one would be to import the entire collection, i.e. IEnumerable of ILogger, and then iterate through it to find the one you want.

Let say you had a list of exports that each one needed to be associated with a specific Order. You'd have no way to add this export with a key for Order123 and this one for Order234. You would have to add orders to the container and find them yourself by doing a GetExports() for example.

This is not broken, its because MEF's discovery mechanism is focused on the "types" of things you bring into the system rather than the instances of those types. Even the metadata you add to contracts it type (part) related, and doesn't change at runtime.

The point is not that you can't use it, it is what is it optimized for. You can use / misuse technologies in lots of ways. You can write a for loop that makes 1000 web service calls for example. But is that a good use of the for loop? You can use Biztalk when all you need is a simple file scanner.

It's the same thing here, you can use MEF, but in doing so you are forcing yourself to conform to a design that was build to address a different set of concerns than you are addressing.

Not sure if this is making any sense.

Mohamed Meligy said...

The generic IoC container used by Microsoft here is "Unity".

Mike Hadlow said...

Ayende has a good post on this: