Windows10(UWP)下的MEF

来源:http://www.cnblogs.com/youngytj/archive/2016/01/09/5114681.html
-Advertisement-
Play Games

前言最近在幫一家知名外企開發Universal Windows Platform的相關應用,開發過程中不由感慨:項目分為兩種,一種叫做前人栽樹後人乘涼,一種叫做前人挖坑後人遭殃。不多說了,多說又要變成月經貼了。講講MEF。MEF全稱Managed Extensibility Framework。我們...


  • 前言

最近在幫一家知名外企開發Universal Windows Platform的相關應用,開發過程中不由感慨:項目分為兩種,一種叫做前人栽樹後人乘涼,一種叫做前人挖坑後人遭殃。不多說了,多說又要變成月經貼了。

講講MEF。

MEF全稱Managed Extensibility Framework。我們做.Net的碰到依賴註入(DI:Dependency Injection)這一塊的內容,一般會選擇使用Unity或者MEF,這也是Prism主要使用的兩種方式。在.Net 4.0之前,MEF一直作為擴展的形式存在,但是.Net 4.0的時候,已經作為Framework的一部分了。但是.Net 4.0的MEF只是原始的版本,後面MEF 2又加入了泛型類導入導出等等特性。MEF 2不作為.Net的一部分,又變成了以擴展包的形式存在,支持了包括.Net 4.5以及之後的平臺,我們可以通過Nuget獲取這個擴展,源碼也被托管在了codeplex平臺。

Microsoft Composition (MEF 2)

Source Code

MEF2支持的平臺

- .NET Framework 4.5

- Windows 8

- Windows Phone 8.1

- Windows Phone Silverlight 8

- Portable Class Libraries

通常意義上,當我們講到MEF的時候,一般都會去描述這是一個用來實行插件式開發的一套東西。當插件式開發成為了一種可能,那就意味著我們的項目可以被完整的解耦,這就保證了我們程式的健壯性,同時在開發的過程中我們也避免了各種開發人員的衝突。

  • 開始

怎樣開始寫一個基於MEF的程式?

假設我們現在寫的是一個UWP的項目,並且我們採用C#+XAML的方式。因為MVVM是XAML的主打的方式,可以很好的應用綁定數據的這個模型,所以我們採用MVVM。

所以我們決定設計一個基於C#+XAML的通過MVVM模式來進行View和ViewModel的解耦,中間我們也可以實現一個觀察者模式的消息傳遞方式來進行View之間的相互傳參等等。看是確實很完美,可以很輕易的下載一個Mvvmlight來直接用。

我們看樣子已經決定了View層和ViewModel層的問題,那麼對於一個完整的系統,我們還缺少一些什麼組件呢?我們可以還缺少數據,所以我們需要數據層,一般我們命名為Service層,來進行跟資料庫或者伺服器的通信;還缺什麼呢?日誌系統,我們需要進行運行過程中的一些數據統計,或者異常捕獲後的消息記錄,所以我們需要Log層;還需要緩存層,緩存我們的數據到記憶體或者磁碟,這樣看上去我們的程式能夠運行的稍微好一點。

當我們決定了以後,我們現在需要寫的東西如下:

- View

- ViewModel

- Service

- Log

- Cache

想想我們怎麼處理這個問題呢?

public sealed class Hub
{
        
    public static Log Log { get; private set; }
    public static Service Service { get; private set; }
    public static Cache Cache { get; private set; }

    private static Hub instance = null;
    private static readonly object padlock = new object();

    Hub()
    {
        Log = new Log();
        Service = new Service();
        Cache = new Cache();
    }

    public static Hub Instance
    {
        get
        {
            if (instance == null)
            {
                lock (padlock)
                {
                    if (instance == null)
                    {
                        instance = new Hub();
                    }
                }
            }
            return instance;
        }
    }
}

上面的解決方案,引入一個單例的Hub類,然後各層作為只讀靜態屬性來提供各類功能,看上去不錯,我們也能很好的調用。

