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

建立我们的项目如下

代码已经完整的托管到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;
    }
}
  • 演示

初始的状态:

当我们点击Show View 1按钮时,容器去创建View1的实例,View1所需要的实例,又会根据导入导出的原则去创建。创建完成后,

点击View 1 Click后,会将ViewModel层的数据传给Service,Service又调用回掉函数,将数据放置到UI上。

也可以点击Show View 2进行相应的操作。

  • 总结

本文讲述了一个简单的MEF在UWP下的引用,体现了MEF通过依赖注入的方式将程序更好的解耦。阅读本文希望对你有所帮助。

谢谢~

代码下载:http://files.cnblogs.com/files/youngytj/uwp_MEF.zip

  • 参考资料:

Unity

《MEF程序设计指南》博文汇总

Prism

Prism与MVVM、Unity、MEF关系

依赖注入那些事儿

时间: 2024-10-14 11:56:44

Windows10(UWP)下的MEF的相关文章

Windows10(uwp)开发中的侧滑

还是在持续的开发一款Windows10的应用中,除了上篇博客讲讲我在Windows10(uwp)开发中遇到的一些坑,其实还有很多不完善的地方,比如(UIElement.Foreground).(GradientBrush.GradientStops)[1].(GradientStop.Offset)这种设置无法生效,还有RelativePanel内的元素不能自动的适应大小,要去手动控制宽高度,以及窗口在靠边的时候一些尺寸上的错误等等.虽然是WPF技术之后的延续,但是很多地方还是要小心仔细的处理,

Windows10 UWP开发 - 响应式设计

Windows10 UWP开发 - 响应式设计 本篇随笔与大家简单讨论一下在开发适配不同分辨率.宽高比的Windows10 Universal App布局时的可行方式与小技巧.经验均从实践中总结,可能有诸多不完善和浅薄之处,欢迎读者严格指正.另外本文也只是抛砖引玉之用,希望能收获更多更好的实战经验. 自适配的必要性 说了这么多,我们首先可能会问了,为什么要做响应式设计?其原因有以下两点: Windows10的跨平台性 Windows10是微软宣称可以统一运行于PC&平板&手机&Xb

vue.js在windows10系统下的环境搭建

vue.js在windows10系统下的环境搭建流程 1.安装node.js(node包含了npm包管理器) node.js安装包以及源码下载地址:https://nodejs.org/en/download/ 注意:使用安装包(.msi)(作为小白的我,直接用了.exe,导致环境变量都不会自动配置,真的挺郁闷的) 下载下来以后可以直接双击安装,按照提示一步步安装. 详细的安装步骤可看网址:http://www.runoob.com/nodejs/nodejs-install-setup.htm

windows10 uwp 开发职位推荐

各位亲,近期有想跳槽的吗? 北京爱奇艺视频需要 windows10 uwp 开发一名, 工作地点在北京,有 wp/win8 开发经验的兄弟姐妹,可以发我邮箱([email protected]) ,帮内推,感激~~~~

Windows10专业版下IE浏览器无法上网怎么办?

Windows10专业版系统内置新一代Edge浏览器,并保留原来的IE浏览器,在前面教程中也介绍过"Windows10系统IE浏览器功能关闭/移除方法",的确还很多俄罗斯轮盘比较习惯使用IE浏览器,但是在使用过中难免会遇到一些问题,近日就有用户在Win10下使用IE浏览器时无法连网或无法加载页面等等问题,我们可以通过重置IE浏览器并使用命令来修复,具体方法如下: 1.重置IE浏览器设置,打开控制面板,依次找到网络选项-高级-选择重置;2.快捷键"Win+X"打开,或

windows10环境下QtCreator中出现skipping incompatible xxx when searching for xxx 问题解决办法

windows10环境下QtCreator中出现skipping incompatible xxx when searching for xxx 我再QtCreator中想导入一个外部库时,他提示不匹配 出现这种问题是因为QtCreator 和 MinGW 其中一个是32位 ,而另一个是64位, 将两者统一后便不会出现该问题!!! 原文地址:https://www.cnblogs.com/zz-1120-wtenlb/p/12602379.html

讲讲我在Windows10(uwp)开发中遇到的一些坑.

7月29日发布的Windows10正式版,当天安装好以后,在网络不太好的情况下,经过多次尝试终于装上了Visual Studio 2015和Windows 10 10240的SDK.这两周一直在开发UWP,讲讲在其中遇到的一些坑,不定时更新,有兴趣的可以关注下. 1.DataType在UWP中缺失的问题 在WPF中使用过MVVMLight的都知道,我们可以在App.xaml文件中通过DataType将ViewModel和View绑定在一起. 1 <DataTemplate DataType=&quo

windows10系统下安装tensorflow2.0

最近开始做深度学习实验,在老师的推荐下准备采用tensorflow框架构建神经网络结构,在此记录下今天安装tensorflow2.0的过程和踩过的坑,方便以后复习,以及给后来者一个参考. 1.安装过程 1)需要安装的东西: i.anaconda(或者miniconda,可以理解为精简版的anaconda,只保留了一些必备的组件,所以安装上会快很多,同时也满足我们管理python环境的需求). ii.CUDA和CuDNN(CPU版不用,GPU版必须) iii.tensorflow CUDA和CuD

FreePascal - Typhon在Windows10 X64下的使用问题!

Typhon是CodeTyphon中的开发FreePascal的IDE工具. 在Windows10 X64中安装完CodeTyphon后,我们会发现有两套Typhon,分别对应32位和64位,32位可以正常运行,但是64位无法启动,会有错误!! 这可能是CodeTyphon的bug,我进入了Typhon的目录:"C:\codetyphon\typhon\bin64",发现里面并没有"typhon.exe",但是有一个"styphon.exe",单