Over a million developers have joined DZone.

Application extensibility model – working with MEF

·

Application extensibility is an interesting aspect that is often overlooked when working on large projects, and when it comes to the actual implementation, there might appear some problems designing the proper extensibility model.

MEF helps simplify the extensibility model by using a well-defined structure for discovering and importing extensible units in the existing application. In this article I am going to show how to create a simple plugin and use it in a .NET application with MEF instead of using the standard assembly import.

First of all, you should understand the three tiers of an extensible application.

At the foundation there is the SDK, defining the standard for plugins. MEF does not define or interfere with that standard, but rather provides a way to use it. Usually, the SDK provides interfaces, abstract classes and helper classes. The plugin is based on the standard implementation and follows the guidelines defined there, however its functionality varies, since in most cases the limits imposed by the standard are more or less flexible. The application uses the SDK as the link to make sure that every plugin that is used corresponds to the expected structure.

Let’s start with creating the simple implementation for the SDK. I created a new class library project and used the following code for it:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PluginDevKit
{
public interface IWriter
{
void DisplayMessage(string message);
}
}

There is a single interface declared that has a method declared that accepts a string parameter. I am trying to keep the concept as simple as possible, so it is easily understood. Don’t focus at the class functionality at the moment but look at how MEF is going to facilitate the process. Notice the fact that the SDK isn’t using any MEF-related calls. It will be used in the application and the plugin.

Now since the SDK skeleton is ready, it is time to work on the plugin. For this specific purpose, I created a separate class library. First of all, add a reference to the existing SDK DLL – just add the reference to the DLL that is the product of the compilation for the class written above (located in the bin/Debug folder).

To work with MEF, a reference to System.ComponentModel.Composition is needed (both the actual reference and the “using” declaration in the class header, obviously). Once done, rename the existing class to something like MyPlugin and make it inherit the IWriter interface, present in the SDK (a using statement in the class header is needed for this as well).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PluginDevKit;
using System.ComponentModel.Composition;
using System.Diagnostics;

namespace Plugin
{
public class MyPlugin : IWriter
{

}
}

Now that the initial structure is ready, it is about time to implement the DisplayMessage method, declared in the IWriter interface. My implementation is quite simple and all it does is it prints a message in the Output window in Visual Studio (reference to System.Diagnostics is required):

public void DisplayMessage(string message)
{
Debug.Print(message);
}

Now it’s time for MEF – add the following attribute to the class:

[Export(typeof(IWriter))]

This attribute is a way for the class to tell the application that it exposes an instance of IWriter and that the class below the attribute can be used wherever an instance of IWriter is needed. The way MEF works is – it links the application with the possible plugins by being the intermediary discovery link. The opposite to the Export attribute is the Import attribute, but we’ll get to that later. Looking for an analogy, the Export attribute sets the class as the plug and the Import attribute defines the socket that only accepts specific plugs.

You can now build the plugin, since this is all it is going to have at the moment.

The actual application is quite simple, but before going deeper into the code, copy the newly built plugin in the application folder (bin/Debug), since that is the place that I am going to use to reference the assembly.

To actually work with an imported assembly, it has to be assigned to an existing instance of the specific type. To do this, in the application’s main class  create a new public property:

public IWriter Writer { get; set; }

But how will the program know that this is the instance that will be used for the extension? To specify this, I am going to use the Import attribute for this specific property:

[Import(typeof(IWriter))]
public IWriter Writer { get; set; }

This is a way for the application to tell MEF that it can accept an instance of IWriter from an external resource (it can be an internal assembly as well).
In the loading method for the application, use the following code:

AssemblyCatalog catalog = new AssemblyCatalog(Assembly.LoadFrom(Application.StartupPath + @"\Plugin.dll"));

CompositionContainer container = new CompositionContainer(catalog);
CompositionBatch batch = new CompositionBatch();
batch.AddPart(this);

container.Compose(batch);

Writer.DisplayMessage("DD");

NOTE: You need to add a reference to System.ComponentModel.Composition and System.ComponentModel.Composition.Hosting since the application “hosts” the extensions.

Although confusing at first, it is quite simple. The AssemblyCatalog is used to read the assembly and parse its internal components for reuse. The CompositionContainer instance manages the composition process, therefore –the detection and introduction of Exports to the proper Imports. The CompositoinBatch contains the parts that need to be added or removed, that are later used in the composition. Finally, the compose method performs the composition.
You can see that without any additional assignments I am calling Writer.DisplayMessage(“DD”). Once composed, the Export (from the plugin class specified as the source assembly) is automatically assigned to the Import present locally (in the application). Once found and assigned, I can call it.

If you run the application, you will see the message displayed in the Output window.

This article introduced just one very small aspect of MEF and it’s absolutely not covering the rest of the framework. You can see that the process of creating an extension model can become more efficient if MEF is properly implmented in the application.

Topics:

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}