但是有個問題,當這個類出現的時候,我們不希望Service等等類再被外部實例化。很不幸,當Service等作為一個可以Public的屬性時,這個類本身為了訪問的一致性就也要不可以避免的被標記為Public,這破壞了我們設立這個類的初衷。

那我們怎麼繼續解決這個問題?把Service等類都設計為單例。這也是一個解決方案。但無論是這種解決方案還是代碼里的解決方案,都強引用的意味都太強了,稍加不慎,系統就會崩潰。

我們不希望引入Hub,也不想Service等類被設計為單例,同時具體的ViewModel中也不希望出現具體的Service的實例,那我們應該怎麼辦?

答案:依賴註入。

  • 重新設計

保留我們之前所設想的所有的組件,引入介面來進入註入:

- IService

- ILog

- ICache

看一下我們的ViewModel現在應該是怎麼樣的?

public class ViewModel
{
    ILog Log;
    IService Service;
    ICache Cache;

    public ViewModel(ILog log, IService service, ICache cache)
    {
            Log = log;
            Service = service;
            Cache = cache;
    }
}

又進了一步,我們只需要調用的時候給我們需要的實例就行了。如果我們需要View,我們還能聲明一個IView的介面。

至此,我們設計還沒有引入MEF,看上去已經相對比較好的解耦了,我們只有在調用ViewModel的時候,引入具體的是實例,耦合發生在了此處。

  • 引入MEF

試想一下,既然我們需要生成的實例的對象都已經在我們的DLL之中,為什麼我們還要手動的去生成一個實例,然後再傳到具體的構造函數裡面,它就不能自己尋找嗎?

假設我們的類都有一個別名,然後我們在需要引用的地方告訴告訴程式,我們需要一個實現Ixxx介面的類,它的名字叫做xxx,這樣我們是不是更進了一部。如下:

public class ViewModel
{
    [Import("LogSample")]
    ILog Log;
    [Import("ServiceSample")]
    IService Service;
    [Import("CacheSample")]
    ICache Cache;

    public ViewModel()
    {
    }
}

當我們構造函數完成後,Log等對象就已經自動在程式集中找到名為LogSample的的實現ILog的類,Service也是,Cache也是。

看下Log

[Export("LogSample", typeof(ILog))]
public class Log : ILog
{
}

到現在為止,我們主要關註具體的功能實現就好了。

  • MEF正式引入

為了簡化我們的程式,更加關註MEF的本質,我們把程式設計為僅包括下列的組件

- View

- ViewModel

- Service

建立我們的項目如下

image

代碼已經完整的托管到GitHub上,可以方便的查閱。

我們在Service中寫了一個演示的功能:

[Export(Constant.Confing.SampleService,typeof(IService))]
public class SampleService : IService
{
    public void QueryData(int numuber, Action<int> action)
    {
        action(numuber);
    }
}

我們看一下我們的程式的主界面:

public sealed partial class MainPage : Page
{
    [Import(Constant.Confing.View1)]
    public IView View1 { get; set; }

    [Import(Constant.Confing.View2)]
    public IView View2 { get; set; }

    public MainPage()
    {
        this.InitializeComponent();
        this.Loaded += MainPage_Loaded;
    }

    private CompositionHost host;

    private void MainPage_Loaded(object sender, RoutedEventArgs e)
    {
        List<Assembly> assemblies = new List<Assembly>()
        {
            Assembly.Load(new AssemblyName("MEF.Service")),
            Assembly.Load(new AssemblyName("MEF.View")),
            Assembly.Load(new AssemblyName("MEF.ViewModel")),
            Assembly.Load(new AssemblyName("MEF.Abstract"))
            };
        ContainerConfiguration configuration = new ContainerConfiguration().WithAssemblies(assemblies);
        host = configuration.CreateContainer();
        host.SatisfyImports(this);
    }

    private void View1_Click(object sender, RoutedEventArgs e)
    {
        IView view = host.GetExport(typeof(IView), Constant.Confing.View1) as IView;
        frame.Content = view;
    }

    private void View2_Click(object sender, RoutedEventArgs e)
    {
        IView view = host.GetExport(typeof(IView), Constant.Confing.View2) as IView;
        frame.Content = view;
    }
}

