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平台。
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
- 参考资料:
Windows10(UWP)下的MEF的更多相关文章
- 讲讲Windows10(UWP)下的Binding
前言 貌似最近来问我XAML这块的东西的人挺多的.有时候看他们写XAML这块觉着也挺吃力的,所谓基础不牢,地动山摇.XAML这块虽说和HTML一样属于标记语言,但是世界观相对更加庞大一点. 今天讲讲X ...
- 揭秘Windows10 UWP中的httpclient接口[2]
阅读目录: 概述 如何选择 System.Net.Http Windows.Web.Http HTTP的常用功能 修改http头部 设置超时 使用身份验证凭据 使用客户端证书 cookie处理 概述 ...
- Windows10 UWP开发 - 响应式设计
Windows10 UWP开发 - 响应式设计 本篇随笔与大家简单讨论一下在开发适配不同分辨率.宽高比的Windows10 Universal App布局时的可行方式与小技巧.经验均从实践中总结, ...
- Windows10(uwp)开发中的侧滑
还是在持续的开发一款Windows10的应用中,除了上篇博客讲讲我在Windows10(uwp)开发中遇到的一些坑,其实还有很多不完善的地方,比如(UIElement.Foreground).(Gra ...
- Windows10系统下,彻底删除卸载MySQL
本文介绍,在Windows10系统下,如何彻底删除卸载MySQL... 1>停止MySQL服务开始->所有应用->Windows管理工具->服务,将MySQL服务停止.2> ...
- Windows10 VS2015下分别编译libevent 32位和64位库
Libevnt 在Windows10 VS2015下分别编译32位和64位库 直接上王道 libevent代码地址: https://github.com/libevent/libevent git ...
- tensorflow安装过程cpu版-(windows10环境下)---亲试可行方案
tensorflow安装过程cpu版-(windows10环境下)---亲试可行方案 一, 前言:本次安装tensorflow是基于Python的,安装Python的过程不做说明 二, 安装环境: ...
- Windows10环境下使用VisualSVN server搭建SVN服务器
参考: Windows10环境下使用VisualSVN server搭建SVN服务器 要搭建个svn用.之前自己的服务器用的乌龟.后来用了这个VisualSVN server. 具体教程见上链接.暂无 ...
- Windows10系统下安装python2和python3双版本
Windows10系统下安装Python3的步骤已经演示过(详见:https://www.cnblogs.com/schut/p/8399195.html),此处不再赘述Python的下载,主要介绍在 ...
随机推荐
- MySQL下建立表
磨砺技术珠矶,践行数据之道,追求卓越价值 回到上一级页面: PostgreSQL杂记页 回到顶级页面:PostgreSQL索引页 [作者 高健@博客园 luckyjackgao@gmail. ...
- bzoj1043 [HAOI2008]下落的圆盘
Description 有n个圆盘从天而降,后面落下的可以盖住前面的.求最后形成的封闭区域的周长.看下面这副图, 所有的红色线条的总长度即为所求. Input 第一行为1个整数n,N<=1000 ...
- dos下edit编辑器的快捷命令一览
Home Move cursor to the beginning of the line currently on. End Move cursor to the end of the line c ...
- 13-[Mysql]--pymysql模块
1.介绍 之前我们都是通过MySQL自带的命令行客户端工具mysql来操作数据库,那如何在python程序中操作数据库呢?这就用到了pymysql模块,该模块本质就是一个套接字客户端软件,使用前需要事 ...
- python之打包、发布模块
一.python中针对于写好的模块,并且比人也可以使用改模块,这样就可以以同意的打出来,让别人安装或者赋值过后可以更好的使用以及集成. 二.最近在学习python所以这里主要是记录一下python的打 ...
- C# 其他的Url 文件的路径转化为二进制流
//将虚拟路径转化为文件的路径然后最后转化为文件流 public ActionResult SaveImage(string path) { var url =System.Web.HttpConte ...
- Jsp中格式化时间戳的常用标签
接下来一段时间的记录,虽然不是一些什么全新的知识,但是作为后台开发人员都是应该必须掌握的前端基础技能,说实话,每一个后台程序员的短板就在于前端技能的弱而造就了不能更上一层楼.从今天开始,我将彻底开启菜 ...
- zabbix监控DELL服务器硬件状态
zabbix监控DELL服务器硬件状态 登录dell服务的管理页面 默认用户名:root 密码:calvin 服务器开放snmp信息,开启完应用 Zabbix服务器导入dell监控硬件模板 验证 sn ...
- [环境配置]Ubuntu 16.04+CUDA 9.0+OpenCV 3.2.0下编译基于Caffe的MobileNet-SSD踩过的一些坑
SSD是Caffe的一个分支,源码在github上:https://github.com/weiliu89/caffe/tree/ssd $ git clone https://github.com/ ...
- 你这一辈子要用到的C数学函数都在这
两数相加 #include <stdio.h> int main(void){ int a = 10; //定义变量a, 把10 赋值给a int b = 20; //定义变量 ...