程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> MEF簡介

MEF簡介

編輯:關於.NET

Managed Extensibility Framework (MEF) 使開發人員能夠在其 .NET 應用程序中提供掛鉤,以供用戶和第三方擴展。可以將 MEF 看成一個通用應用程序擴展實用工具。

MEF 使開發人員能夠動態創建擴展,無需擴展應用程序,也不需要任何特定於擴展內容的知識。這可以確保在編譯時兩者之間不存在耦合,使應用程序能夠在運行時擴展,不需要重新編譯。MEF 還可以在將擴展加載到應用程序之前,查看擴展程序集的元數據,這是一種速度更快的方法。

本實驗中包含幾個與可擴展性相關的關鍵概念:

• Composition(復合)是將幾個帶有不同功能的對象組合為一個或多個復雜對象的過程。復合不是從父類中繼承功能,而是將幾個不同的對象組合為一個對象。例如,Wing、Propeller、Fuselage 和 VerticalStablizer 對象可以組成 Aircraft 對象的一部分。

• ComposableParts 是 MEF 的關鍵構建塊。ComposableParts 支持應用程序通過 Exports 和 Imports 公開和使用組件擴展。

• Contracts 是 Export 和 Import 組件之間的通信途徑。Contract 通常通過 Interface 類實現。Contracts 支持 MEF ComposableParts 以避免依賴關系或者與其他組件之間的緊密耦合。

• Conditional Binding 允許加載滿足特定元數據標准的組件。以上述示例為例,您可以選擇加載 VerticalStabilizer  組件,這些組件僅由復合石墨 (composite graphite) 組成。

實現擴展的主要方式是,在應用程序的擴展點添加 Import 屬性並向擴展添加相應的 Export 屬性。Import 和 Export 可以看做是供應商和消費者的關系:Export 組件提供了一些價值;Import 組件消費這些價值。其他擴展選項對於開發人員是開放的,包括完全自定義的擴展方法;但是,本實驗僅關注上文提到的主要方法。

目標

在本次動手實驗中,您將學習如何:

• 定義組件的可擴展性選項

• 執行條件綁定和組件創建

• 在應用程序運行時導入擴展的程序集

系統要求

您必須擁有以下工具才能完成本實驗:

• Microsoft Visual Studio 2010

• .NET Framework 4

安裝

使用 Configuration Wizard 驗證本實驗的所有先決條件。要確保正確配置所有內容,請按照以下步驟進行。

注意: 要執行安裝步驟,您需要使用管理員權限在命令行窗口中運行腳本。

1.如果之前沒有執行,運行 Training Kit 的 Configuration Wizard。要做到這一點,運行位於 %TrainingKitInstallationFolder%\Labs\IntroToMEF\Setup 文件夾下的 CheckDependencies.cmd 腳本。安裝先決條件中沒有安裝的軟件(如有必要請重新掃描),並完成向導。

注意:為了方便,本實驗中管理的許多代碼都可用於 Visual Studio 代碼片段。CheckDependencies.cmd 文件啟動 Visual Studio 安裝程序文件安裝該代碼片段。

練習

本次動手實驗由以下練習組成:

1.使用 MEF 向應用程序動態添加模塊

2.動態擴展窗體

初始材料

這次動手實驗包括以下初始材料。

• Visual Studio 解決方案。您將發現可以用作練習起點的 Visual Studio 解決方案,具體解決方案取決於練習。

如果我束手無策了怎麼辦?

該動手實驗中的源代碼包括一個最終文件夾,如果完成了每個練習的每一步,您可以在該文件夾中找到應該獲取的 Visual Studio 解決方案。如果需要其他幫助來完成練習,您可以使用該解決方案作為指南。

完成本實驗的估計時間:30 分鐘。

下一步

練習 1:使用 MEF 向應用程序動態添加模塊

練習 1:使用 MEF 向應用程序動態添加模塊

Managed Extensibility Framework 的一個實際應用是在運行時向應用程序添加模塊。這在以下場景非常有用:用戶選擇購買或初始安裝特定模塊,之後可能需要添加更多模塊。使用 MEF,您可以配置應用程序來監控眾所周知的目錄,並在該目錄中添加找到的任何模塊程序集。將程序集放入目錄可以使應用程序加載這些程序集,無需明確設置對它們的引用。

