Posted by: Zeeshan Amjad | May 11, 2011

Using DirectoryCatalog with ImportMany attribute in MEF


Once i came across a situation where I have to implement multiple logging mechanism in my application. Such as logging in database, event log, XML file etc. Not only that, but all of these should be independent to other. In other words, when I deploy my application, at some places I need only database auditing, some places only event log, some places both event log, database log and can be any combination. Now this will become a difficult task at first, because there are several different combination of it which my application suppose to handle. Not only that if I am going to add one more implementation of logging, such as streaming then I have to re-write the code of my application to handle this. I want to prefer a clean design that I wrote code once which will handle all current and future implementation of this. The first approach came to my mind is using reflection, but using reflection is not a good and clean design.

I decided two main principle for my application. First principle is all of my implementation must be in different component (DLL file). Second my application should look for one particular location on my file system to find the components and work accordingly depending on how many components are places on that location.

MEF came to rescue me to solve exactly the same problem. I used Directory Catalog of MEF to see the particular location in my file system and use ImporMany attribute to load multiple components and use their functionality accordingly. Here is a class diagram of different catalog provided by MEF.

MEF_01

 

We are going to use ImportMany attributes. This not the only attribute define in MEF here is a class diagram shows all the attributes define in System.ComponentModel.Composition namespace.

MEF_03

Import Many attributes define four new properties. Here is a class diagram of ImportmanyAttribute class.

MEF_04

Here CreationPolicy is an enum with three possible values Any, Shared and NonShared.

Our starting point is to define one logging interface. Just for simplicity I define a very simple interface with only one method in it.

Code Snippet
public interface ILogInterface
{
    void LogMessage(string message);
}

 

Then in my container class, I define one IEnumerable type property and define ImportMany attribute on it. Here is a code of it.

Code Snippet
[ImportMany(typeof(ILogInterface))]
public IEnumerable<ILogInterface> LogInterfaces
{ get; set; }

 

constructor of our container class is very straight forward just created a directory catalog, composition container and composition batch. Here is a complete code of our container class.

Code Snippet
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

namespace MefContainer
{
    public class Container
    {

        [ImportMany(typeof(ILogInterface))]
        public IEnumerable<ILogInterface> LogInterfaces
        { get; set; }

        public Container()
        {
            DirectoryCatalog catalog = new DirectoryCatalog(".");
            CompositionContainer container = new CompositionContainer(catalog);
            CompositionBatch batch = new CompositionBatch();
            batch.AddPart(this);
            container.Compose(batch);
        }
    }

    public interface ILogInterface
    {
        void LogMessage(string message);
    }
}

 

Now comes to the implementation of this interface. I created a separate component, which implement this interface and export it. Here is a complete code of this.

Code Snippet
using System;
using System.ComponentModel.Composition;

namespace MefContainer
{
    [Export(typeof(ILogInterface))]
    class Component1 : ILogInterface
    {        
        public void LogMessage(string message)
        {
            Console.WriteLine("From Component 1 Message = {0}", message);
        }
    }
}

 

I can make as many component as I want to provide different implementation of this interface. One implementation may log in database, other may log in event viewer, another one may log in file system, another one may stream the logging and so on. Just for simplicity my other implementation also write information on console window, but with slightly different message. Here is our second implementation of the same interface in another component.

Code Snippet
using System;
using System.ComponentModel.Composition;

namespace MefContainer
{
    [Export(typeof(ILogInterface))]
    class Component2 : ILogInterface
    {
        public void LogMessage(string message)
        {
            Console.WriteLine("From Component 2 Message = {0}", message);
        }
    }
}

 

Now comes to the client side. Using this container in our client is very straight forward. Just create an object of container class and traverse the container property using foreach loop and call the method for every component.

Here is complete implementation of our client.

Code Snippet
using MefContainer;

namespace MefDirCatalogClient
{
    class Program
    {
        static void Main(string[] args)
        {
            Container cont = new Container();

            foreach (var component in cont.LogInterfaces)
            {
                component.LogMessage("Hello World");
            }
        }
    }
}

If we copied both implementation components (DLLs) in the same folder where executable is (container dll to be more precise) It display the following message at the console window.

From Component 1 Message = Hello World
From Component 2 Message = Hello World

It shows that we are calling same method from both component. Now if we just remove one DLL from the same location then appropriate message will disappear. In future if we are going to write another implementation, then we simply implement this interface, export it and placed the DLL in the same folder, our program will automatically pick it.

Advertisements

Responses

  1. Very nice application of MEF.

    • Thanks to like it.

  2. […] of using DirectoryCatalog with ImportMany attribute to load multiple DLL at run time using MEF here. This is a good starting point to develop plug in architecture. But when developing plug in […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Categories

%d bloggers like this: