Passing Metadata to Exports in MEF
Perhaps this post should have been out before my previous one the topic. Anyway, continuing the journey on Managed Extensibility Framework (MEF) in .NET, let us see how we can attach metadata to an exported type and how it can be retrieved on the importing side, all in short steps (I am not going to spend much time and dig deep in to each of the available options; this objective here is simply to highlight the available options). I cannot stress enough the importance of metadata in MEF and discuss various scenarios where it proves useful. Nevertheless, there are four ways by which you can associate metadata to an export:
- Using an interface definition with properties representing the metadata
- Using a raw IDictionary <string, object>
- Using a helper class that receives an IDictionary<string, object> and provides a convenient wrapper
- Using a custom class for strongly-typed metadata
The first three approaches evolve around IDcictionary<string, object> type and as such, metadata is limited to key-value pair. Let us see how the first one works: you define an interface with read-only properties that represent the metadata. Once that is done, you can straightaway go and use ExportMetadata on the export parts:
It is very important that the interface properties are read-only and theirs name matches with the string key specified in ExportMetadata attribute. Also, you do not have to create a class that implements the metadata view interface; the MEF automatically creates one which accessible via the Metadata property on the Lazy<ProviderBase, IMetaView> type.
The second does not require you to define an interface to wrap the metadata properties; rather, MEF exposes the raw metadata dictionary directly on the Lazy<ProviderBase, IMetaView> type. It is the export and import developers' responsibility that they use pre-agreed keys (as an explicit contract) and values types for the metadata dictionary. A simple misspell of the key or an incorrect data value for example, might result in erroneous metadata processing.
The next option for providing metadata is sort of a blended version of the first and second ones. You need to create a concrete class with properties representing the metadata but that class should have a public constructor that accepts IDictionary<string, object> as the only parameter. It is up to you how that class should interpret and expose the dictionary of export metadata. It is also not necessary that the class's property names should match the keys in the dictionary received by the constructor. The key-value pair is all at your disposal for how you want to make use of it.
Here is the metadata view class. Just to demonstrate that the class can interpret and expose the provided metadata values by its own logic, I am simply exposing Name and Version metadata values in a different name to the importing type.
The final option gives you a strongly typed way to declare, define and consume metadata. As a first step, as in option 1, define an interface with read-only properties that would act as the metadata view. Then, define an attribute (class derived from System.Attribute) and mark it with MetadataAttribute attribute. This custom attribute should implement the interface defined in the previous step. These properties can be populated via constructor parameters (shown below) or direct property assignment at the calling site (export types). The final step is to decorate the export types with this custom attribute and supply the metadata values:
The custom metadata class is here below. Please note that this class does not have to implement the above metadata view interface as long as it exposes read-only properties matching the names and types of the properties present in that interface (duck typing).
Hope you found this post useful.