任務 1 –更新主窗體以加載支持的模塊

在本任務中,您將向現有窗體添加代碼,以創建擴展掛鉤並動態導入稍後在本練習中創建的類。僅導入那些與配置元數據匹配的類。

MainForm.cs 中定義的主窗體需要一種方法來從文件系統中讀取模塊。為了本例的簡便起見,您將在窗體初始加載過程中導入模塊。

1.從 Start | All Programs | Microsoft Visual Studio 2010 | Microsoft Visual Studio 2010 打開 Microsoft Visual Studio 2010。

2.打開 MefLab.sln 解決方案文件。默認情況下,該文件位於以下文件夾:%TrainingKitInstallFolder%\Labs\IntroToMEF\Source\Ex01-DynamicallyAddModules\begin\C#。

3.在代碼視圖中打開 MainForm(右鍵單擊解決方案資源管理器中的 MainForm,然後選擇 View Code)。

4.更新窗體以使用 MEF 庫。為此,在 MainForm 類定義中的頂部添加以下語句。

C#

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;

注意:MEF 框架通過契約導入和導出。這些契約由接口指定,用於定義哪些可擴展點 (Imports) 與哪些導出組件相關。您的 MainForm 將導入實現 IMainFormContract 的模塊,後者在 MefCommon 項目中定義。要導入這些模塊,您需要定義一個集合來通過屬性保存和公開它們。MEF 的復合階段將解析適用的 Exports 和 Imports 並加載您的集合。

5.將以下代碼添加到 MainForm.cs 文件中構造函數的上方。

(代碼片段– MEF 簡介實驗 - 練習 1 ImportedMainFormContracts)

C#

[ImportMany]
public Lazy<IMainFormContract, IDictionary<string, object>>[] ImportedMainFormContracts { get; set; }

6.MEF 的 CompositionContainer 用於匹配組件與導出和導入值。CompositionContainer 支持加載、綁定和檢索組件及其值。通過在 MainForm 類的上方添加以下變量聲明創建全局 CompositionContainer。

C#

private CompositionContainer _container;

7.您現在必須創建一個幫助函數從文件夾導入對象,您已經使用 MainForm.cs 文件中的 _extensionDir 變量指定了該文件。在 MainForm.cs 文件 MainForm 類的下方添加以下代碼。

(代碼片段 – MEF 簡介實驗 - 練習 1 GetContainerFromDirectory)

C#

private CompositionContainer GetContainerFromDirectory()
{
      var catalog = new AggregateCatalog();
      var thisAssembly =
          new AssemblyCatalog(
              System.Reflection.Assembly.GetExecutingAssembly());
      catalog.Catalogs.Add(thisAssembly);

      catalog.Catalogs.Add(
          new DirectoryCatalog(_extensionDir));

      var container = new CompositionContainer(catalog);
      return container;
}

8.接下來,您需要使用已加載的容器。您必須遵從常用 MEF 模式,這需要使用 Compose 方法讓 MEF 解析所有適用的導入和導出操作。在 GetContainerFromDirectory 方法下方添加以下方法。

(代碼片段 – MEF 簡介實驗 - 練習 1 Compose)

C#

private bool Compose()
{
    _container = GetContainerFromDirectory();

    try
    {
        _container.ComposeParts(this);
    }
    catch (CompositionException compException)
    {
        MessageBox.Show(compException.ToString());
        return false;
    }

    return true;
}

9.您將調用窗體構造函數中的 Compose。添加代碼,構造函數應該如下所示:

(代碼片段 – MEF 簡介實驗 - 練習 1 Compose 調用)

C#

public MainForm()
{
InitializeComponent();
    bool successfulCompose = Compose();
    if (!successfulCompose) 
    {
        this.Close();
    }
}

任務 2 –創建一個擴展模塊

在本任務中,您將使用窗體處理現有的 Windows Forms 項目以維護員工數據。您將使用 MEF 標記窗體,表示其可用於導出到其他應用程序。MefEmployeeModule 項目預建了幾個類,以節省時間。

