How-to:应用mef 提高程序扩展能力(part1)

Simple Introduction to Extensible Applications with the Managed Extensions Framework

 

Recently my team has been working on the Managed Extensions Framework (MEF)... I have gotten a chance to explain the concept to folks and I think I have discovered a way to talk about MEF that folks can easily get.  So I thought I'd spend a little time walking through a *very* simple MEF example as a way to introduce folks to the power of extensible applications in general, and MEF in particular. 

BTW, you can download the current MEF CTP  and the final working sample.

Background

Let's start with the most simple example: Hello World! 

   1: using System;
   2:  
   3: class Program
   4: {
   5:     public void Run()
   6:     {
   7:         Console.WriteLine("Hello World!");
   8:         Console.ReadKey();
   9:     }
  10:     static void Main(string[] args)
  11:     {
  12:         Program p = new Program();
  13:         p.Run();
  14:     }
  15: }

Now, you might now always want to print the same string, so let's refactor slightly to pull the string out.. 

   1: public string Message { get; set; }
   2:  
   3: public void Run()
   4: {
   5:     Console.WriteLine(Message);
   6:     Console.ReadKey();
   7: }

This looks nice, now we need to add the message... well, the actual text is a separate concern, as such it should be in a different class.    Such as:

   1: public class SimpleHello 
   2: {
   3:     public string Message
   4:     {
   5:         get
   6:         {
   7:             return "hello world!!";
   8:         }
   9:     }
  10: }

Now we simply need wire these up:

   1: public void Run()
   2: {
   3:     SimpleHello hello = new SimpleHello();
   4:     Message = hello.Message;
   5:  
   6:     Console.WriteLine(Message);
   7:     Console.ReadKey();
   8: }

This works, but something looks odd about line 3 and 4... We have introduced tight coupling back.. What we really want to do is externalize lines 3 and 4, so they can be controlled without effecting the rest of the logic of the program. 

 

Enter MEF

一、基本用例

STEP 1:

Add a Reference to the System.ComponentModel.Composition.dll assembly found in the bin directory of the MEF zip. 

Add

   1: using System.ComponentModel.Composition;

Now in the Program class, we need to import a value for Message -- that is, we want to specify that someone outside of this program needs to supply a message.  Then we need to remove our tight coupling.    Note in line 4-5 and we saying we want to import the value for Message.  Here I am showing doing it by type (string).. because basic types such as strings might be pretty common, consider using a named import such as [Import("Message")]

   1: class Program
   2: {
   3:  
   4:     [Import]
   5:     public string Message { get; set; }
   6:  
   7:     public void Run()
   8:     {
   9:        // SimpleHello hello = new SimpleHello();
  10:         //Message = hello.Message;
  11:  
  12:         Console.WriteLine(Message);
  13:         Console.ReadKey();
  14:     }

 

STEP 2:

Now in the SimpleHello class we need to export the Message property.  This tells the system it can be used to satisfy requirements.    Notice line 3 and 4 and am marking it with an Export attribute..  Again, this exports it by type (string in this case).  As with above, you might want to do it with an explicit name for a more real world example [Export("Message")]

   1: public class SimpleHello 
   2: {
   3:     [Export]
   4:     public string Message
   5:     {
   6:         get
   7:         {
   8:             return "hello world!!";
   9:         }
  10:     }
  11: }

 

STEP 3:

Now we need to tell MEF to wire these up for us.

   1: public void Run()
   2: {
   3:     //SimpleHello hello = new SimpleHello();
   4:     //Message = hello.Message;
   5:     var catalog = new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly());
   6:     var container = new CompositionContainer(catalog.CreateResolver());
   7:     container.AddPart(this);
   8:     container.Compose();
   9:  
  10:  
  11:     Console.WriteLine(Message);
  12:     Console.ReadKey();
  13: }

In line 5, we create a catalog -- that tells MEF where to look for imports and exports.  In this case, we are saying the currently running assembly.  There are tons of different parts catalogs, we will look at some later and you can of course build your own.

in line 6, we create a Composition container-- this is effectively the soup that all the different parts will be wired up together.

In line 7, we add this instance of Program to the container so that its dependencies get wired up.

In line 8, we do the compose magic.  this is where the Message property of Program gets set. 

Notice, in this case the wire up is done by matching types (String to String)... clearly that isn't always the right way, we will look at other ways to wire up later.

Run it and you get the expected output of "hello world!".

 

二、小小的扩展

 

STEP 1:

Now let's add another message, just to be a little more fun... 

   1: public class MoreMessages
   2: {
   3:     [Export]
   4:     public string FunMessage
   5:     {
   6:         get
   7:         {
   8:             return "This is getting fun!";
   9:         }
  10:     }
  11: }

Now run.. It blows up!  Why?  Well, let's look at the exception:

System.ComponentModel.Composition.CompositionException  Error : Multiple exports were found that match the constraint '(composableItem.ContractName = \"System.String\")'. The import for this contract requires a single export only."

 

STEP 2:

From the error it looks like we provided too many ways to satisfy the Import... MEF didn't know which one to pick.  Of course you can programmatically get in there and help, you can also just remove the export from one of the messages... but more fun, you can actually tell MEF you are able to deal with zero or more results.  Change the Message property of Program as follows:

   1: [Import]
   2: public IEnumerable<string> Messages { get; set; }

Notice we changed the return type to be a collection of strings rather than just one string.

Now change the usage code slightly and we get:

   1: class Program
   2: {
   3:  
   4:     [Import]
   5:     public IEnumerable<string> Messages { get; set; }
   6:  
   7:     public void Run()
   8:     {
   9:         //SimpleHello hello = new SimpleHello();
  10:         //Message = hello.Message;
  11:         var catalog = new AttributedAssemblyPartCatalog(Assembly.GetExecutingAssembly());
  12:         var container = new CompositionContainer(catalog.CreateResolver());
  13:         container.AddPart(this);
  14:         container.Compose();
  15:  
  16:         foreach (var s in Messages)
  17:         {
  18:             Console.WriteLine(s);
  19:         }
  20:  
  21:     
  22:         Console.ReadKey();
  23:     }

 

输出为:

hello world!!

This is getting fun!

 

Wow -- we get both messages!  Pretty cool...

 

 

 

 

你可能感兴趣的:(REST,UP)