Filtering With Metadata In The Managed Extensibility Framework
Please notice the two different attributes ImportMany and Import. There is an important difference. The first one will find a lot of implementations of the contract (zero to many) while the latter will find only one exact match to the contract. In the first case we want MEF to find all IWorkItem implementations that are decorated with the IWorkItemView metadata. In the second case we want to find one single collection of IWork.
Filtering with Metadata in the Managed Extensibility Framework
The trick is to collect all the instances (ImportManyAttribute) and hand them back out (ExportAttribute) as a single collection. What we do in the base class is import the many lazy with metadata instances and then immediately export a whole collection of the new contract to be picked up somewhere in the code as needed.
The principle purpose of MEF is extensibility; to serve as a 'plug-in' framework for when the author of the application and the author of the plug-in (extension) are different and have no particular knowledge of each other beyond a published interface (contract) library.
Another problem space MEF addresses that's different from the usual IoC suspects, and one of MEFs strengths, is [extension] discovery. It has a lot of, well, extensible discovery mechanisms that operate on metadata you can associate with extensions. From the MEF CodePlex site:
Combined with an ability to delay-load tagged extensions, being able to interrogate extension metadata prior to loading opens the door to a slew of interesting scenarios and substantially enables capabilities such as [plug-in] versioning.
Again, MEFs 'intent' is tightly focused on anonymous plug-in extensibility, something that very much differentiates it from other IoC containers. So while MEF can be used for composition, that's merely a small intersection of its capabilities relative to other IoCs, with which I suspect we'll be seeing a lot of incestuous interplay going forward.
With all the existing approaches, why come up with something new? We realized none of our current solutions were ideal for general third-party extensibility. They were either too heavyweight for general use or required too much effort on the part of either the host or the extension developer. MEF represents the culmination of learning from each of these solutions, and an attempt to address the pain points just mentioned.
When integrating MEF into an existing application, or with other frameworks, you will often find non-MEF related class instances (meaning they are not parts) that you will want to make available to importers. These may be sealed framework types such as System.String; application-wide singletons such as Application.Current; or instances retrieved from a factory, such as a logger instance retrieved from Log4Net.
To allow the accessing of metadata, MEF leverages a new API of the .NET Framework 4, System.Lazy. It allows delaying the instantiation of an instance until the value property of the Lazy is accessed. MEF further extends Lazy with Lazy to allow accessing export metadata without instantiating the underlying export.
Catalogs provide part definitions (ComposablepartDefinition), which describe the available exports and imports. They are the main unit for discovery in MEF. MEF provides several catalogs in the System.ComponentModel.Composition namespace, some of which you already saw, including DirectoryCatalog, which scans a directory; AssemblyCatalog, which scans an assembly; and TypeCatalog, which scans through a specific set of types. Each of these catalogs is specific to the attributed programming model. The AggregateCatalog, however, is agnostic to programming models. Catalogs inherit from ComposablepartCatalog and are an extensibility point in MEF. Custom catalogs have several uses, from providing a completely new programming model to wrapping and filtering existing catalogs.
In the preceding code, childContainer is arranged as a child of rootContainer. Both rootContainer and childContainer provide their own catalogs. For more on using the container to host MEF within your applications, see -has-landed-in-silverlight-4-we-come-in-the-name-of-extensibility/.
Managed Extensibility Framework (MEF) is a component of .NET Framework 4.0 for creating lightweight, extensible applications. It allows application developers to discover and use extensions with no configuration required. It also lets extension developers easily encapsulate code and avoid fragile hard dependencies. MEF not only allows extensions to be reused within applications, but across applications as well. MEF was introduced as a part of .NET 4.0 and Silverlight 4.0 and 5.0. It is available on CodePlex along with source and as a result can be used, albeit unsupported and with limitations, on version 3.5 of the framework.
MEF is used by Visual Studio 2010 for add-ons and is quickly gaining momentum. It is important for developers of modular and extensible applications to learn to master this core .NET technology. This video will provide fundamentals for using the Managed Extensibility Framework (MEF) and explain when and why to use it, as well as how to use it most effectively. Topics from basic MEF composition to advanced metadata filtering are covered.
Part 1 dealt with fundamentals, imports and exports. Part 2 follows on from part 1 and explores additional features of the Managed Extensibility Framework (MEF). This time the focus is on metadata and creation policies.
This class needs to be decorated with the MetadataAttribute attribute and derived from the Attribute class. The individual values to be exported via the metadata are specified using properties. The type and name of the properties must match that specified in the interface for the metadata. We previously defined the ICarContract interface as follows:
There is one further option, but this I will leave for a later post in which I will talk about inherited exports. It allows both the export and metadata to be decorated with an attribute simultaneously.
In general, it is not necessary to specify the contract name, and most contracts should be defined in terms of the contract type and metadata. However, under certain circumstances, it is important to specify the contract name directly. The most common case is when a class exports several values that share a common type, such as primitives. The contract name can be specified as the first parameter of the Import or Export attribute. The following code shows an import and an export with a specified contract name of MajorRevision.
Exports can provide additional information about themselves known as metadata. Metadata can be used to convey properties of the exported object to the importing part. The importing part can use this data to decide which exports to use, or to gather information about an export without having to construct it. For this reason, an import must be lazy to use metadata.
Ordinarily, all of the properties named in the metadata view are required, and any exports that do not provide them will not be considered a match. The DefaultValue attribute specifies that a property is optional. If the property is not included, it will be assigned the default value specified as a parameter of DefaultValue. The following are two different classes decorated with metadata. Both of these classes would match the previous metadata view.
It is the importer that specifies what metadata view, if any, will be in use. An import with metadata is declared as a lazy import, with the metadata interface as the second type parameter to Lazy. The following class imports the previous part with metadata.
In many cases, you will want to combine metadata with the ImportMany attribute, in order to parse through the available imports and choose and instantiate only one, or filter a collection to match a certain condition. The following class instantiates only IPlugin objects that have the Name value "Logger".
If there is metadata associated with an InheritedExport attribute, that metadata will also be inherited. (For more information, see the earlier "Metadata and Metadata Views" section.) Inherited metadata cannot be modified by the subclass. However, by re-declaring the InheritedExport attribute with the same contract name and contract type, but with new metadata, the subclass can replace the inherited metadata with new metadata. The following class demonstrates this principle. The MegaLogger part inherits from Logger and includes the InheritedExport attribute. Since MegaLogger re-declares new metadata named Status, it does not inherit the Name and Version metadata from Logger.
Since interfaces cannot be instantiated directly, they generally cannot be decorated with Export or Import attributes. However, an interface can be decorated with an InheritedExport attribute at the interface level, and that export along with any associated metadata will be inherited by any implementing classes. The interface itself will not be available as a part, however.
A custom attribute can specify the contract type, the contract name, or any other metadata. In order to define a custom attribute, a class inheriting from ExportAttribute (or InheritedExportAttribute) must be decorated with the MetadataAttribute attribute. The following class defines a custom attribute.
This class defines a custom attribute named MyAttribute with contract type IMyAddin and some metadata named MyMetadata. All properties in a class marked with the MetadataAttribute attribute are considered to be metadata defined in the custom attribute. The following two declarations are equivalent.
Well, other solutions exist in this space problem associated with extensibility, such as Microsoft's own Managed Addin Framework (MAF) and even inversion of control (IoC) frameworks like StructureMap and Unity. Microsoft felt that these solutions were not ideal for third party extensibility because of the solution being either too heavyweight for general use, or just required an infrastructure constructed from scratch. MEF provides a powerful attributed programming model, out of the box, using the regular .NET attributes and provides a standard way for application to expose the extension points as well as to consume extensions. Actually, this represents the fundamental principle that underlines the MEF platform: the supply of services, and the demand/need to consume them.