天天看點

.NET自帶IOC容器MEF之初體驗

本文主要把MEF作為一種IOC容器進行講解,.net中可用的IOC容器非常多,如 CastleWindsor,Unity,Autofac,ObjectBuilder,StructureMap,Spring.Net等,這些第三方工具各不相同,但功能大體都相同,大都需要事先對接口與實作進行配對(通過代碼或配置檔案),然後由系統自動或手動來通過接口來獲得相應實作類的執行個體,對象執行個體化的工作由IOC容器自動完成。

概述   

官方說法: Managed Extensibility Framework(MEF)是.NET平台下的一個擴充性管理架構,它是一系列特性的集合,包括依賴注入(DI)等。MEF為開發人員提供了一個工具,讓我們可以輕松的對應用程式進行擴充并且對已有的代碼産生最小的影響,開發人員在開發過程中根據功能要求定義一些擴充點,之後擴充人員就可以使用這些擴充點與應用程式互動;同時MEF讓應用程式與擴充程式之間不産生直接的依賴,這樣也允許在多個具有同樣的擴充需求之間共享擴充程式。

 解決的問題    

MEF解決了什麼呢?以往,如果一個應用程式需要支援插件方式必須要實作自己的底層并且這些插件通常是針對特有應用的,不能被其他應用所使用。實際上MEF提供了發現群組合的能力使你的應用程式可以加載擴充,為運作時的可擴充性提供了一種簡單的解決方法:  MEF為宿主應用提供了一種标準的途徑來暴露自身并使用外部擴充。而擴充本身是可以被不同的應用程式所使用的。而一個擴充依舊可以通過針對特定應用的方法來實作。擴充之間也可以存在依賴關系,MEF則會自動将它們按照正确的順序進行調用。MEF還提供了一些用來定位和加載可用擴充的方法。MEF允許使用附加中繼資料對擴充進行标記,進而達到易于豐富的查詢和篩選的目的。

工作原理

.NET自帶IOC容器MEF之初體驗
簡短說一下MEF的工作原理,MEF的核心包括一個catalog和一個CompositionContainer。category用于發現擴充,而container用于協調建立和梳理依賴性。每個可組合的Part提供了一個或多個Export,并且通常依賴于一個或多個外部提供的服務或Import。每個Part管理一個執行個體為應用程式運作
.NET自帶IOC容器MEF之初體驗

MEF 提供一種通過“組合”隐式發現元件的方法。 MEF 元件(稱為“部件-Part”)。部件以聲明方式同時指定其依賴項(稱為“導入-Import”)及其提供的功能(稱為“導出-Export”)。MEF原理上很簡單,找出有共同接口的導入、導出。然後找到把導出的執行個體化,賦給導入。說到底MEF就是找到合适的類執行個體化,把它交給導入。

如何聲明一個部件-導入與導出

導出”是部件向容器中的其他部件提供的一個值,而“導入”是部件向要通過可用導出滿足的容器提出的要求。 在特性化程式設計模型中,導入和導出是由修飾類或成員使用 Import 和Export 特性聲明的。 Export 特性可修飾類、字段、屬性或方法,而 Import 特性可修飾字段、屬性或構造函數參數。為了使導入與導出比對,導入和導出必須具有相同的協定。

假設有一個類MyClass,它聲明了可以導入插件的類型是IMyAddin。

public class MyClass
{
    [Import]
    public IMyAddin MyAddin { get; set; }
}      

這裡有一個類,它聲明為導出。類型同樣為IMyAddin。

[Export(typeof(IMyAddin))]
public class MyLogger : IMyAddin { }      

這樣我們使用MyAddin屬性的時候就可以獲得到MyLogger的執行個體。

發現部件

  MEF提供三種方式發現部件

  • AssemblyCatalog 在目前程式集發現部件。
  • DirectoryCatalog 在指定的目錄發現部件。
  • DeploymentCatalog 在指定的XAP檔案中發現部件(用于silverlight)

當通過不同方式發現部件的時候,還可以使用AggregateCatalog來把這些部件聚合到一起。

var catalog = new AggregateCatalog();
            //把從Program所在程式集中發現的部件添加到目錄中
            catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly));
            //把從指定path發現的部件添加到目錄中
            catalog.Catalogs.Add(new DirectoryCatalog("C:\\Users\\v-rizhou\\SimpleCalculator\\Extensions"));      

如何組合部件?

在加載完部件之後,要把它們放到一個CompositionContainer容器中。

var container = new CompositionContainer(catalog)      

通過調用容器的ComposeParts()方法可以把容器中的部件組合到一起。

container.ComposeParts(this);      

下面我們使用一個簡單的列子學習使用MEF

1、         項目結構圖

.NET自帶IOC容器MEF之初體驗

注:三個項目都要添加System.ComponetModel.Composition.dll的引用。

2、         METTest項目

 (1)、IHelloWord.cs