1.在繼續下一步前,您將需要引用 MEF 庫。將對 MEF 庫的引用添加到 MefEmployeeModule 項目。

a.選擇 Solution Explorer 中的 MefEmployeeModule 項目並選擇 Project | Add Reference 。將出現 Add References 對話框。

b.選擇 .NET 選項卡。

圖 1

MEF 引用

c.選擇 System.ComponentModel.Composition 組件。單擊 OK 按鈕添加對該庫的引用。

注意: 如果您沒有找到 System.ComponentModel.Composition 組件,則轉到 Browse 選項卡,在 %SystemRoot%\Microsoft.net\Framework\v4.0.20704 文件夾或相應的 .NET Framework 4.0 文件夾中查詢該程序集。

2.您還需要使用合適的 using 語句更新類文件。在代碼視圖中打開 EmployeeMaintenance 窗體,並在文件頂部 using 塊中添加以下行。

C#

using System.ComponentModel.Composition;

3.EmployeeMaintenance 窗體就是需要添加到主窗體的模塊。記住,您已經在 MainForm 中使用了 ImportMany 屬性,以便僅拉入實現 IMainFormContract 契約的組件。您需要根據該契約指定 EmployeeMaintenance 窗體。類聲明之後的代碼如下:

C#

[Export(typeof(IMainFormContract))]
public partial class EmployeeMaintenance :Form, IMainFormContract

4.IMainFormContract 指定必須實現的兩個屬性。將以下代碼添加到 EmployeeMaintenance 構造函數上方。

(代碼片段 – MEF 簡介實驗 - 練習 1 Implement IMainFormContract)

C#

public string MenuItemText
{
    get { return "&Employees"; }
}

public string SubFormTitle
{
    get { return "Employee Pane"; }
}

5.您還需要使用將用於導入 MainForm 類的一些元數據來修飾該類。MEF 允許您在各個位置查詢該元數據。添加 ExportMetaData 屬性如下:

(代碼片段 – MEF 簡介實驗 - 練習 1 ExportMetaData attributes)

C#

[Export(typeof(IMainFormContract))]
[ExportMetadata("Name", "Employee Pane")]
[ExportMetadata("MenuText", "&Employees")]
public partial class EmployeeMaintenance :Form, IMainFormContract

6.保存窗體並編譯 MefEmployeeModule 項目。選擇 Build | Build MefEmployeeModule。MefEmployeeModule 程序集現在已經編譯,並可使用 MEF 導入到其他應用程序中。

任務 3 –導入 Employee Maintenance 窗體

在本任務中,您將更新主應用程序窗體,以在運行時用戶單擊菜單項時導入可用的模塊。

1.在 Solution Explorer 的 MefLabMain 項目中雙擊 MainForm.cs,在設計視圖下打開 MainForm。

2.現在需要浏覽 CompositionContainer 中的組件並確定導出菜單文本信息的組件。您需要調整窗體,以根據導出的元數據加載菜單項。Compose 方法加載支持 IMainFormContract 的模塊列表,因此您需要浏覽該集合並收集將添加到菜單中的項。為窗體的 Load 事件創建一個事件處理程序,以便可以根據需要調整菜單。在窗體中央雙擊。將出現代碼視圖窗口,並帶有定義的新 MainForm_Load 方法。添加以下代碼以對集合進行迭代,根據需要更新菜單項。注意,您還要與事件處理程序掛鉤以處理每個新的菜單項。

(代碼片段– MEF 簡介實驗 - 練習 1 填充菜單)

C#

private void MainForm_Load(object sender, EventArgs e)
{
    foreach (var export in this.ImportedMainFormContracts)
    {
        var exportedMenuText = export.Metadata["MenuText"] as string;
        if (String.IsNullOrEmpty(exportedMenuText))
        {
            return;
        }
        ToolStripItem menuItem =
            modulesToolStripMenuItem.DropDownItems.Add(exportedMenuText);
        menuItem.Click += new System.EventHandler(this.LaunchModule_Click);
    }
}

注意:如下所示,將邏輯混合到應用程序表示層不是一種好的軟件工程實踐。很難測試、混合問題,以及打破許多其他可靠的設計原則。