將所有的程式集加入容器之中,然後通過容器去創建對象。

View的代碼:

[Export(Constant.Confing.View1,typeof(IView))]
public sealed partial class View1 : UserControl, IView
{
    IService Service;
    IViewModel ViewModel;
    [ImportingConstructor]
    public View1(
        [Import(Constant.Confing.SampleService)]IService service, 
        [Import(Constant.Confing.ViewModel1)]IViewModel viewModel)
    {
        this.InitializeComponent();
        this.Service = service;
        this.ViewModel = viewModel;
    }

    private void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
    {
        Service.QueryData(ViewModel.Number, ShowValue);
    }

    private void ShowValue(int i)
    {
        Btn.Content = i;
    }
}
  • 演示

初始的狀態:

image

當我們點擊Show View 1按鈕時,容器去創建View1的實例,View1所需要的實例,又會根據導入導出的原則去創建。創建完成後,

image

點擊View 1 Click後,會將ViewModel層的數據傳給Service,Service又調用回掉函數,將數據放置到UI上。

image

也可以點擊Show View 2進行相應的操作。

image

image

  • 總結

本文講述了一個簡單的MEF在UWP下的引用,體現了MEF通過依賴註入的方式將程式更好的解耦。閱讀本文希望對你有所幫助。

謝謝~

代碼下載:http://files.cnblogs.com/files/youngytj/uwp_MEF.zip

  • 參考資料:

Unity

《MEF程式設計指南》博文彙總

Prism

Prism與MVVM、Unity、MEF關係

依賴註入那些事兒

System.Composition


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • live() 與bind()作用基本一樣。 最重要區別:live()可以將事件綁定到當前和將來的元素(eg:為id=zy元素綁定點擊事件,而當你用js動態生成一個節點並插入到dom文檔結構中時,如果你是用bind()綁定的,怎麼新插入的節點將不會有該bind綁定事件。而live()則可以); ...
  • 一,c#中的值類型和引用類型 眾所周知在c#中有兩種基本類型,它們分別是值類型和引用類型;而每種類型都可以細分為如下類型: 什麼是值類型和引用類型什麼是值類型:進一步研究文檔,你會發現所有的結構都是抽象類型System.ValueType的直接派生類,而System.ValueType本身又是直接....
  • 一、前言 AgileEAS.NET SOA 中間件平臺是一款基於基於敏捷並行開發思想和Microsoft .Net構件(組件)開發技術而構建的一個快速開發應用平臺。用於幫助中小型軟體企業建立一條適合市場快速變化的開發團隊,以達到節省開發成本、縮短開發時間,快速適應市場變化的目的。 AgileEAS....
  • 1. ExcelUtility功能:  1.將數據導出到EXCEL(支持XLS,XLSX,支持多種類型模板,支持列寬自適應)  類名:ExcelUtility. Export  2.將EXCEL數據導入到數據對象中(DataTable、Dataset,支持XLS,XLSX) ...
  • 【實例說明】 本例關鍵點是練習使用^的使用,沒有什麼技術含量,大牛勿噴。呵呵。。 在介紹實例之前先來剪短瞭解一下加密的概念。 加密是指通過某種特殊的方法,更改已有信息的內容,使得未授權的用戶即使得到了加密的信息,如果沒有正確解密的方法,那麼也無法得到信息的內容。 談到加密的話題,一些讀者朋友一定非常...
  • Cron Expressions——Cron 表達式按順序依次為秒(0~59)分鐘(0~59)小時(0~23)天(月)(0~31,但是你需要考慮你月的天數)月(0~11)天(星期)(1~7 1=SUN 或 SUN,MON,TUE,WED,THU,FRI,SAT)7.年份(1970-2099)其中每個...
  • 來源:http://zxlovenet.cnblogs.com聲明:本文原創發表於博客園,作者為初行本文歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則視為侵權。從安裝到配置首先到官網http://www.nunit.org/下載如下圖的資料,安裝NUnit-2.6....
  • 主要介紹一下C#的delegate的用法以及如何實現類似javascript的each方法,並簡要說明delegate,Func, Action和 Predicate的區別
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...