程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> 實戰MEF(1)一種不錯的擴展方式

實戰MEF(1)一種不錯的擴展方式

編輯:關於C#

在過去,我們完成一套應用程序後,如果後面對其功能進行了擴展或修整,往往需要重新編譯代碼生 成新的應用程序,然後再覆蓋原來的程序。這樣的擴展方式對於較小的或者不經常擴展和更新的應用程 序來說是可以接受的,而對於像ERP系統那樣復雜而且常常需要擴展的應用程序,這種擴展方法就不夠方 便,因為每次都要修改源代碼或重新引用組件。

尤其是組件(許多dll),如果每編寫一個新組 件又要在主項目中引用一次,顯然主項目就不得不經常重新生成。要是能有一種機制,可以在主項目應 用程序不作任何修改就可以自動識別並擴展組件,就會很便捷,我們每次擴展只需要更新或者添加某些 dll文件即可。

MEF正是為了解決上述問題而誕生。MEF全稱Managed Extensibility Framework, 至於如何翻譯不重要,你喜歡怎麼個譯法都無所謂,我們只要明白它用來干啥就好了。

寬泛的理 論似乎作用不明顯,我們還是先來弄一個簡單的例子。現在假設我在開發一個應用程序,首先我要為一 些組件以及將來可以要擴展的組件定義公共接口(或者說是協定,大家是否記得在WCF中也是這樣,先定 義一些公共的服務協定,然後視具體情況對這些協定進行擴展),然後我可以按照不同的情形去實現這 些接口,這也是我們常說的,接口可以起到規范作用,有了規范,正是為後期擴展打下可行性基礎。

例子的主項目是一個控制台應用程序,我們先在解決方案中添加一個類庫項目,為了簡單演示, 我定義了以下接口:

public interface IExtBase
     
 {
     
void DoTask();
     
string TaskName { get; }
     
 }

這個IExtBase接口就作為我們要擴展的組件的公共協定,不管我以後怎麼擴展,哪怕我要添 加100000個組件,這些組件都要實現IExtBase接口。

這裡我做了兩個擴展作為例子,為了表明 MEF框架能自動發現組件,我把兩個實現IExtBase接口的類寫到另外一個類庫項目中——TaskToa.dll。

[Export("task1", typeof(CommExtBase.IExtBase))]
     
public class Task_1 : CommExtBase.IExtBase
     
 {
     
public void DoTask()
     
 {
     
Console.WriteLine("任務1執行。");
     
 }
     
      
     
public string TaskName
     
 {
     
get
     
 {
     
return "任務1";
     
 }
     
 }
     
 }
     
      
     
 [Export("task2", typeof(CommExtBase.IExtBase))]
     
public class Task_2 : CommExtBase.IExtBase
     
 {
     
public void DoTask()
     
 {
     
Console.WriteLine("任務2執行。");
     
 }
     
      
     
public string TaskName
     
 {
     
get { return "任務2"; }
     
 }
     
 }

附加ExportAttribute特性用於擴展的組件類,表示它們將被導出,導出的類型會被MEF自動 發現。

在主項目中我們不引用這個TaskToa類庫,先把TaskToa項目生成一個TaskToa.dll,直接 復制到.exe應用程序的動行目錄下,在調試模中為\\項目\\bin\\Debug目錄下。

由於實現公共接口的類不止一個,後續可能還有10000000個,為了能夠使所有的擴展組件都能被發 現,統一的協定類型為IExtBase接口(與WCF的實現服務協定相似),在附加ExportAttribute特性時指 定了每個組件類的協定名,而協定類型都是IExtBase接口,協定類型必須統一才能保證所有擴展的類能 被MEF框架發現。

最後在.exe主項目的代碼中加入以下代碼:

using System; 
    
using System.Collections.Generic; 
    
using System.Linq; 
    
using System.Text; 
    
using System.Threading.Tasks; 
    
using System.ComponentModel.Composition; 
    
using System.ComponentModel.Composition.Hosting; 
    
     
    
namespace MainApp 
    
{ 
    
     
    
class TestWork
    
 { 
    
 [Import("task1")] 
    
public CommExtBase.IExtBase Task1; 
    
 [Import("task2")] 
    
public CommExtBase.IExtBase Task2; 
    
 } 
    
     
    
class Program
    
 { 
    
static void Main(string[] args) 
    
 { 
    
ApplicationCatalog appCat = new ApplicationCatalog(); 
    
CompositionContainer container = new CompositionContainer(appCat); 
    
     
    
TestWork tw = new TestWork(); 
    
     
    
try
    
 { 
    
 container.ComposeParts(tw); 
    
Console.WriteLine("Task1的類型:{0}\tTaskName: {1}\t調用DoTask方法:",tw.Task1.GetType().Name,tw.Task1.TaskName); 
    
 tw.Task1.DoTask(); 
    
Console.Write("\n\n"); 
    
     
    
Console.WriteLine("Task2的類型:{0}\tTaskName:{1}\t調用DoTask方法:", 
    
tw.Task2.GetType().Name, tw.Task2.TaskName); 
    
 tw.Task2.DoTask(); 
    
 } 
    
catch (CompositionException cex) 
    
 { 
    
Console.WriteLine(cex.Message); 
    
 } 
    
     
    
Console.Read(); 
    
 } 
    
 } 
    
}

TestWork類用來包裝最後被合並的組件,它有兩個公共字段,類型雖然都是IExtBase,但由於應用 了ImportAttribute特性,並且指定了協定名,這些協定名一定要與我們之前在擴展類中應用 ExportAttribute是指定的協定名相對應。附加了ImportAttribute特性可以讓MEF識別對應的組件並導入 到TestWork類中。

在Main入口點中,我們先使用ApplicationCatalog類來收集所有可用的擴展組 件,然後把收集到的信息傳給CompositionContainer容器,容器負責把收集到的組件進行合並(組裝) 。合並完成後我們就可以使用這些組件了。

本例子的運行結果如下面的截圖所示:

從截圖中我們看到,TestWork類的Task1和Task2字段的類型分別為Task_1和Task_2,同時也調 用了它們的成員,輸出結果表明,我們之前開發的兩個擴展類Task_1和Task_2已經自動導入到我們當前 的應用程序中了。

ApplicationCatalog類是在當前應用程序的運行目錄下查找所有符合要求的 exe或dll中的擴展組件,一旦找到就自動收集並生成組件目錄,而後提供給CompositionContainer進行 組裝。

從這個例子我們看到MEF框架就像一個大型的組裝廠車間,首先設計師們尋找靈感,構思 產品的基本模型,這也就是我們所定義的公共接口規范;隨後,進行精確計算,進一步把抽象的模型變 為具體的工程圖,這相當於我們自己實現編寫的各個擴展類;接著,相關工作人員會把設計師和工程師 做好的各個零部件的工程圖收集整理,准備提供給車間進行生產組裝,這就相當於我們例子中的 ComposablePartCatalog,我們例子中用到的ApplicationCatalog只是其中一個收集方式,其他的方式還 有按程序集進行收集或按特定路徑目錄下的所有類庫進行收集。然後車間開始制作並組裝成產品,最終 投入使用。

我們可以用下面的圖來描述一下整個過程(此圖純屬虛構,如有雷同,實屬巧合)

現在我們先不必過多關注代碼細節,因為後面我會慢慢介紹,我們只要明白MEF的用途就可以了。

OK,本文就說到這裡吧,88

查看本欄目

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved