Annotated version of the MEF framework

一笑奈何 提交于 2021-02-02 01:44:12

MEF is a complement to the Unity Container, unlike the Unity Container, which is in fact a Object life cycle management framework, which coupled with other component, provides the object building, object lifecycle management, service registration and many others,  and the MEF is a tool/libraries for Interface/service discovery, and objct composition, object discovery and examination by their metadata, it also provide certain means for the component to communicate with each other. 

The offical site for details on the Managed Extensibility Framework (MEF) is available at the location of http://msdn.microsoft.com/en-us/library/dd460648.aspx, where you can find that the following example are based on . 

Though the MSDN "Managed Extensibility Framework (MEF)" has more content, my post only has the necessary information on the examples that we have discussed and we will only present the example with more elaborated/uncovered content from the offical MEF site. 

First, we need to have some means of compostion, this is done via the "Composition Container" and the so called "Catalogs". 

the "Composition Container" is where the components are composited, the catalogs list possible locatoin to look for possible parts.. (the two most commonly used Catalogs include the following: 1. the Current running type's Assembly 2. a directory which has assemblies which could possibly export the parts). 

A MEF component, called a Part, declaratively specifies both its dependencies (Known as imports) and what capabilities (known as exports) it makes available.

know that we have the basic knowledge, let's first declare a container. the code is something liek below. 

// we need to declare new caontainer.
        private CompositionContainer _container;
The code below will show how generally we compose the MEF container, code as below. 

private Program()
        {
            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
            // catalog.Catalogs.Add(new DirectoryCatalog(@"C:\Temp\dev\workspaces\dotnet\clr\trunk\src\assemblies\mef\ExtendedOperations\bin\Debug"));

            _container = new CompositionContainer(catalog);


            try
            {
                this._container.ComposeParts(this);
            }
            catch (CompositionException compositionException)
            {
                Console.WriteLine(compositionException.ToString());

            }
        }
As you can see, it create a AggregateCatalog, passing the assembly of the current type. and the Container is initialized to the CompositeContainer with the appropriate catalogue. 

the core part is started by the "Container.ComposeParts(this);" call, which does all the wiring and object constructing. 

Then, we make our Program class a parts, to perform its action, we will make a ICalculator part of its imports, here is how you declare a import. 

// the Program will use the Calculator instance, so here it is
        [Import(typeof(ICalculator))]
        public ICalculator calculator;
The most important thing here is the "Import" attribute, which states something to be an import, and the contract will be fullfilled by the composition engine  when the object is composed. 

Then, it is now the ICalculator interface.

public interface ICalculator
    {
        string Calculate(string input);
    }

Though we can see that we have specified the type of the ICalculator as a parameter to the Import attribute, this is not absolutely necessary, if you don't provide the type value, it can figure it out from the type of the field.

then, let's see the real implementor of the ICalculator class, MySimpleCalculator. 

[Export(typeof(ICalculator))]
    class MySimpleCalculator : ICalculator
    {
        // ...
    }

Do you see the "Export" Attribute, which declares to the world that if you need a ICalculator interface, here I am , I am a ICalculator parts, which can used to satisify the requirement from other parts's demands on the interfaces of "ICalculator".


now, let make the Program parts the consumer of the ICalculator interface. Here is now how the Main methods looks like. 

static void Main(string[] args)
        {
            Program p= new Program();
            String s;
            Console.WriteLine("Enter Command");

            while (true)
            {
                s = Console.ReadLine();
                Console.WriteLine(p.calculator.Calculate(s));
            }
        }
THe main methods does is basically constantly read from input and delegate the work to ICalculator to do the calculation.

What shall we have for the members of the MySimpleCalculator, we will carry out the real math calculation.So we define those two interfaces. 

public interface IOperation
    {
        int Operate(int left, int right);
    }
and 

public interface IOperationData
    {
        Char Symbol { get;  }
    }
and the ICalculator interface need to support more than one type of operation, such as Adding, and substracting. Again, witih MEF, we will make them importe
[ImportMany]
IEnumerable<Lazy<IOperation, IOperationData>> operations;
d, but how to do declare that they should be imported in a way that the total number of import is yet unknown.Here is the solution. 

Lazy<T, TMetadata> is a type provided by MEF to hold indirect references to exports. Here, in addition to the exported object itself, you also get export metadata, or information that describes the exported object. Each Lazy<T, TMetadata> contains an IOperation object, representing an actual operation, and an IOperationData object, representing its metadata.  

Suppose one of the supported work is '+', how do we define the '+' operation?

[Export(typeof(IOperation))]
    // the MEF will create implicit a class which has the proprties based on the names of the metadata provided 
    // so a class which has a Symbol field and the Symbol field has the value called '+'
    // you can tell that the type of Symbol field is Char
    //
    [ExportMetadata("Symbol", '+')]
    public class Add  : IOperation
    {
        public int Operate(int left, int right)
        {
            return left + right;
        }
    }

As  you might have discovered from the comment that I put into the code above, we don't have a explicitly defined IOperationData class, but instead, the ExportMetaData will implicitly create a class which will base its member on the name of the metada, in our case, the class will have one member called "Symbol", and its value is '+', and the type is of course Char. Author here used to create the Medta with value of "Symbol" as string "+", and thus has lead to hard to trace error! :(

Now, let's do the real thing, here is the code that does calculation by operation that it supports. 

public string Calculate(string input)
        {
            int left;
            int right;
            Char operation;

            int fn = FindFirstNonDigit(input);
            if (fn < 0) return "Could not parse command";

            try
            {
                left = int.Parse(input.Substring(0, fn));
                right = int.Parse(input.Substring(fn + 1));
            }
            catch
            {
                return "Could not parse command";
            }

            operation = input[fn];
            foreach (Lazy<IOperation, IOperationData> i in operations)
            {
                if (i.Metadata.Symbol.Equals(operation)) return i.Value.Operate(left, right).ToString();
            }

            return "Operation Not Found!";
        }

        private int FindFirstNonDigit(string input)
        {
            for (int i = 0; i < input.Length; i++)
            {
                if (!Char.IsDigit(input[i])) return i;
            }
            return -1;
        }
Now, wiring it out and do a small compilation, then you can start running the code. However, now it only support 1+2, or 3+4 types of calculation. 

So comes the need to extend the MEF application, there are some means, one is define a new IOperation class in the same assemblies , the code is as below.

[Export(typeof(IOperation))]
    [ExportMetadata("Symbol", '-')]
    public class Subtract : IOperation
    {
        public int Operate(int left, int right)
        {
            return left - right;
        }
    }
while this does not require a code recompilation, but what if we need to have to reference some types from the references assembies, and we don't know the name of the assemblies. so what can we do?

remember that we said that we can have some catalog which list the assemblies from a directory, we can do this:

catalog.Catalogs.Add(new DirectoryCatalog(@"C:\Temp\dev\workspaces\dotnet\clr\trunk\src\assemblies\mef\ExtendedOperations\bin\Debug"));

now the final code of the Program code will looks like this 

private Program()
        {
            var catalog = new AggregateCatalog();
            catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
            catalog.Catalogs.Add(new DirectoryCatalog(@"C:\Temp\dev\workspaces\dotnet\clr\trunk\src\assemblies\mef\ExtendedOperations\bin\Debug"));

            _container = new CompositionContainer(catalog);


            try
            {
                this._container.ComposeParts(this);
            }
            catch (CompositionException compositionException)
            {
                Console.WriteLine(compositionException.ToString());

            }
        }

And we can create another assemblies, which is called "ExtendedOperation", which has the following code 

[Export(typeof(MefDemo1.IOperation))]
    [ExportMetadata("Symbol", '%')]
    public class Mod : MefDemo1.IOperation
    {
        public int Operate(int left, int right)
        {
            return left%right;
        }
    }

References:

Managed Extensibility Framework (MEF) - http://msdn.microsoft.com/en-us/library/dd460648.aspx 





标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!