Reader Level:
ARTICLE

What is MEF?

Posted by Praneeth Kumar Articles | Visual Studio .NET May 16, 2011
In this article I will be trying to simplify the importance of MEF and where can MEF be applied. Let me ask a question before explaining the uses of MEF.
  • 2
  • 0
  • 8778
Download Files:
 

Introduction

Application requirements change frequently and software is constantly evolving. As a result, such applications often become monolithic making it difficult to add new functionality. The Managed Extensibility Framework (MEF) is a new library in .NET Framework 4 that addresses this problem by simplifying the design of extensible applications and components.

In this blog I will be trying to simplify the importance of MEF and where can MEF be applied. Let me ask a question before explaining the uses of MEF. Imagine you have an application which communicates with SQL database. One fine day your client asks you to migrate the data layer from SQL database to Oracle or MYSql or so on… How will you accommodate the change without having an impact on the application? Will you not want a database layer wherein any change in the database layer does not force you to change even a single line of code any where? Will you not want a database layer such that a dll is picked accommodating the database changes instead of modifying the existing code? Will you not want a form of deployment such that the dll is picked at runtime accommodating the new database changes?

If the answer is yes for all the above questions; you should have explored MEF by now. Today I will be taking you through how to create a database layer using a MEF such that a new database change can be accommodated by deploying a dll in the database folder.

Prerequisites

  • Visual Studio 2010
  • Knowledge of C#.Net

Project Flow

Add a class library project having namespace BSIL.POC.MEFDatabase.

MEF1.gif

Add an interface named IDatabase.cs

MEF2.gif

In the IDatabase.cs we have the following code:

namespace BSIL.POC.MEFDatabase
{
    public interface IDatabase
    {
        string DatabaseName { get; set; }
 
        bool Login(string userName, string password);
    }
}


Add another class project having namespace BSIL.POC.MEFDatabase.SQL . Add a class named Database.cs. This class will connect to SQL database and implements IDatabase interface as below:

using System;
using System.ComponentModel.Composition;

namespace BSIL.POC.MEFDatabase.SQL
{

    [Export(typeof(IDatabase))]
    public class Database : IDatabase
    {
        private string _databaseName=string.Empty;

        public Database()
        {
            _databaseName = "SQL";
        }

        public bool Login(string userName, string password)
        {
            if (userName != string.Empty && password != string.Empty)
            {
                return true;
            }

            return false;
        }

        public string DatabaseName
        {
            get
            {
                return _databaseName;
            }
            set
            {
                _databaseName = value;
            }
        }
    }
}


Add reference for System.ComponentModel.Composition and BSIL.POC.MEFDatabase in BSIL.POC.MEFDatabase.SQL project. Important for MEF is the "Export" attribute from the "System.ComponentModel.Composite" (MEF) Namespace.

Export means: This is a "IDatabase" plugin.

MEF3.gif

MEF4.gif

Add another class project having namespace BSIL.POC.MEFDatabase.Oracle .

MEF5.gif

Add a class named Database.cs. This class will connect to Oracle database and implements IDatabase interface as below:

using System;
using System.ComponentModel.Composition;

namespace BSIL.POC.MEFDatabase.Oracle
{
 
    [Export(typeof(IDatabase))]
    public class Database : IDatabase
    {
        private string _databaseName=string.Empty;
 
        public Database()
        {
            _databaseName = "Oracle";
        }

        public bool Login(string userName, string password)
        {
            if (userName != string.Empty && password != string.Empty)
            {
                return true;
            }

            return false;
        }
 
        public string DatabaseName
        {
            get
            {
                return _databaseName;
            }
            set
            {
                _databaseName = value;
            }
        }
    }
}


Add reference for System.ComponentModel.Composition and BSIL.POC.MEFDatabase in BSIL.POC.MEFDatabase.Oracle project.

MEF6.gif

MEF7.gif

Console App

Create a console app having namespace BSIL.POC.ConsoleApp. Add reference of System.ComponentModel.Composition and BSIL.POC.MEFDatabase.

Plugin directory

Your application needs to know where plugins are placed, that´s why we create a "PlugIns" dictionary. This plugin directory will have the dll which we want to use at run time. It could be dll of either SQL or Oracle or anything which implements IDatabase.

MEF8.gif

For ease of use we write a Container.cs class so that it can be accessed by Program.cs or any other class file. The Container.cs will have code as below.

Container.cs

using System.ComponentModel.Composition;
using BSIL.POC.MEFDatabase;
using System.ComponentModel.Composition.Hosting;

namespace BSIL.POC.ConsoleApp
{
   public class Container
    {

       [Import(typeof(IDatabase))]
       public IDatabase Database { get; set; }

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


Plugins are manged with parts and catalogs. We tell our catalog to search for plugins in this assembly. Through the container we tell MEF that here is a plugin interface (the list with the import attribute) and start the "compose" process.

Directory Catalog

To discover all the exports in all the assemblies in a directory one would use the [System.ComponentModel.Composition.Hosting.DirectoryCatalog].

DirectoryCatalog catalog = new DirectoryCatalog("Extensions");

If a relative directory is used it is relative to the base directory of the current AppDomain.

The DirectoryCatalog will do a one-time scan of the directory and will not automatically refresh when there are changes in the directory. However, you can implement your own scanning mechanism, and call Refresh() on the catalog to have it rescan. Once it rescans, recomposition will occur.

var catalog = new DirectoryCatalog("Extensions");

The entry point of BSIL.POC.ConsoleApp.Program.cs. The code will look like below:

using System;

namespace BSIL.POC.ConsoleApp
{
    class Program
    {

        static void Main(string[] args)
        {
 
            Console.WriteLine("Loading the database dll using directory catalog....");
            Container container = new Container();

            Console.WriteLine(string.Empty);
            Console.WriteLine("Connecting to database....");
            bool isLoggedIn = container.Database.Login("username", "password");
 
            Console.WriteLine(string.Empty);
            Console.WriteLine("I am {0}logged in using {1}", isLoggedIn ? string.Empty : "not ", container.Database.DatabaseName);
            Console.ReadLine();
        }
    }
}

Inside the "Program" class is a "IDatabase" property, which is decorated with the "Import" attribute from the MEF namespace.

Import means: I took everything of type IDatabase inside plugins directory.

Initially I have put Sql dll inside plugins directory as in screenshot below:

MEF9.gif

When I run the application I see the output as below

Output seen when Sql dll is placed inside Plugins folder

MEF10.gif

Later I delete the Sql dll and put Oracle dll. When I run the application I see the output as below

MEF11.gif

Output seen when Oracle dll is placed inside Plugins folder

MEF12.gif

Finally the project structure should look like this

MEF13.gif

Conclusion

Now at any point of time we can change the implementation of datalayer by just replacing the dll either SQL, Oracle or any dll which implements IDatabase interface. We can implement the same flow using Unity blocks. I felt Unity could be a better match as it is more easily configurable and more developer friendly. Next article we can see how to implement the same Database change using Unity blocks.
 

COMMENT USING

Trending up