Windows10(UWP)下的MEF


  • 前言

最近在幫一家知名外企開發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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM