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.
I have been playing around with Managed Extensibility Framework (MEF) of .NET 4.5 for a while now. Overall, it is a great framework for designing many plug-in/extensibility scenarios of .NET applications, web, desktop or even Windows Store (Metro). One area that got interesting in my MEF experiment was the way in which metadata worked in the case of inherited exports. As you know, in general, there are three ways to attach metadata to export parts (classes decorated with Export or InheritedExport attribute):
- Via ExportMetadata attribute
- Define a class deriving from ExportAttribute type, define one or more read-only properties representing the metadata and mark that class with MetadataAttribute type
- Define a class deriving from Attribute type, define one or more read-only properties representing the metadata and mark that class with MetadataAttribute type
In my experiment, the first two options were like breeze. The third turned out be a bit challenging to get it correct; I was not sure if the behavior I noticed was the intended one or a bug. Here is what I did:
- Define a class deriving from Attribute type and mark it with MetadataAttribute. This class will have read-only properties each representing the required export metadata. In fact, the metadata properties were wrapped in an interface and the custom attribute class implemented that interface.
- Define my base export class marked with InheritedExport attribute and also the custom metadata attribute I created in step 1
Here is the code:
Then, I went on to apply this custom attribute to my base export part that also has InheritedExport attribute on it. I defined two more classed deriving from the base export and applied metadata with the custom attribute. At this point, there are three export parts, each with its own metadata tagged via the custom attribute - CustomMetadata. Here is the code:
I setup a simple composition container to test out the metadata:
However, the output of the above took me for a surprise and I spent hours trying to fix this but later I inferred from various blog posts, Stack Overflow responses and the MEF CodePlex site that this behavior is "by design" in MEF! I was expecting the respective mask and symbol metadata values of each export part to be printed; rather I got the mask and symbol of the base export part printed for all three.
As you can see, the metadata supplied at each inherited export type was completely ignored. Rather, the metadata specified at the base class was carried on to the inherited classes too (contradicting to what I read at http://mef.codeplex.com/discussions/82448/). One solution to this issue is to stay away from the InheritedExport and explicitly apply Export attribute on each export part specifying the base export type:
And the corresponding output is:
The other solution is to have the custom metadata attribute extend ExportAttribute (in addition to the metadata interface) as shown below:
Then apply this attribute on each export part (without explicit Export attribute, since the new custom attribute extends the ExportAttribute type):
The output remains the same: each export part correctly gets the metadata supplied via the new custom attribute.