您將打破該原則,在 MainForm_Load 等窗體方法中顯示邏輯混合,以保證示例的簡明扼要。在實際的應用程序中,該邏輯應該分離到另一個類,最好使用 Model View Presenter 或類似方法。我們建議您浏覽 MVP 及其相關項目作為設計原則,幫助您編寫更多靈活、可維護、可測試的應用程序。

3.下一步是添加從菜單操作啟動組件的功能。您將使用與之前相同的方法:查看導入組件列表中加載的組件,通過查看元數據找到適用的項。Value 屬性允許您啟動特定的組件。在 MainForm 類定義的底部添加以下方法。該方法與 MainForm_Load 方法結合可處理動態添加菜單的單擊事件。

(代碼片段– MEF 簡介實驗 - 練習 1 菜單單擊處理程序)

C#

private void LaunchModule_Click(object sender, EventArgs e)
{
    ToolStripItem thisItem = sender as ToolStripItem;
    if (thisItem == null) { return; }

    string thisItemTitle = thisItem.Text;

    foreach (var export in this.ImportedMainFormContracts)
    {
        string menuTitle = export.Metadata["MenuText"] as string;
        if (String.IsNullOrEmpty(menuTitle))
        {
            return;
        }
        if (menuTitle == thisItemTitle)
        {
            Form frm = export.Value as Form;
            if (frm == null) { return; }
            frm.Show(this);
            return;
        }
    }
}

下一步

練習 1:驗證

練習 1:驗證

在本驗證中,您將分別使用和不使用導入模塊來運行應用程序。

1.添加以下 Post-Build 命令以生成附加輸出文件夾。為此,右鍵單擊 MefEmployeeModule 項目並選擇 Properties。單擊 Build Events 選項卡並將以下內容剪切到 Post-build 事件命令行字段中。確定包含了引號,以正確處理帶空格的目錄。

Post-Build Command

if not exist "$(SolutionDir)MefLabMain\bin\Debug\ExtModules" mkdir "$(SolutionDir)MefLabMain\bin\Debug\ExtModules"

2.編譯解決方案 (CTRL+SHIFT+B)。

3.設置 MefLabMain 作為啟動項目。在 Solution Explorer 中,右鍵單擊 MefLabMain 並選擇 Set as startup project。

4.按 F5 運行應用程序。應該會出現父窗口,其頂部應有一個菜單。Modules 菜單下方應該沒有任何項目。

圖 2

空的 Modules 菜單

5.退出應用程序。選擇 File | Exit。

6.您已經構建了該應用程序來監控 MefLabMain\bin\Debug\ExtModules 目錄的更改。您可以更新 MefEmployeeModule 項目,以自動通過項目的 Post Build 事件將其輸出二進制文件保存到該文件夾。將以下粗體顯示的行添加到 Post-Build 命令行中。

Post-Build Command

if not exist "$(SolutionDir)MefLabMain\bin\Debug\ExtModules" mkdir "$(SolutionDir)MefLabMain\bin\Debug\ExtModules"

copy "$(TargetPath)" "$(SolutionDir)MefLabMain\bin\Debug\ExtModules"

7.按 F5 運行應用程序。應該會出現父窗口,其頂部應有一個菜單。Employees 菜單項應該在 Modules 菜單下方出現。

圖 3

加載了模塊的 Modules 菜單

8.單擊 Employees 菜單項。應該出現一個 Employee Maintenance 窗體。

圖 4

Employee Maintenance 窗體

注意: 加載模塊時拋出 CompositionExceptions 通常表示您忘了將所需的某個文件復制到 ExtModules 文件夾中。

9.注意,將從父項目調用 Employee 模塊,無需明確設置任何引用。MEF 處理了對所有程序集的加載操作,允許您創建一個完全解耦的組件集合。

10.單擊 EmployeeMaintenance 窗體右上方的關閉按鈕 ( ) 關閉它。

11.選擇 File | Exit 關閉父窗體。

下一步

練習 2:動態擴展窗體

練習 2:動態擴展窗體

