MEF or Managed Extensibility Framework – Creating a Zoo and Animals


ANOOP MADHUSUDANAN

Vote on HN

MEF or Managed Extensibility Framework is cool. Firstly, it allows you to decouple your components pretty easily. Secondly, it supports various component discovery scenarios, and enables you to write better frameworks. In this post, I’ll cover few basic aspects of MEF. image

MEF classes reside in the assembly System.ComponentModel.Composition.dll – Creating plug-in frameworks is quite easy with MEF. The main application can Import plug-ins, marked with an Export attribute.

MEF terminology is pretty simple. To start with, you can Export and Import your Parts. A Part is anything you export or import - be it a class, method, or property. Any such composable Part should be attributed with either the Export or Import attributes (You can find those attributes in System.ComponentModel.Composition namespace). For simple scenarios, you may use the Export attribute along with a Contract to mark your part as exportable.

Also, you may use the Import attribute to specify where you want to import the available exported parts. MEF will do the back ground work of dynamically discovering information about parts, to resolve and import them where ever you specify, based on the Contracts. This step is called Composing the Parts. MEF discovers information about these parts from various sources, i.e Catalogs, or you may add them directly to the container. For example - you may use an assembly catalog to point MEF to a specific assembly to grab the exported parts from there, a directory  catalog to point to a set of assemblies in a folder etc.

To make things straight, let us create a simple Zoo example with MEF, and we’ll cover MEF terminologies on the go.

Visiting the MEF Zoo

Alright, so to start with, assume that you are creating a Zoo application, where Animals can be ‘plugged-in’. I.e, if you are creating a new Animal, you don’t really need to rebuild your zoo. Needless to say – that means, your Animals will be loosely coupled with your Zoo.

We have the following projects in our MefZoo solution.

  • MefZoo.Lib – A simple library for keeping our contracts, to identify parts while exporting and importing them.
  • MefZoo.Animals – A couple of concrete animals for our Zoo.
  • MefZoo – Here is where most of the work happens – like grabbing parts from Catalogs, composing them etc.

image

In our MefZoo.Lib project, we’ve a simple IAnimal class. If any one need to create an Animal for your Zoo, IAnimal is the interface or contract they should use, to create their concrete Animal.

The IAnimal interface is pretty simple.

public interface IAnimal
    {
        string Name { get; }
    }

In MefZoo.Animals project, we have a reference to MefZoo.Lib. There we’ve a couple of ‘concrete’ animals. For now, let us have a Lion and Rabbit in our Zoo. If you are curios, here is the Lion and Rabit classes.

  
[Export(typeof(IAnimal))]
    public class Lion : IAnimal
    {
     public string Name
        {
            get { return "Lion1"; }
        }
    }

[Export(typeof(IAnimal))]
 public class Rabbit : IAnimal
    {
     public string Name
        {
            get { return "Rabbit1"; }
        }
    }

If you’ve observed, we are using the Export attribute to ‘export’ our animals, so that they can be ‘imported’ to our Zoo later. Time to visit our MefZoo project. We have a simple Zoo class there – and as you'd expect we’ve a collection of Animals there.

public class Zoo
    {
        [ImportMany(typeof(IAnimal))]
        public IEnumerable<IAnimal> Animals { get; set; }
    }

And before we actually discuss about loading animals to the Zoo, you might want to note that we have the build path of MefZoo as ..\bin and that of MefZoo.Lib as ..\bin\Extensions. Essentially, you need to make sure MefZoo.Lib.dll will be under the Extensions folder in MefZoo.exe's path

Alright, now it’s time for the main action. Here is the Main method in Program.cs.

The Main method simply creates the Zoo object, and pass the same to LoadAnimals.
 static void Main(string[] args)
        {
            Zoo z=new Zoo();
            LoadAnimals(z);
           foreach (var animal in z.Animals)
                Console.WriteLine(animal.Name);
            Console.ReadLine();
        }

