.NET Tutorials, Forums, Interview Questions And Answers
Welcome :Guest
 
Sign In
Register
 
Win Surprise Gifts!!!
Congratulations!!!


Top 5 Contributors of the Month
david stephan

Home >> Articles >> .Net Framework >> Post New Resource Bookmark and Share   

 Subscribe to Articles

MEF Features

Posted By:Mahadesh Mahalingappa       Posted Date: August 06, 2011    Points: 200    Category: .Net Framework    URL: http://www.dotnetspark.com  

In this article I would be explaining some of the useful features MEF Provides.
 

MEF Features - With Examples:

In this article I would be explaining some of  the useful features MEF Provides . In my article MEF WCF Startup MEF with WCF Startup I had used the Import and Export attributes . Lets move on now and check what else is there .

InheritedExport Attribute :

MEF has a really nice little gem of a feature called InheritedExport.  It lets you put an InheritedExport attribute on an interface or a base, which autoimatically propagates to inheritors / implementers. Ideal for meffing up existing framework libraries without pushing an MEF details to the consumer.


Create the Interface and add the attribute InheritedExport to it as shown below .

[InheritedExport]
interface I
{
    void display();
}

 By using the attribute InheritedExport we let the container to compose those parts which have inherited the interface I .

Lets say we have a class ClassA  as shown below which implements the interface I.


class ClassA : I
{
    public void display()
    {
        Console.WriteLine("Class A is Imported");
    }
}

Now create another class say Importer . We are going to put the importing logic here .

 
public class Importer
{
    [Import]
    I i;

    public void Run()
    {
        var catalog = new AssemblyCatalog
        (System.Reflection.Assembly.GetExecutingAssembly());

        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);

        i.display();

    }
}

Advantages :

  1. The Most obvious advantage here is the Importer class is completely ignorant of the Type that is being referred. This is the most amazing feature which Mef offers through the InheritedExport Attribute.
  2. I could very easily now replace the ClassA with ClassB as shown below :

class ClassB : I
{
    public void display()
    {
        Console.WriteLine("Class B is imported");
    }
}

And now when I run the program the ClassB is imported . This is what we want in our application .

So MEF not only brings Extensibility to our applications it also does what a AOP would do for you.  Reduce , If not Completely Remove The Dependency among the classes .

Now we can simply call our Importer Class in Main as shown.

class Program
{

    static void Main(string[] args)
    {
        Importer imp = new Importer();
        imp.Run();
        Console.ReadLine();
    }
}

ImportMany Attribute :


ImportMany is a way to express that you want to import all "items" into a type capable of exposing many items (usually IEnumerable). This is another very important attribute which makes Extensibility so simple .

Lets Define a class ClassB now .

class ClassB : I
{
    public void display()
    {
        Console.WriteLine("Class B");
    }
}

Lets make the changes to Importer Class . This time I would create a Type IEnumerable .



public class Importer
{
    [ImportMany(typeof(I))]
    private IEnumerable objects { get; set; }

    public void Run()
    {
        var catalog = new AssemblyCatalog
        (System.Reflection.Assembly.GetExecutingAssembly());

        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);


    }
}

Once I have added the ImportMany attribute on the IEnumerable . I would be able to  import all the objects that are of the Type I.

Below is the screenshot . Objects is composed of two parts here both of Type I .



Once we have imported the objects of Type I we could either loop through them as shown below :
  
foreach (I i in objects)
{
    i.display();
}

We could also use Linq to query specific object as shown below :

// Query the object of ClassA

var classa = (from o in objects
              where o.GetType().ToString().Contains("ClassA")
              select o).First();

// Using the Object just call the method 
classa.display();

ImportingConstructor :

There would be situations where we would run into scenarios where we would have multiple constructors in the Part which would like to Import in that situation you would want to mention which Constructor we want to use while Importing . ImportingConstructor offers you that feature .

Ok lets look into this attribute with a short example :


Lets just code a simple class A as shown below :

1.  A simple class A and lets export it.
  [Export]
  class A
  {
  
  }

  2. Lets add two constructors  . One default and another which accepts a single parameter as string .

[Export]
class A
{
    public string message;
    
    public A()
    {
    }
    
    public A([string str)
    {   
        message = str;
    }

}
3.  I would now choose my ImportingConstructor . The constructor I want the catalog to use when the part is being composed . Really Good feature of MEF .

 
[Export]
class A
{
    public string message;

    public A()
    {

        //  This constructor would not be called . Since we use a ImportingConstructor attribute

    }

    [ImportingConstructor]
    public A(string str)
    {
        message = str;
    }

}

  1. In the Importing Constructor , I need to add a custom parameter and Import it which I would later in my code create an instance of specific value.  So add an [Import("Msg")] to the ImportingConstructor as shown below .



 
[Export]
class A
{
    public string message;

    public A()
    {

        //  This constructor would not be called . Since we use a ImportingConstructor attribute

    }