在最後一個練習中,您將程序集放入眾所周知的目錄中,向應用程序動態添加一個新的 Employee Maintenance 窗體。現在,您將使用 MEF 擴展這個新添加的窗體,動態加載可以為窗體提供更多功能的窗體控件。

無論有沒有擴展,應用程序都必須能夠正常工作,因此需要通過 Employee Maintenance 窗體來松散耦合擴展的功能。兩者都不需要了解另一方的內在工作機制。您將通過 Interface 使用 Contract 支持此功能。

本練習將使用幾個已經預建的類,使您能關注本實驗的關鍵方面。

我們的計劃是擴展 EmployeeMaintenance 窗體,動態添加操作員工列表的命令按鈕。您將使用現有接口 ICmdButtonInfo,該接口包含命令按鈕和一個 CompanyInfo 對象,該對象可以從導出的對象繼承而來。

任務 1 –將 Add Employee Button 標記為可導出

MEFEmployeeExtender 項目包含可以在運行時動態加載到 Employee Maintenance 窗體的控件。對於本實驗,您將修改兩個控件– AddEmployeeButton 和 DeleteEmployeeButton,它們都實現 ICmdButtonInfo 契約。

在本任務中,您將修改 AddEmployeeButton,通過 ICmdButtonInfo 契約導出其信息。

1.從 Start | All Programs | Microsoft Visual Studio 2010 | Microsoft Visual Studio 2010 打開 Microsoft Visual Studio 2010。

2.打開 MefLab.sln 解決方案文件。默認情況下,該文件位於以下文件夾:%TrainingKitInstallFolder%\Labs\IntroToMEF \Source\Ex02-DynamicallyExtendAForm\begin\C#。您也可以繼續使用上一個練習完成時獲得的解決方案。

3.您還需要使用 EmployeeMaintenance 窗體中的 AddEmployee 按鈕,因此需要將其標記為可通過契約導出。修改 AddEmployeeButton.cs 類,使用 Export 屬性將該類標記為可導出。

C#

[Export(typeof(ICmdButtonInfo))]
public class AddEmployeeButton :ICmdButtonInfo

4.保存文件。

任務 2 –將 DeleteEmployee Button 標記為可導出

1.與 AddEmployeeButton 一樣,您需要通知 MEF 通過 ICmdButtonInfo 契約導出 DeleteEmployeeButton。修改 DeleteEmployeeButton.cs 類,使用 Export 屬性將該類標記為可導出。

C#

[Export(typeof(ICmdButtonInfo))]
public class DeleteEmployeeButton:ICmdButtonInfo

2.保存文件。

任務 3 –向 Employee Maintenance 窗體添加一個 Panel 控件

在本任務中,向 Employee Maintenance 窗體添加一個 Panel 控件。之後,您可以動態向窗體添加按鈕。將它們放在該面板上將更容易將這些按鈕對齊。

1.雙擊 Solution Explorer 中 MEFEmployeeModule 項目下方的 EmployeeMaintenance.cs,在設計器中打開 EmployeeMaintenance 窗體。

2.如果 Toolbox 面板不可見,單擊 View 菜單的 Toolbox。

3.從工具箱 Containers 選項卡中將 Panel 控件拖到窗體上。

a.在 DataGridView 下找到 Panel。

b.拖動 Panel 的邊緣,將其寬度拉伸到 DataGridView 的寬度。

c.將 Panel 的名稱更改為“ButtonsPanel”。

任務 4 –向 Employee Maintenance 窗體添加代碼以加載擴展的對象

在本任務中,您將向 Employee Maintenance 窗體的 Load 事件處理程序添加代碼。該代碼將查看包含導出控件的眾所周知的程序集目錄。這些控件將在運行時自動添加到窗體中。

1.在 Solution Explorer 中右鍵單擊並選擇 View Code,以打開 EmployeeMaintenance.cs。

2.添加一個集合來保存擴展的對象。您將引用該集合作為導入對象,因為您將把這些對象導入到這個特定類中。注意,您只接受滿足 ICmdButtonInfo 契約的導入。在 EmployeeMaintenance 類的頂部添加以下代碼。

(代碼片段– MEF 簡介實驗 - 練習 2 ImportedButtons)

C#