為我們定義的接口,隻有兩個簡單方法,代碼如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace METTest
{
    public interface IHelloWord
    {
         string SayHello(string str);
         string SayWord(string str);
    }
}      

(2) HelloWord.cs

該檔案繼承IHelloWord接口并實作接口中的方法,HelloWord類被聲明成internal防止方法在類外被引用,用[Export(typeof(IHelloWord))]修飾聲明該類為導出,類型為IHelloWord,代碼如下

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;

namespace METTest
{
    [Export(typeof(IHelloWord))]
   internal class HelloWord:IHelloWord
    {
       public string SayHello(string str)
       {
           return "Hello" + str;
       }
       public string SayWord(string str)
       {
           return str;
       }
    }
}      

(3) HelloWordB.cs

該檔案先不用看,下面用到了在做說明

3、         METTest1項先不管,下面用到了在做說明

4、         MEFConsoleApplication

該項目為控制台項目,添加對METTest的引用,不要添加對METTest1項目的引用。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using METTest;
using System.Reflection;
using System.ComponentModel.Composition.Hosting;
using System.IO;

namespace MEFConsoleApplication
{
   [Export]
    class Program
    {
        [Import]
       public IHelloWord HelloWord { get; set; }

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

        public void Method()
        {
            AggregateCatalog catelog = new AggregateCatalog();
            catelog.Catalogs.Add(new DirectoryCatalog(Directory.GetCurrentDirectory()));//查找部件,目前應用程式
           
            //catelog.Catalogs.Add(new DirectoryCatalog(@"../../../MEFTest1/bin/Debug"));//這個我們通過路徑找到部件
            //catelog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            CompositionContainer  container = new CompositionContainer(catelog);//聲明容器
            container.ComposeParts(this);//把容器中的部件組合到一起
            //CompositionBatch Batch = new CompositionBatch();
            //Batch.AddPart(this);
            //container.Compose(Batch);
            //HelloWord = container.GetExportedValue<IHelloWord>();//這樣也可以執行個體化借口
            Console.WriteLine(HelloWord.SayHello("eric"));
            Console.WriteLine(HelloWord.SayWord("_eric"));
            
            Console.Read();
        }
    }
}      

運作結果:

.NET自帶IOC容器MEF之初體驗

5、下面我們來看一下一個接口被多個類執行個體化

當一個接口被多個類執行個體化時,用ImportMany 聲明,具體如下

[ImportMany]
public IEnumerable<IHelloWord> HelloWord { get; set; }      

 打開HelloWordB.cs檔案繼承IHelloWord,并用[Export(typeof(IHelloWord))]修飾。這樣HelloWord和HelloWordB都繼承了IHelloWord,并且用[Export]聲明。

.NET自帶IOC容器MEF之初體驗
.NET自帶IOC容器MEF之初體驗
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;

namespace METTest
{
    [Export(typeof(IHelloWord))]
   internal class HelloWordB:IHelloWord
    {
        public string SayHello(string str)
        {
            return "我是HelloB:" + str;
        }

        public string SayWord(string str)
        {
            return "B_"+str;
        }
    }
}      

View Code

修改MEFConsoleApplication項目的Program.cs

.NET自帶IOC容器MEF之初體驗
.NET自帶IOC容器MEF之初體驗
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel.Composition;
using METTest;
using System.Reflection;
using System.ComponentModel.Composition.Hosting;
using System.IO;

namespace MEFConsoleApplication
{
   [Export]
    class Program
    {
        [ImportMany]
       public IEnumerable<IHelloWord> HelloWord { get; set; }

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

        public void Method()
        {
            AggregateCatalog catelog = new AggregateCatalog();
            catelog.Catalogs.Add(new DirectoryCatalog(Directory.GetCurrentDirectory()));//查找部件,目前應用程式
           
            //catelog.Catalogs.Add(new DirectoryCatalog(@"../../../MEFTest1/bin/Debug"));//這個我們通過路徑找到部件
            //catelog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly()));
            CompositionContainer  container = new CompositionContainer(catelog);//聲明容器
            container.ComposeParts(this);//把容器中的部件組合到一起
            //CompositionBatch Batch = new CompositionBatch();
            //Batch.AddPart(this);
            //container.Compose(Batch);
            //HelloWord = container.GetExportedValue<IHelloWord>();//這樣也可以執行個體化借口
            //Console.WriteLine(HelloWord.SayHello("eric"));
            //Console.WriteLine(HelloWord.SayWord("_eric"));
            foreach (var item in HelloWord)
            {
                Console.WriteLine(item.SayHello("eric"));
            }
            Console.Read();
        }
    }
}      

本文參考:

http://wenku.baidu.com/view/abc72ec80508763231121273.html

http://www.cnblogs.com/techborther/archive/2012/02/06/2339877.html

點選下載下傳源代碼

每天學習一點點,每天進步一點點。

MEF