    [ImportingConstructor]
    public A([Import("Msg")]string str)
    {
        message = str;
    }

}

4.  Finally add a method say show to the Class and our class is ready .

 
[Export]
class A
{
    public string message;

    public A()
    {

        //  This constructor would not be called . Since we use a ImportingConstructor attribute

    }

    [ImportingConstructor]
    public A([Import("Msg")]string str)
    {
        message = str;
    }

    public void show()
    {
        Console.WriteLine("Welcome" + message);
    }
}


Now lets move to our Program class . Here I will have to perform couple of new things to create an instance of specific value which I have imported previously .

Lets pass the value for the Imported Msg parameter as shown below :

  container.ComposeExportedValue("Msg", "How are You!!!");

Getting the Specific Exported Value  as shown below :

  var person = container.GetExportedValue();

Program class is shown below :

class Program
{
    [Import]
    A a;


    public void Run()
    {
        var catalog = new AssemblyCatalog
        (System.Reflection.Assembly.GetExecutingAssembly());

        var container = new CompositionContainer(catalog);
        container.ComposeExportedValue("Msg", "How are You!!!");
        container.ComposeParts(new Program());


        var person = container.GetExportedValue();
        person.show();

    }
    static void Main(string[] args)
    {
        Program p = new Program();
        p.Run();
        Console.ReadKey();
    }
}


When we give it a Run we would get

Welcome How are You!!!

Director Catalog :

To discover all the exports in all the assemblies in a directory MEF offers you with a attribute called System.ComponentModel.Composition.Hosting.DirectoryCatalog.

Lets discuss the attribute with a example .

Let us create a new Class Library Project named as Extensions .

Lets create a new Interface here .

[InheritedExport]
public interface ILogger
{
    void Write(string message);
}
 
Note that I have added the InheritedExport attribute to it.

Now let me create a ConsoleLogger Component which would implement the interface Ilogger .

This would be one of my Extension Component .


public class ConsoleLogger : ILogger
{
    public ConsoleLogger()
    {
    }
    public void Write(string message)
    {
        Console.WriteLine(message);
    }
}

Create a Console project and add a class Application . Add a reference of Class Library Dll.

Composing the Container :

  var catalog = new DirectoryCatalog(@"C:\Extensions\Extensions\bin\Debug");
  var container = new CompositionContainer(catalog);
  container.ComposeParts(this);

Pass the directory Path where the Class Library Dll exists .

Import the Component .


[Import]
public ILogger Logger;

Call the Method using the Component .


Logger.Write("Hello World");

Extension is loaded and works perfectly .


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.

catalog.Refresh();

Once it rescans, recomposition will occur. The part would be added to the catalog and would be ready to use .


Dynamic Instantiation :


Consider ClassB as shown below :

[Export]
class B
{
    public void display()
    {
        Console.WriteLine("Display Method Of B Class");
    }
}

I can use the Dynamic Instantiation as well with MEF as shown below :


class Program
{
    [Import]
    Lazy b;

    void Run()
    {
        var catalog = new AssemblyCatalog
        (System.Reflection.Assembly.GetExecutingAssembly());

        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);


        b.Value.display();
    }
}


The Object would only be instantiated when it is actually used as b.Value .  The IsValueCreated property would be set to True when it is used .






PartNotDiscoverable :  ( Avoid Discovery of a Part )

This is something similar to private access modifier . You don't want a particular component to be discovered by the Catalog so use this attribute to hide the component .

For example, the part may be a base class intended to be inherited from, but not used. There are two ways to accomplish this.
Use the abstract keyword on the part class. Abstract classes never provide exports, although they can provide inherited exports to classes that derive from them.


If the class cannot be made abstract, you can decorate it with the PartNotDiscoverable attribute. A part decorated with this attribute will not be included in any catalogs. The following example demonstrates these patterns. 
 

[Export]
public abstract class A
{
    //This part will not be discovered
    //by the catalog.
}

[PartNotDiscoverable]
[Export]
public class B
{
    //This part will also not be discovered
    //by the catalog.
}


Creation Policies :


When a part specifies an import and composition is performed, the composition container attempts to find a matching export. If it matches the import with an export successfully, the importing member is set to an instance of the exported object. Where this instance comes from is controlled by the exporting part's creation policy.


The two possible creation policies are shared and non-shared.

Create a Class B as shown below:


 

[Export]
[PartCreationPolicy(CreationPolicy.Shared)]
class B
{
    public void show()
    {
        Console.WriteLine("B - object is Accessed");
    }
}

Note the PartCreationPolicy Attribute on the class B . I have set the CreationPolicy.Shared . So this part will be shared between every import in the container for a part with that contract. When the composition engine finds a match and has to set an importing property, it will instantiate a new copy of the part only if one does not already exist; otherwise, it will supply the existing copy.


Create a Class A as shown below :

[Export]
class A
{
    [Import(RequiredCreationPolicy = CreationPolicy.Shared)]
    B b;
    public void display()
    {
        b.show();
    }
}