As explained earlier, to compose all these parts together, we need to

  1. Specify the catalogs from where these parts are coming.  If you have a look at the below code, you’ll find that we are pointing MEF to all libraries under the Extension folder of the main application, and also to the current assembly, for discovering the parts (Remember, anything that is marked with either Export or Import is a Composable Part).
  2. Create a container, and compose the parts
    static void LoadAnimals(Zoo zoo)
        {            
            try
            {
                //A catalog that can aggregate other catalogs
                var aggrCatalog = new AggregateCatalog();
                //A directory catalog, to load parts from dlls in the Extensions folder
                var dirCatalog = new DirectoryCatalog(Path.GetDirectoryName
                    (Assembly.GetExecutingAssembly().Location) + "\\Extensions", "*.dll");
                //An assembly catalog to load information about part from this assembly
                var asmCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());

                aggrCatalog.Catalogs.Add(dirCatalog);
                aggrCatalog.Catalogs.Add(asmCatalog);

                //Create a container
                var container = new CompositionContainer(aggrCatalog);

                //Composing the parts
                container.ComposeParts(zoo);

            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }            
        }
At this point, if you run the application, you'll find the following output.
Lion1
Rabbit1

Feeding your MEF Animals

Now you have the Zoo, obviously the next problem is feeding your animals. For this, we’ll need our animals to make some grumble sound (a call back), to call the attention of zoo keepers. (If you are tired with story telling, this section will show you how to inject a call back mechanism to the loaded Animals). First of all, let us define a GiveFood method in our Zoo class. It is self explanatory. Only thing you might notice is, we are exporting GiveFood method as a part. How ever, we are using "AnimalFood" as the contract name, instead of a type. This is allowed, and valid in MEF – You can either use a string or a type, or both together, as a contract.

public class Zoo
    {
        [ImportMany(typeof(IAnimal))]
        public IEnumerable<IAnimal> Animals { get; set; }

        [Export("AnimalFood")]
        public string GiveFood(string animalType)
        {
            switch (animalType.ToLower())
            {
                case "herbivores":
                    return "GreenGrass";
                case "carnivores":
                    return "Readmeat";
                default:
                    return "Waste";
            }
        }

    }
Now, we have the facility in our Zoo to give food to Animals. To enable our Animals to consume food, let us extend our IAnimal class a bit. Here is the new IAnimal class - We just added a delegate property, synonymous to the GiveFood method, so that MEF can hook up the GiveFood method later, when importing the exported GiveFood. 
public interface IAnimal
    {
        string Name { get; }
        Func<string ,string> GiveMeFood { get; set; }
    }

And well, let us re-wire the Lion class a bit, so that the Lion can periodically call back the zoo keeper to request some food.

 [Export(typeof(IAnimal))]
    public class Lion : IAnimal
    {
        Timer t;

        //MEF will inject the call back here
        [Import("AnimalFood")]
        public Func<string , string> GiveMeFood { get; set; }

        public string Name
        {
            get { return "Lion1"; }
        }

        //Let us use a timer to get food and eat it regularly
        public Lion()
        {
            t = new Timer(1000);
            t.Elapsed += (sender, args) =>
            {
                string food = GiveMeFood("carnivores");
                Console.WriteLine("Lion eating " + food);
            };
            t.Start();
        }       
    }
Nothing fancy there, we just have a timer there to make the Lion hungry. And let me do the same modifications for rabbit as well. Run the application, and you'll see Rabbit and Lion eating their food (The timer interval for rabbit is lesser than the lion).

image

Also, I just want to point that we havn’t made any modifications to the part composition code, for exporting a call back method for our Animals. One interesting aspect here is, the Zoo can decide what food to supply for each animal (probably based on available stock). And this is an example of how you can use MEF to achieve decoupling even in fine-grained systems.

You may also need to visit the official MEF site, http://www.codeplex.com/MEF

© 2012. All Rights Reserved. Amazedsaint.com