An introduction to MEF programming models
Disclaimer: This is a prototype and work in progress. While the code does work it has not gone through the same degree of testing as the Attributed Programming Model that is shipped with the Managed Extensibility Framework
Back in late November 2008, when MEF Preview 3 shipped, I started looking into what the Managed Extensibility Framework was and what it had to offer. After having spent a couple of weeks exploring the shipped features of the framework, I soon started looking for ways that I could extend it with custom functionality.
One of the ideas I had was to write a custom catalog and instead of extracting part information from the types in an assembly, I wanted to use the configuration file to define parts. This would get rid of static part definitions using the Import/Export attributes and instead introduce a way of modifying the composition of an application even after it had been built and shipped.
After spending some time digging around in the framework it soon dawned on me that this would be impossible, because of the dependency it had on extracting definitions from types in assemblies (remember this was back in Preview 3, a lot has changed in the framework since).
I spent some time talking to Glenn Block, Program Manager of MEF, and it soon dawned on me that the framework was built around the concept of programming models. This definitely opened up a completely different realm of possibilities with the framework and also turned my project from a two day coding session into an open source project.
Programming models
I would go as far as to say that programming models are the least known feature of the entire framework, but if you ask me it is one of the most interesting ones. The MEF team designed to allow the framework itself to be extensible so you could customize it to work the way you need.
To get a better understanding of programming models, and how they relate to MEF, it is easiest to think of MEF as a two tier architecture.
The core services tier provides all the necessary functionality to perform part composition. The main responsibility of this tier is to figure out which export goes to what import. There are a bunch of other things that is taken care of in this tier such as lifetime management and part creation policies.
However, the core services does not implement the logic of how the compositions are made, not how the exports and imports are defined. This is the responsibility of the programming model tier. Flexibility in this tier allows customization of composition behavior and capabilities. For example Microsoft is shipping a single programming model in the initial release of the framework.
The model is known as the Attributed Programming Model and it has gotten its name from the fact that is uses attributes to provide a declarative approach for defining parts. To give you an idea of some of the behavior and capabilities that the attributes programming model adds to MEF, please review the following list
- Part definitions - Allows imports and exports to be defined using a declarative approach with the help of attributes
- Recomposition control – Control if an import can be satisfied more than once, for example if new matching exports are identified by the framework
- Default value behavior – Control if it’s ok for an import to not be satisfies, i.e. is no matching exports is found then the default value of the import type will be kept instead of throwing an exception
- Interface exports – Define that an interface can be used to create parts. All classes that implement the interface can then be turned into exports
- Export inheritance – Ensure that exports which have been inherited from a base class also will be satisfied
- Import type support – Enables you to import different types of imports, such as single imports, collections and arrays
- Hierarchical composition – Enables nested relationships of imports and exports
This is far from a complete list of what the attributed programming model adds to MEF, but it gives you an idea of just how important programming models are to the framework. Imagine removing the model. Imagine what would be left.
As you can see if you remove the programming model from the framework stack then there’s very little functionality left. So with this knowledge you might think that creating a custom programming model would require a lot of work. It's quite possible that you are right, it all depends on the functionality you wanted to put into your model.
Creating a custom programming model
Creating a custom programming model varies in complexity, depending on the behavior and functionality you want to include. Either way you need to know a bit about some of the classes that exists in the framework and that are used to customize it.
These classes are collectively known as the Composition Primitives
- ComposablePartDefinition – A high level description and factory of parts that can be involved in the composition of an application
- ExportDefinition - Describes a capability that a part has to share in the composition
- ImportDefinition – Describes a compositional dependency that a part has. This is a requirement that a part needs to have satisfied when it participates in composition
- ComposablePart - Provides a high-level abstraction of the underlying instance of a part. Contains the logic for both satisfying the requirements of a part and serving the instance once requested
- ComposablePartCatalog - Provides the mechanism for discovering and supplying part definitions to the framework
Depending on the complexity of the programming model you are implementing, you are going to be implementing one of more of these classes. You can read more about the Composition Primitives in the Hosting the .NET Composition Primitives by Nicholas Blumhardt, member of the MEF team.
There is also the option to build a composite programming model, i.e. a model which leverages the functionality of an existing programming model to provide new functionality. An example of such a model is the Functional Programming Model prototype by Glenn Block. You can read more about it in the Creating a functional programming model for MEF post on Glenn’s blog.
Next we're going to looking at a custom programming model that I have been working on for a while now. It's part of the MEF Contrib project and the full source code is available for download.
The provider model
The provider model is a custom programming model for MEF, built from the bottom up to support a rich extensibility experience. The idea for the model is to try and be as feature equivalent as the Attributed Programming Model but to add custom functionality on top of that. The model is in a very early development phase and even though the model works, it has not gone through the same amount of testing as the model that is being shipped with MEF. So if you plan on using this model in production code, then please perform an adequate amount of testing first. If you do find any issues please let me know, or even better contribute a patch!
The provider model is only possible because the people on the MEF team decided that there should be as loose coupling between the core functionality of the framework and extended behaviors and capabilities. In my opinion this is one of the most important design decisions they have made and it enables you to customize the MEF stack to exactly match your needs.
This is going to be a brief overview of the provider model and its functionality. For a more in-depth documentation please refer to the documentation over at the MEF Contrib webpage. Please drop a line in the discussion forum is you have any thoughts on the model, suggestions on enhancements or end up using it in a project – I’d really like to know if you use it in an application!
Definition Providers
This was the very first feature that made it into the provider model. Initially I was only going to use the configuration file to define parts but when Glenn and I bounced some ideas back and forth I soon realized that it wouldn’t be too much work to build an open-ended approach for supplying part definitions to the model. The Attributed Program Model is limited to using attributes to declare parts, but the provider model adds a third tier on the MEF stack – the definition providers tier.
The definition provider tier is an abstraction layer between the model and the way that definitions are provided. The new tier introduces an interface called IDefinitionProvider and all you need to do in order to talk to the provider model is to implement a single read-only property, called Parts, that the interface defines and you are ready to go. Of course you also need to implement the logic for defining the definitions.
At the time of this blog post there are three definition providers included with the model
- XML – Defines definitions with the help of the app.config file
- Fluent – Uses a fluent API to define parts with the help of code
- Attribute – A provider model compatible way of defining parts with attributes
You tell the model which definition provider to use by providing the type to the catalog used by the model. The name of the new catalog is DefinitionProviderPartCatalog and below is an example of how you would setup the model to use the app.config way of defining parts. First you need to define the parts in the configuration file.
<mef.configuration> <parts> <part type="MefContrib.NullMessageService, MefContrib" > <exports> <export contract="IMessageService" /> </exports> </part> <part type="MefContrib.Program, MefContrib"> <imports> <import member="Service" contract="IMessageService" /> </imports> </part> </parts></mef.configuration>
Once that is done, you pretty much do like you would do with the Attributed Programming Model. You create a catalog, in this case a DefinitionProviderPartCatalog, but when you do that you need to tell it which provider to use.
var provider = new ConfigurableDefinitionProvider("mef.configuration");var catalog = new DefinitionProviderPartCatalog<ConfigurableDefinitionProvider>(provider);var batch = new CompositionBatch();batch.AddProviderPart(this);var container = new CompositionContainer(catalog);container.Compose(batch);The ConfigurableDefinitionProvider needs to know the name of the configuration section in order to know where to find the information that is needed to manufacture the part definition. If you wanted to change this into using the fluent definition provider, all you would have to do would be to create a new instance of the FluentDefinitionProvider class and pass it to the catalog.
provider .Import<Program>(p => p.Service) .WithContract(typeof(IMessageService)) .Export<NullMessageService>(n => n) .WithContract(typeof(IMessageService));var catalog = new DefinitionProviderPartCatalog<FluentDefinitionProvider>(provider);
The FluentDefinitionProvided uses an Internal DSL to define that available parts. So with the help of the IDefinitionProvider interface you could easily implement a custom approach to defining definitions. You could read from a file on the network to allow for a shared definition file, or configure a cluster of MEF enabled applications with a single file. Or you could implement a definition provider that called a web service to get the definitions, or build a definition provider that used YAML or XAML, the possibilities are literally endless.
Object Factories
An object factory gives you control over an important step in the composition process – the creation of the part instance. This enables you to implement custom logic for how the instance should be created.
For example you could implement an object factory that returns AOP or remoting proxies instead of the actual objects.
public class NAspectObjectFactory : ObjectFactory{ public override object Create(Type type, params object[] parameters) { IEngine engine = ApplicationContext.Configure(); object proxy = engine.CreateProxy(type, parameters); return proxy; }}You would then tell the model to use the factory by assigning an instance of it to the DefinitionProviderPartCatalog instance that you are using
var catalog = new DefinitionProviderPartCatalog<FluentDefinitionProvider>(provider);catalog.Factory = new NAspectObjectFactory();
To read more about the object factory functionality of the provider model please refer to the Object Factories documentation section of the model at the MEF Contrib page.
Trusted Assemblies & Configuration
One of the features that’s currently being developed is the ability to tell a catalog which assemblies to trust. When an assembly is added to the trust list of a catalog then only parts defined in those assemblies will be returned by the catalog, all other will be discarded.
To give you an idea of what this will look like look at the following source code
provider .Import<Program>(p => p.Service) .WithContract(typeof(IMessageService)) .Export<NullMessageService>(n => n) .WithContract(typeof(IMessageService));var catalog = new DefinitionProviderPartCatalog<FluentDefinitionProvider>(provider);catalog.Configuration .Trust(Assembly.GetExecutingAssembly()) .Trust(AppDomain.Current) .Trust("some-public-key");As you see there are quite a lot of overloads for specifying which assemblies to trust.
The Future
If you do find any bugs then please consider adding it to our issue tracker on codeplex or drop me a line. Equally if you have any suggestions for improvements of the code or the model itself, please post on the project discussion list on our codeplex page.
I’ll keep on adding more features as I think of them, but I'm also looking for people to actively help work on the model so if you are interested please let me know!
I would like to thank Glenn Block and Kathleen Dollard for taking the time to review the post while I was working on it.