Discoverable and non-discoverable parts in MEF
Join the DZone community and get the full member experience.
Join For FreeIf you’ve read my article that introduces to the basics of MEF, then you probably already understand what imports and exports are.
Based on these imports and exports, the MEF composition engine is putting the extensible (and extension) parts together to build the complete functionality set, provided by various modules. Many of the examples show cases where all parts exposed by the extension are discoverable by the composition engine, when called in the main application.
For example, there is an interface that defines the structure of an extension:
[InheritedExport]
public interface ISample
{
string Unit { get; set; }
}
The extension itself looks like this:
public class Sample : ISample
{
public string Unit
{
get
{
return "Assigned text";
}
set { }
}
}
It explicitly exposes the Sample class as a discoverable part via the InheritedExport attribute that decorates the interface. Basically, what the InheritedExport attribute says is that whatever class inherits it, exports its instance. Therefore, Sample will automatically export an instance of ISample.
The composition engine will be able to get this instance in case the main application provides an import for the contract.
This is what’s called a discoverable part – the composition engine is able to detect it and instantiate it for the base application. But what if the developer doesn’t want to make the part discoverable, but still provide imports and exports for some components? I found out that there are ridiculously small amounts of documentation regarding this topic online, so I will try to explain how it works here.
This is the case specific for abstract classes (that are not discoverable anyway) and for classes that for some reason cannot be set abstract. A class can implement an interface. If the interface has the InheritedExport attribute, this will automatically mean that this class exposes an instance of that interface, however this won’t be completely true, since the class can implement other classes, methods or fields.
There is no doubt that the class will abide the contract and will actually be imported if requested, but since it was intended to have different functionality (for example, it is a helper class), the developer could avoid this.
MEF provides the PartNotDiscoverable attribute that tells the composition engine to ignore this part (therefore, it won’t be composed). Let’s take a look at this snippet:
[PartNotDiscoverable]
public class Sample : ISample
{
[Import("MyContract")]
public string Unit
{
get;
set;
}
public void ShowText()
{
Debug.Print("Another text");
}
}
It is the same part as the one shown above with small modifications- first of all, it has the ShowText method and it imports the string setting. This certainly qualifies as an instance of ISample, but I don’t want my application to see it since I am using it as a base class for another class (besides, I won’t be able to use the ShowText method from an ISample instance unless it is called internally – and that requires the class to actually inherit a base class that inherits the interface):
public class MainClass : Sample
{
}
This class can use the ShowText method from the base class in one of its own methods, so I am using the MainClass class as a helper.
In the main application, I can define an export to satisfy the import for the MainClass class:
[Export("MyContract")]
public string welcome = "Sample String";
And of course, there is an import for an ISample instance that will be the an instance of MainClass:
[Import(typeof(ISample))]
ISample sample;
Now I can use the composition process to instantiate the MainClass with the import filled:
AssemblyCatalog catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
var batch = new CompositionBatch();
batch.AddPart(this);
container.Compose(batch);
Debug.Print(sample.Unit);
This will print “Sample String” in the Output window. It works!
Now, let’s take a look at a graphic representation of the above process to better understand the need for a non-discoverable part.
First of all there is a base class that is invisible to the composition engine. It has an import defined and a set of helper methods:
There could be one or more classes that inherit the base class. Although the base class is not discoverable, its imports are discovered by the composition engine when the inheriting class is checked for the ancestor (it discovers the interface behind the ancestor). For example, there is the MainClass (like the one that was shown a bit earlier) that inherits from the base class and OtherClass:
By inheriting the base class, they are automatically considered instances of the interface that defines the base class. And each one of them inherits the import.
The application, by exposing an export, fills the import for the base class, and that will automatically fill the imports for every single class that inherits its functionality from the base class.
While still implementing the needed interface, the base class can provide additional functionality to inheriting classes and helps avoid additional export attributes inside the classes that inherit it. This is just one of the reasons to make a part non-discoverable. Eventually, you will see that the need to implement a class that should not be detected as a part is eventually present, when the plugin model expands.
Opinions expressed by DZone contributors are their own.
Comments