A Fix We Really Need in MEF for .NET 4.0
I rarely lobby publically for feature changes. But as we get closer to Beta 2, the feature lockdown becomes tighter. MEF is in the core of the framework, so any changes need to be carefully considered. I know that. But this change is small, testable and very important.
And Glenn has told me that its unlikely to happen, partly because I seem to be the only one asking for it. I think that’s just because you don’t know about it yet.
You need to help me make this happen.
The attributed model of MEF is the one you will almost certainly use in the VS 2010 timeframe. In this model, you adorn classes that you want in play in your container with the Export attribute. This is MEF specific attribute and a separation concern guideline is that you want to expose as little as possible about your technology, except where you need it. So, MEF Preview 6 came out with a very slick solution. The ExportAttribute class is unsealed. You can inherit from it and create your own attributes, named whatever you want. If you have a class which exports as an IProcess you can have a ProcessAttribute adorn that class. This makes your class more readable and moves the type your exporting (IProcess) into the base class where no one needs to remember it. This can be important when you have more complex derivation stacks.
This is especially valuable with custom metadata attributes. Let’s say my process needs a name, perhaps so it can be displayed in a menu. MEF metadata allows you to add this information, managed at the manifest level so your class isn’t fully loaded until needed. Custom metadata is very valuable and by adding a MetadataAttributeAttribute (yes, that’s the name) to the class derived from ExportAttribute, you give your ProcessAttribute a nice name, and the data required for MEF to do its job.
So far everything is very nice. Nick describes this briefly in his post here.
But it’s about to get very ugly.
Metadata is inserted into a dictionary for use by MEF. Your name is a string, so you expect to be access it as a string. You commonly MEF metadata via an interface, and a best practice prior to Preview 6 was to supply the same interface for your custom attribute (ProcessAttribute) and the call to retrieve the metadata.
This fails when you derive from ExportAttribute.
Let’s first look at why, then the simple fix that I think is so important to add, even if its late in the cycle.
ExportAttribute is a class that has an available attribute of AttributeUsage. AttributeUsage has a parameter called AllowMultiple. ExportAttribute quite reasonably has AllowMultiple set to true so you can export several interfaces at once.
MetadataAttributeAttribute is marking a class as supplying metadata information to MEF for us in a dictionary. This dictionary allows you to find out more information about your part before loading it – for display, filtering, custom prioritization, and other reasons. MetadataAttributeAttribute is applied to a class deriving ultimately from Attribute. It checks the AttributeUsage to see if the attribute allows multiples. If it allows multiples, MEF adds the values to the dictionary as an array of the declared type instead of the declared type.
OK, that’s a bit convoluted. Let me explain why this is really nice behavior. Let’s say you want to add metadata to category to any of your MEF parts. You create a CategoryAttribute class and give this class both the MetadataAttributeAttribute attribute and the AttributeUsage(AllowMultiple=true) attribute. Now you can supply Category as an attribute to any of your MEF parts and you can supply as many categories as you want to each part. This is very nice behavior.
However, we take these two very nice behaviors together we get a rather bizarre result. If you simply inherit from ExportAttribute the way Nick describes, you’ll place your metadata into the metadata dictionary as an array, and you will only be able to successfully retrieve the metadata with an interface that uses an array. The attribute you declare and the way you retrieve the metadata will not be able to use the same interface. This will be totally unexpected, you will get a rather cryptic error, and I hope you find this blog post so you spend somewhat less time than the 20 hours I did. You will also be able to place the attribute on multiple times – this is almost never what you want because you placed the interface you were exporting into the attribute – in other words, the ProcessAttribute is identifying things that export IProcess. You don’t want to repeat this.
Thanks for sticking with me through that. This is arcane knowledge that puts a twisty mountain pass between you and the pit of success. Instead I want it to “just work” the way you expect.
Now here’s the itty bitty fix that to solve this problem….
The MEF core needs to add a new attribute class – let’s say “ExportCustomBaseAttribute” for this discussion. This attribute inherits from ExportAttribute and does only one thing. It resets the AllowMultiple parameter to False. All you need to know is that you generally inherit from ExportCustomBaseAttribute instead of ExportAttribute. You will find this attribute adjacent to ExportAttribute in Intellisense. It gives a very nice place in the documentation to explain this problem for the folks that wish to understand it. Alternatively, this attribute could be called “ExportSingleAttribute” or a variety of other names. It might be possible to make it abstract if avoiding direct usage is desirable.
The benefit is that you do not need to understand the entire scenario described in this blog post to be able to create readable custom attributes that tuck the MEF dependency away behind the scenes.
But this isn’t going to happen unless we tell the team we want it. Comments on this site have been a bit ditzed. If you have a problem with posting a comment here (or you just want to bug Glenn instead), please place a comment here in Glenn’s Preview 6 post or here at Nick’s