[ImportMany]
public Lazy<ICmdButtonInfo>[] ImportedButtons
{
    get;
    set;
}

3.現在,您需要迭代該集合,並加載導入的對象,本例中為按鈕。將代碼添加到 MainForm 之後,您需要使用 Value 屬性訪問導出對象本身。在 EmployeeMaintenance 類的底部添加以下方法。

(代碼片段– MEF 簡介實驗 - 練習 2 CreateButtons)

C#

private void CreateButtons()
{
    int left = 10;

    foreach (var importedButton in this.ImportedButtons)
    {
        var btnInfo = importedButton.Value;

        btnInfo.CompanyInfo = this.CompanyInfo;
        btnInfo.CompanyInfo.EmployeeListChanged +=
            new EventHandler(CompanyInfo_EmployeeListChanged);
        Button btn = btnInfo.CommandButton;

        this.ButtonsPanel.Controls.Add(btn);
        btn.Left = left;
        left += btn.Width;
        left += 20;
    }
}

4.更新窗體的加載方法以調用 CreateButtons 方法。

C#

private void EmployeeMaintenance_Load(object sender, EventArgs e)
{
GetAllEmployees(); 
    CreateButtons();
}

5.當用戶選擇 DataGridView 中的行時,您需要更新 CompanyInfo 對象的 SelectedEmployee 字段。這樣一來,松散耦合的 CompanyInfo 對象中的按鈕可以了解要對哪個員工進行操作。在 EmployeeMaintenance 類中添加以下方法。

(代碼片段– MEF 簡介實驗 - 練習 2 empDataGridView_SelectionChanged)

C#

private void empDataGridView_SelectionChanged(object sender, EventArgs e)
{
    if (empDataGridView.SelectedRows.Count > 0)
    {
        Employee selectedEmployee
            = (Employee)empDataGridView.SelectedRows[0].DataBoundItem;
        this.CompanyInfo.SelectedEmployee = selectedEmployee;
    }
    else
    {
        this.CompanyInfo.SelectedEmployee = null;
    }
}

6.將該方法連接到 DataGridView 的 SelectionChanged 事件。在設計模式中打開 EmployeeMaintenance 窗體。右鍵單擊 DataGridView 控件並從上下文菜單中選擇 Properties。在 Properties 表單上,單擊 Event 圖標列出事件。在列表中找到 SelectionChanged 事件,並使用下拉列表選擇 empDataGridView_SelectionChanged 方法。

圖 5

連接 SelectionChanged 事件

7.編譯解決方案。選擇 Build | Build Solution。

下一步

練習 2:驗證

練習 2:驗證

在本驗證中,您將運行帶有新 MEF 擴展的解決方案。

1.如果它尚未打開,可以從 Start | All Programs | Microsoft Visual Studio 2010 | Microsoft Visual Studio 2010 中打開 Microsoft Visual Studio 並打開 MEFLab 解決方案。

2.測試帶有新擴展的應用程序。MefEmployeeExtender 程序集已經通過預配置的 Post-Build 事件復制到 ExtModules 文件夾中。

a.按 F5 運行應用程序。選擇 Modules | Employees。

b.應該出現一個 Employee Maintenance 窗體。這一次,它應該包含兩個新按鈕:Delete Employee 和 Add Employee。

圖 6

EmployeeMaintenance 窗體

c.單擊 Add Employee 按鈕。DataGridView 中將顯示一個新行。

d.單擊任何行左邊的灰色框,突出顯示 DataGridView 該行。單擊 Delete Employee 按鈕。該行應該會消失。

圖 7

刪除行

e.在 Employee Maintenance Load 方法中設置斷點,每個按鈕的單擊方法都可以在該代碼中步進,以查看發生了什麼。

f.關閉 Employee Maintenance 窗體和主窗體

總結

在本實驗中,您使用 Microsoft Managed Extensiblity Framework 為應用程序添加了幾個可擴展性點,並構建了擴展來插入這些可擴展性點。

使用 MEF,您可以設置導出屬性,無需明確引用即可加載外部程序集,還可以在運行時動態擴展應用程序。您了解了導入和導出的交互,並通過接口使用契約指定了要處理的導出組件。

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