Talk with examples and experience the scalability that MEF brings, let ' s rock!!!
1: New Console program SimpleCalculator
The program to be implemented here is SimpleCalculator, as the name suggests: a simple calculator.
So we need to define an interface to compute:
public interface ICalculator
{
String Calculate (string input);
}
The program's code is as follows:
Class Program
{
Private Compositioncontainer _container;
[Import (typeof (ICalculator))]
Private ICalculator Calculator;
Public program ()
{
var catalog = new Aggregatecatalog ();
Catalog. Catalogs.add (New Assemblycatalog (typeof). Assembly));
var catalog = new Assemblycatalog (typeof). Assembly);
_container = new Compositioncontainer (catalog);
Try
{
This._container.composeparts (this);
}
catch (Compositionexception compositionexception)
{
Console.WriteLine (Compositionexception.tostring ());
}
}
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));
}
}
}
What the MEF is trying to do is look for plug-ins, and the traditional way to implement plug-ins is to use interfaces, that is, to declare an interface, and then use a configuration file to configure which implementation class the interface uses.
Microsoft is aware of this requirement, so it provides a MEF to implement the plug-in functionality.
Composite principle :
1: Declare a Compositioncontainer object that contains a bunch of catalog.
2: This heap of catalog if it is Assemblycatalog, then find in assembly, if it is Directorycatalog,
Find in your directory, if you want to find it in assembly, and you need to find it in your directory,
Then use Aggregatecatalog.
3: Then in this heap catalog find the implementation class marked by the export tag corresponding to the import attribute, and invoke the constructor of the implementation class to
Composite (combination).
Once you know the principle, you can also implement your own Compositioncontainer class,
To use MEF, you need to add a System.ComponentModel.Composition.dll reference for SimpleCalculator.
Then import the namespaces:
Using System.ComponentModel.Composition;
Using System.ComponentModel.Composition.Hosting;
Next look at what the program's constructor does:
Declare a assemblycatalog, point to the assembly where the program is located. and add it to the
In Compositioncontainer, the Compositioncontainer composeparts extension method is invoked to compose the parts of (this).
Note: composeparts is an extension method and requires a using System.ComponentModel.Composition;
OK, how to compose, in which assembly to find the implementation of the class to do compose has been completed.
The question now is: which classes need Compose??
To answer this question, Microsoft offers import and export features:
Import: Which object needs to be compose. That is, it needs to be populated by the implementation class, so the import tag is an object, which is usually an interface, because if it is a specific class, does it need import?
Export: Which class can be used to compose, that is, the class is not an implementation class that can be used to populate, so the export tag is a class, not a specific object.
So here Calculator use the import attribute to mark:
[Import (typeof (ICalculator))]
Private ICalculator Calculator;
Next, the MEF's combination engine, in the Composeparts (this), looks for the implementation class that the export attribute modifies in the Catalog Representative's Assemblycatalog, and compose after the implementation class is found.
If the class that is decorated with the export attribute is not found, the result is as follows:
OK, next add an implementation class and use the export feature to decorate it:
[Export (typeof (ICalculator))]
public class Mysimplecalculator:icalculator
{
public string Calculate (string input)
{
Return "MySimpleCalculator processing" + input;
}
}
The results of the operation are as follows:
Of course, import and export also provide other constructors, so you can also modify the above import and export to:
[Import ("Calculator1", typeof (ICalculator))]
[Export ("Calculator1", typeof (ICalculator))]
Contractname is provided as Calculator1 because you may have multiple ICalculator objects that need to be populated.
The code to modify the program is as follows:
Class Program
{
Private Compositioncontainer _container;
[Import ("Calculator1", typeof (ICalculator))]
Private ICalculator Calculator1;
[Import ("Calculator2", typeof (ICalculator))]
Private ICalculator Calculator2;
Public program ()
{
var catalog = new Aggregatecatalog ();
Catalog. Catalogs.add (New Assemblycatalog (typeof). Assembly));
var catalog = new Assemblycatalog (typeof). Assembly);
_container = new Compositioncontainer (catalog);
Try
{
This._container.composeparts (this);
}
catch (Compositionexception compositionexception)
{
Console.WriteLine (Compositionexception.tostring ());
}
}
static void Main (string[] args)
{
Program P = new program ();
string S;
Console.WriteLine ("Enter Command:");
while (true)
{
s = Console.ReadLine ();
Console.WriteLine (P.calculator1.calculate (s));
Console.WriteLine (P.calculator2.calculate (s));
}
}
}
Modify the export-decorated class to:
[Export ("Calculator1", typeof (ICalculator))]
public class Mysimplecalculator1:icalculator
{
public string Calculate (string input)
{
Return "The first calculator processed" + input;
}
}
[Export ("Calculator2", typeof (ICalculator))]
public class Mysimplecalculator2:icalculator
{
public string Calculate (string input)
{
Return "The second calculator processed" + input;
}
}
The results of the operation are as follows:
Because import and export are one by one corresponding, in the real world, there are a large number of cases, Microsoft also expected this situation, so provides the importmany characteristics.
In the previous example, the MySimpleCalculator calculate method returns a sentence in which the function of the computation is really implemented, such as input 5+3, output 8, input 7*4, output 28.
In order to support +-*/four kinds of operation. So declare a list of operations in MySimpleCalculator.
[Export (typeof (ICalculator))]
Class Mysimplecalculator:icalculator
{
[ImportMany]
Ienumerable<lazy<ioperation, ioperationdata>> operations;
public string Calculate (string input)
{
Return "Calculate processing" + input;
}
}
Operations is declared in MySimpleCalculator because it is a calculator that supports multiple operations. Because operations requires multiple operation to compose (padding), the ImportMany feature is used to decorate, as with the import feature, the ImportMany feature is generally a cosmetic interface.
The definitions of ioperation and IOperationData are as follows:
public interface IOperation
{
int Operate (int left, int. right);
}
public interface IOperationData
{
Char Symbol {get;}
}
Lazy<ioperation, ioperationdata> operations:
Provides a deferred indirect reference to an object and its associated metadata for use by the Managed extensibility Framework.
It means that the reference between IOperation and IOperationData needs to be delayed, why should it be delayed? because the ioperation needs to delay creation based on the symbol symbols of ioperationdata.
In other words, if the symbol for IOperationData equals "+", then the IOperation object is addoperation. If ioperationdata symbol equals "-", Then the IOperation object is subtractoperation.
So how do you guarantee this?
The key point lies in the exportmetadata attribute .
Look at the definition of add Operation:
[Export (typeof (IOperation))]
[ExportMetaData ("Symbol", ' + ')]
Class Add:ioperation
{
public int Operate (int left, int right)
{
Return to left + right;
}
}
Here the symbol for the ExportMetaData attribute is +. So when the symbol for IOperationData is "+", the match is add Operation
The complete code for MySimpleCalculator is as follows:
[Export (typeof (ICalculator))]
Class Mysimplecalculator:icalculator
{
[ImportMany]
Ienumerable<lazy<ioperation, ioperationdata>> operations;
public string Calculate (string input)
{
int left;
int right;
char operation;
INT fn = findfirstnondigitposition (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 Findfirstnondigitposition (string s)
{
for (int i = 0; i < s.length; i++)
{
if (!) ( Char.isdigit (S[i])) return i;
}
return-1;
}
}
Look back at the program code for the previous example:
Class Program
{
Private Compositioncontainer _container;
[Import (typeof (ICalculator))]
Private ICalculator Calculator;
Public program ()
{
var catalog = new Aggregatecatalog ();
Catalog. Catalogs.add (New Assemblycatalog (typeof). Assembly));
var catalog = new Assemblycatalog (typeof). Assembly);
_container = new Compositioncontainer (catalog);
Try
{
This._container.composeparts (this);
}
catch (Compositionexception compositionexception)
{
Console.WriteLine (Compositionexception.tostring ());
}
}
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));
}
}
}
When This._container.composeparts (this); , the MEF combination engine starts to compose the interface that marks the import feature, so here's the calculator. Where do I find the implementation class? , Assemblycatalog shows that the implementation class is found in the current assembly of the program, so we find the mysimplecalculator when constructing MySimpleCalculator, The operations of ImportMany characteristic modification was found. Then continue to find the add in the Assemblycatalog.
The above process is the compose process.
So how does mysimplecalculator carry out the calculate?
such as 5+3
1: Find the first non-digit position, that is, to find the +.
2: Statement left,right. And left for 5,right for 3.
3: Constructs the IOperation object according to the symbol +, then calls the operate (Left,right) method of the IOperation object.
foreach (Lazy<ioperation, ioperationdata> I in Operations)
{
if (i.metadata.symbol.equals (operation))
Return I.value.operate (left, right). ToString ();
}
Run Result:
Because the operation of add is currently defined. So according to the symbol + can find add, but * we have no definition, so operation not found!.
So start defining multiple:
[Export (typeof (IOperation))]
[ExportMetaData ("Symbol", ' * ')]
Class Multiple:ioperation
{
public int Operate (int left, int right)
{
Return to Left * right;
}
}
Run again, the results are as follows:
Of course, you can also add to the current assembly--/,^,%, etc. operation.
To make things more interesting, I'm going to add a directory calculateextensions to the debug directory and then-,/. Operation into the inside and let the MEF find it automatically.
First create a new Class library project: Simplecalculatorextension
Because you need to implement ioperation, you need to add a reference to the SimpleCalculator project.
Because the export feature is required, you need to add a reference to the System.ComponentModel.Composition.
The results of the entire project are as follows:
The subtract code is as follows:
Using System;
Using System.Collections.Generic;
Using System.Linq;
Using System.Text;
Using System.ComponentModel.Composition;
Namespace Simplecalculatorextension
{
[Export (typeof (Simplecalculator.ioperation))]
[ExportMetaData ("Symbol", '-')]
Class Subtract:SimpleCalculator.IOperation
{
public int Operate (int left, int right)
{
return left-right;
}
}
}
After the build succeeds, copy the SimpleCalculatorExtension.dll to the Calculateextensions directory:
Now SimpleCalculator's debug directory should be like this.
And there are SimpleCalculatorExtension.dll under the Calculateextensions folder.
The only thing to change next is the program's catalog object.
To enable catalog to find both in the assembly of the program and in the Calculateextensions directory. Modify the code as follows:
Public program ()
{
var catalog = new Aggregatecatalog ();
Catalog. Catalogs.add (New Assemblycatalog (typeof). Assembly));
Catalog. Catalogs.add (New Directorycatalog ("Calculateextensions"));
_container = new Compositioncontainer (catalog);
Try
{
This._container.composeparts (this);
}
catch (Compositionexception compositionexception)
{
Console.WriteLine (Compositionexception.tostring ());
}
}
The results of the operation are as follows:
The subtract method for modifying simplecalculatorextension is:
Namespace Simplecalculatorextension
{
[Export (typeof (Simplecalculator.ioperation))]
[ExportMetaData ("Symbol", '-')]
Class Subtract:SimpleCalculator.IOperation
{
public int Operate (int left, int right)
{
Console.WriteLine ("Simplecalculatorextension Method");
return left-right;
}
}
}
Regenerate the SimpleCalculatorExtension.dll and then copy it to the Calculateextensions folder:
Run the program again, the output into the following:
The article is a bit long and messy, and it's best to practice the MEF yourself, but it's all about MEF, and hopefully it will help you, and if you're not using MEF, and you're using interface-oriented programming principles, it's easy to realize your own "MEF."
】:
Talk with examples and experience the scalability that MEF brings, let ' s rock!!!
1: New Console program SimpleCalculator
The program to be implemented here is SimpleCalculator, as the name suggests: a simple calculator.
So we need to define an interface to compute:
public interface ICalculator
{
String Calculate (string input);
}
The program's code is as follows:
Class Program
{
Private Compositioncontainer _container;
[Import (typeof (ICalculator))]
Private ICalculator Calculator;
Public program ()
{
var catalog = new Aggregatecatalog ();
Catalog. Catalogs.add (New Assemblycatalog (typeof). Assembly));
var catalog = new Assemblycatalog (typeof). Assembly);
_container = new Compositioncontainer (catalog);
Try
{
This._container.composeparts (this);
}
catch (Compositionexception compositionexception)
{
Console.WriteLine (Compositionexception.tostring ());
}
}
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));
}
}
}
What the MEF is trying to do is look for plug-ins, and the traditional way to implement plug-ins is to use interfaces, that is, to declare an interface, and then use a configuration file to configure which implementation class the interface uses.
Microsoft is aware of this requirement, so it provides a MEF to implement the plug-in functionality.
Composite principle :
1: Declare a Compositioncontainer object that contains a bunch of catalog.
2: This heap of catalog if it is Assemblycatalog, then find in assembly, if it is Directorycatalog,
Find in your directory, if you want to find it in assembly, and you need to find it in your directory,
Then use Aggregatecatalog.
3: Then in this heap catalog find the implementation class marked by the export tag corresponding to the import attribute, and invoke the constructor of the implementation class to
Composite (combination).
Once you know the principle, you can also implement your own Compositioncontainer class,
To use MEF, you need to add a System.ComponentModel.Composition.dll reference for SimpleCalculator.
Then import the namespaces:
Using System.ComponentModel.Composition;
Using System.ComponentModel.Composition.Hosting;
Next look at what the program's constructor does:
Declare a assemblycatalog, point to the assembly where the program is located. and add it to the
In Compositioncontainer, the Compositioncontainer composeparts extension method is invoked to compose the parts of (this).
Note: composeparts is an extension method and requires a using System.ComponentModel.Composition;
OK, how to compose, in which assembly to find the implementation of the class to do compose has been completed.
The question now is: which classes need Compose??
To answer this question, Microsoft offers import and export features:
Import: Which object needs to be compose. That is, it needs to be populated by the implementation class, so the import tag is an object, which is usually an interface, because if it is a specific class, does it need import?
Export: Which class can be used to compose, that is, the class is not an implementation class that can be used to populate, so the export tag is a class, not a specific object.
So here Calculator use the import attribute to mark:
[Import (typeof (ICalculator))]
Private ICalculator Calculator;
Next, the MEF's combination engine, in the Composeparts (this), looks for the implementation class that the export attribute modifies in the Catalog Representative's Assemblycatalog, and compose after the implementation class is found.
If the class that is decorated with the export attribute is not found, the result is as follows:
OK, next add an implementation class and use the export feature to decorate it:
[Export (typeof (ICalculator))]
public class Mysimplecalculator:icalculator
{
public string Calculate (string input)
{
Return "MySimpleCalculator processing" + input;
}
}
The results of the operation are as follows:
Of course, import and export also provide other constructors, so you can also modify the above import and export to:
[Import ("Calculator1", typeof (ICalculator))]
[Export ("Calculator1", typeof (ICalculator))]
Contractname is provided as Calculator1 because you may have multiple ICalculator objects that need to be populated.
The code to modify the program is as follows:
Class Program
{
Private Compositioncontainer _container;
[Import ("Calculator1", typeof (ICalculator))]
Private ICalculator Calculator1;
[Import ("Calculator2", typeof (ICalculator))]
Private ICalculator Calculator2;
Public program ()
{
var catalog = new Aggregatecatalog ();
Catalog. Catalogs.add (New Assemblycatalog (typeof). Assembly));
var catalog = new Assemblycatalog (typeof). Assembly);
_container = new Compositioncontainer (catalog);
Try
{
This._container.composeparts (this);
}
catch (Compositionexception compositionexception)
{
Console.WriteLine (Compositionexception.tostring ());
}
}
static void Main (string[] args)
{
Program P = new program ();
string S;
Console.WriteLine ("Enter Command:");
while (true)
{
s = Console.ReadLine ();
Console.WriteLine (P.calculator1.calculate (s));
Console.WriteLine (P.calculator2.calculate (s));
}
}
}
Modify the export-decorated class to:
[Export ("Calculator1", typeof (ICalculator))]
public class Mysimplecalculator1:icalculator
{
public string Calculate (string input)
{
Return "The first calculator processed" + input;
}
}
[Export ("Calculator2", typeof (ICalculator))]
public class Mysimplecalculator2:icalculator
{
public string Calculate (string input)
{
Return "The second calculator processed" + input;
}
}
The results of the operation are as follows:
Because import and export are one by one corresponding, in the real world, there are a large number of cases, Microsoft also expected this situation, so provides the importmany characteristics.
In the previous example, the MySimpleCalculator calculate method returns a sentence in which the function of the computation is really implemented, such as input 5+3, output 8, input 7*4, output 28.
In order to support +-*/four kinds of operation. So declare a list of operations in MySimpleCalculator.
[Export (typeof (ICalculator))]
Class Mysimplecalculator:icalculator
{
[ImportMany]
Ienumerable<lazy<ioperation, ioperationdata>> operations;
public string Calculate (string input)
{
Return "Calculate processing" + input;
}
}
Operations is declared in MySimpleCalculator because it is a calculator that supports multiple operations. Because operations requires multiple operation to compose (padding), the ImportMany feature is used to decorate, as with the import feature, the ImportMany feature is generally a cosmetic interface.
The definitions of ioperation and IOperationData are as follows:
public interface IOperation
{
int Operate (int left, int. right);
}
public interface IOperationData
{
Char Symbol {get;}
}
Lazy<ioperation, ioperationdata> operations:
Provides a deferred indirect reference to an object and its associated metadata for use by the Managed extensibility Framework.
It means that the reference between IOperation and IOperationData needs to be delayed, why should it be delayed? because the ioperation needs to delay creation based on the symbol symbols of ioperationdata.
In other words, if the symbol for IOperationData equals "+", then the IOperation object is addoperation. If ioperationdata symbol equals "-", Then the IOperation object is subtractoperation.
So how do you guarantee this?
The key point lies in the exportmetadata attribute .
Look at the definition of add Operation:
[Export (typeof (IOperation))]
[ExportMetaData ("Symbol", ' + ')]
Class Add:ioperation
{
public int Operate (int left, int right)
{
Return to left + right;
}
}
Here the symbol for the ExportMetaData attribute is +. So when the symbol for IOperationData is "+", the match is add Operation
The complete code for MySimpleCalculator is as follows:
[Export (typeof (ICalculator))]
Class Mysimplecalculator:icalculator
{
[ImportMany]
Ienumerable<lazy<ioperation, ioperationdata>> operations;
public string Calculate (string input)
{
int left;
int right;
char operation;
INT fn = findfirstnondigitposition (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 Findfirstnondigitposition (string s)
{
for (int i = 0; i < s.length; i++)
{
if (!) ( Char.isdigit (S[i])) return i;
}
return-1;
}
}
Look back at the program code for the previous example:
Class Program
{
Private Compositioncontainer _container;
[Import (typeof (ICalculator))]
Private ICalculator Calculator;
Public program ()
{
var catalog = new Aggregatecatalog ();
Catalog. Catalogs.add (New Assemblycatalog (typeof). Assembly));
var catalog = new Assemblycatalog (typeof). Assembly);
_container = new Compositioncontainer (catalog);
Try
{
This._container.composeparts (this);
}
catch (Compositionexception compositionexception)
{
Console.WriteLine (Compositionexception.tostring ());
}
}
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));
}
}
}
When This._container.composeparts (this); , the MEF combination engine starts to compose the interface that marks the import feature, so here's the calculator. Where do I find the implementation class? , Assemblycatalog shows that the implementation class is found in the current assembly of the program, so we find the mysimplecalculator when constructing MySimpleCalculator, The operations of ImportMany characteristic modification was found. Then continue to find the add in the Assemblycatalog.
The above process is the compose process.
So how does mysimplecalculator carry out the calculate?
such as 5+3
1: Find the first non-digit position, that is, to find the +.
2: Statement left,right. And left for 5,right for 3.
3: Constructs the IOperation object according to the symbol +, then calls the operate (Left,right) method of the IOperation object.
foreach (Lazy<ioperation, ioperationdata> I in Operations)
{
if (i.metadata.symbol.equals (operation))
Return I.value.operate (left, right). ToString ();
}
Run Result:
Because the operation of add is currently defined. So according to the symbol + can find add, but * we have no definition, so operation not found!.
So start defining multiple:
[Export (typeof (IOperation))]
[ExportMetaData ("Symbol", ' * ')]
Class Multiple:ioperation
{
public int Operate (int left, int right)
{
Return to Left * right;
}
}
Run again, the results are as follows:
Of course, you can also add to the current assembly--/,^,%, etc. operation.
To make things more interesting, I'm going to add a directory calculateextensions to the debug directory and then-,/. Operation into the inside and let the MEF find it automatically.
First create a new Class library project: Simplecalculatorextension
Because you need to implement ioperation, you need to add a reference to the SimpleCalculator project.
Because the export feature is required, you need to add a reference to the System.ComponentModel.Composition.
The results of the entire project are as follows:
The subtract code is as follows:
Using System;
Using System.Collections.Generic;
Using System.Linq;
Using System.Text;
Using System.ComponentModel.Composition;
Namespace Simplecalculatorextension
{
[Export (typeof (Simplecalculator.ioperation))]
[ExportMetaData ("Symbol", '-')]
Class Subtract:SimpleCalculator.IOperation
{
public int Operate (int left, int right)
{
return left-right;
}
}
}
After the build succeeds, copy the SimpleCalculatorExtension.dll to the Calculateextensions directory:
Now SimpleCalculator's debug directory should be like this.
And there are SimpleCalculatorExtension.dll under the Calculateextensions folder.
The only thing to change next is the program's catalog object.
To enable catalog to find both in the assembly of the program and in the Calculateextensions directory. Modify the code as follows:
Public program ()
{
var catalog = new Aggregatecatalog ();
Catalog. Catalogs.add (New Assemblycatalog (typeof). Assembly));
Catalog. Catalogs.add (New Directorycatalog ("Calculateextensions"));
_container = new Compositioncontainer (catalog);
Try
{
This._container.composeparts (this);
}
catch (Compositionexception compositionexception)
{
Console.WriteLine (Compositionexception.tostring ());
}
}
The results of the operation are as follows:
The subtract method for modifying simplecalculatorextension is:
Namespace Simplecalculatorextension
{
[Export (typeof (Simplecalculator.ioperation))]
[ExportMetaData ("Symbol", '-')]
Class Subtract:SimpleCalculator.IOperation
{
public int Operate (int left, int right)
{
Console.WriteLine ("Simplecalculatorextension Method");
return left-right;
}
}
}
Regenerate the SimpleCalculatorExtension.dll and then copy it to the Calculateextensions folder:
Run the program again, the output into the following:
The article is a bit long and messy, and it's best to practice the MEF yourself, but it's all about MEF, and hopefully it will help you, and if you're not using MEF, and you're using interface-oriented programming principles, it's easy to realize your own "MEF."