Note that I am importing the Object of B Class and I have set the CreationPolicy as Shared.


Now I can access the Parts from my Program : I can just Import the Part A in the Program . The Part A will take care of Importing B . So the Program Class would look as follows :

class Program
{
    [Import]
    A a;
    public void run()
    {
        var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
        var container = new CompositionContainer(catalog);
        container.ComposeParts(this);
        a.display();
    }
    static void Main(string[] args)
    {
        Program p = new Program();
        p.run();
        Console.ReadKey();
    }
}


Note that Changing the Creation Policy on just one of the places would result in an error . You would have to change the Creation Policy on Both to make the application work .


Now lets add another part to our application . Lets say the CreationPolicy for the imported part B is set as Nonshared as shown below :


 

[Export]
class C
{
    [Import(RequiredCreationPolicy = CreationPolicy.NonShared)]
    B b;
    public void display()
    {
        b.show();
    }
}

This would not work for sure . No valid exports were found that match the constraint Exception results .


To make this scenario work just remove the part creation Policy on the Class B. The Default Part creation is Any . hence now it would work fine .


Disposing MEF Objects :


The MEF container is responsible for managing the lifetime of your exports, so regardless of which CreationPolicy is used (default is Shared), the final call to the container's Dispose method will dispose of any Export instances (which wrap your actual class instances). Also, calling Dispose on an Export instance will cause the actual class instance to dispose also.


Let us check out an example of Dispose Method as shown below :

class Program
{
    [Import]
    A a;
    public void run()
    {
        CompositionContainer container = null;
        try
        {
            var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            container = new CompositionContainer(catalog);
            container.ComposeParts(this);
            a.display();
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            container.Dispose();
        }
    }
    static void Main(string[] args)
    {
        Program p = new Program();
        p.run();
        Console.ReadKey();
    }
}
[Export]
class A
{
    public void display()
    {
        Console.WriteLine("The Object A is accessed");
    }
}

 
Exporting MetaData :


Exports can provide additional information about themselves known as metadata. Metadata can be used to convey properties of the exported object to the importing part.


The importing part can use this data to decide which exports to use, or to gather information about an export without having to construct it. For this reason, an import must be lazy to use metadata.


I used this example
http://codepaste.net/jnheqa as my reference for posting this section .
Lets Create a Enumeration Type Category .
 

public enum Category
{
    Sports, National, International, News
}


public interface IPlug
{
    void Write();
}
public interface IMetadataView
{
    Category[] Categories { get; }
}

Create a class National which would implement the Type IPlug . Note the ExportMetadta Attribute attached on the class .

[Export(typeof(IPlug))]
[ExportMetadata("Categories", Category.News, IsMultiple = true)]
public class National : IPlug
{
    public void Write()
    {
        Console.WriteLine("National");
    }
}


Lets create the Program Class :
Lets us create the Program class now.

class Program
{
    static void Main(string[] args)
    {
        var instance = new Program(); // Creating instance of Program
        var cat = new AssemblyCatalog(typeof(Program).Assembly); // Creating catalog
        var container = new CompositionContainer(cat);
        container.ComposeParts(instance); // Compose parts by passing the Program instance 

        foreach (var plug in instance.Plugins)
        {
            foreach (var category in plug.Metadata.Categories)
            {
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.Write("{0}, ", category);
                Console.ResetColor();
            }
            Console.WriteLine(string.Empty);
            plug.Value.Write(); // Get Value using Lazy instantiation
        }
        Console.ReadLine();
    }
    [ImportMany]
    public Lazy[] Plugins
    {
        get;
        private set;
    } // Use of Lazy Instantiation for Plugins
}


 Lets give it a run .




Alternative Method Of Exporting MetaData : Use of  MetadataAttribute

We can make use of the MetadataAttribute and Create a class CategoryAttribute which would extend the Attribute Class . I like this approach .
 

[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class CategoryAttribute : Attribute
{
    public CategoryAttribute(Category category)
    {
        Categories = category;
    }
    public Category Categories
    {
        get;
        private set;
    }
}


Once we have created the CategoryAttribute we can just Create a class Cricket which would inherit from the IPlug .
Lets give it a run now .

[Export(typeof(IPlug))]
[Category(Category.Sports)]
public class Cricket : IPlug
{
    public void Write()
    {
        Console.WriteLine("Cricket");
    }
}


Conclusion :

So friends I have tried to put together here some of the features that MEF offers . Please let me know your thoughts about the article .  I plan to compose Some Advanced Mef Features in my next article .  Thanks . Happy Coding .


 Subscribe to Articles

     

Further Readings:

Responses

No response found. Be the first to respond this post

Post Comment

You must Sign In To post reply
Find More Articles on C#, ASP.Net, Vb.Net, SQL Server and more Here

Hall of Fame    Twitter   Terms of Service    Privacy Policy    Contact Us    Archives   Tell A Friend