MVC实用架构设计(二)——使用MEF应用IOC(依赖倒置)

 

前言

  在《上篇》中,基本的项目结构已经搭建起来了,但是有个问题,层与层之间虽然使用了接口进行隔离,但实例化接口的时候,还引入了接口实现类的依赖。如下图:

  面向接口编程,Controller应该只依赖于站点业务层的接口,而不能依赖于具体的实现,否则,就违背了在层之间设置接口的初衷了。

  另外,如果上层只依赖于下层的接口,在做单元测试的时候,就可以用MoqFakes等Mock工具来按实际需求来模拟接口的实现,就可以灵活的控制接口的返回值来对各种情况进行测试,如果依赖于具体的实现,项目的可测试性将大大减小,不利于进行自动化的单元测试。

  要不依赖于具体的实现,就不能使用通常的 T t = new T() 的方式来获得一个类的实例了,需要通过IOC容器来对对象生命周期,依赖关系等进行统一的管理。

MEF的优势

  .net中可用的IOC容器非常多,如 CastleWindsor,Unity,Autofac,ObjectBuilder,StructureMap,Spring.Net等,这些第三方工具各不相同,但功能大体都相同,大都需要事先对接口与实现进行配对(通过代码或配置文件),然后由系统自动或手动来通过接口来获得相应实现类的实例,对象实例化的工作由IOC容器自动完成。

  MEF相对于上面的这些IOC容器有什么优势呢?下面是我推荐的理由:

  1. .net4.0 自带:MEF的功能在 System.ComponentModel.Composition.dll 程序集中,直接引用即可使用,不用安装第三方组件
  2. 0 配置:MEF是不需要使用配置文件或代码对接口与实现进行一一配对的,只需要简单的使用几个Attribute特性,就能自动完成源与目标的配对工作
  3. 自动化:系统初始化时自动遍历程序目录或指定文件夹下的dll,根据程序集中接口与类的特定Attribute特性进行自动配对。

MEF在桌面程序中的使用

  在桌面程序中,需要完成两个部分的目录匹配,一个是dll中的匹配,另一个为exe程序集中的匹配,分别使用到DirectoryCatalog与AssemblyCatalog两个目录类。而两个目录类需加入到 AggregateCatalog 目录类中,才能参与组合容器CompositionContainer的初始化。

  在服务提供方的实现类中,使用 ExportAttribute 标记要与之匹配的接口,如下图所示。在服务调用方,使用 ImportAttribute 来给接口注入实现类的实例,如上图所示。

  由于调用方法为静态的方法,Program类的实例仍需手动从组件容器中获得,然后尝试登录:

  输出结果,接口AccountContract并没有赋值,但能输出其实现类的信息,同时登录也能成功调用:

MEF在MVC中的使用

  在MVC的项目中,IOC组件是通过 DependencyResolver类中的 SetResolver(IDependencyResolver resolver) 方法来向MVC提供注册点的,所以我们只需要实现一个 IDependencyResolver 接口的MEF实现类,即可完成MEF在MVC中的注册工作。

  另外考虑到Web应用程序的无状态性,即每次访问都是独立进行的,所以IOC组件产生的对象实例也必须唯一,否则不同用户的操作就可能串线,产生相互干扰。在这里,我们使用HttpContext.Current.Items集合来保存 组合容器CompositionContainer的实例,以使每个用户的数据保持独立,并且同一用户的同一个Http请求中使用同一对象实例。另外考虑到可能会有某种情况下需要手动获取组合容器中的实例,把组合容器缓存到了当前上下文中的Application中。

  MefDependencySolver实现代码如下:

 1     /// <summary>
2 /// MEF依赖关系解析类
3 /// </summary>
4 public class MefDependencySolver : IDependencyResolver
5 {
6 private readonly ComposablePartCatalog _catalog;
7 private const string HttpContextKey = "MefContainerKey";
8
9 public MefDependencySolver(ComposablePartCatalog catalog)
10 {
11 _catalog = catalog;
12 }
13
14 public CompositionContainer Container
15 {
16 get
17 {
18 if (!HttpContext.Current.Items.Contains(HttpContextKey))
19 {
20 HttpContext.Current.Items.Add(HttpContextKey, new CompositionContainer(_catalog));
21 }
22 CompositionContainer container = (CompositionContainer)HttpContext.Current.Items[HttpContextKey];
23 HttpContext.Current.Application["Container"] = container;
24 return container;
25 }
26 }
27
28 #region IDependencyResolver Members
29
30 public object GetService(Type serviceType)
31 {
32 string contractName = AttributedModelServices.GetContractName(serviceType);
33 return Container.GetExportedValueOrDefault<object>(contractName);
34 }
35
36 public IEnumerable<object> GetServices(Type serviceType)
37 {
38 return Container.GetExportedValues<object>(serviceType.FullName);
39 }
40
41 #endregion
42 }

  在Global.asax.cs的Application_Start方法中初始化MEF容器,由于Web应用程序中只需要在DLL中查找匹配,所以只使用DirectoryCatalog即可。

  在AccountController类中加入MEF的Attribute标签,并删除原来构造函数中的AccountContract属性的赋值代码

  在加入了IOC组件之后,我们的架构就变成了面向接口编程的架构了,上层代码仅依赖于下层的接口,而不依赖于下层的具体实现,为了防止站点业务层的实现代码中出现如下所示的代码:

IAccountSiteContract accountContract = new AccountSiteService();

上一篇文章中也提到过,需要把站点业务层中的实现类的可访问性由 public 修改为 Internal,以实现上层代码对下层代码的真正隔离。

  其他代码不变,运行网站,同样能正常调用登录功能

  最后,给个温馨提示:

  MEF的导出导入是整体关联的,只要树中某一个部件匹配失败,整个树将无法实例化,也就是会出现Import的属性值为null的情况,这种情况下,可以使用MEF开发团队提供的调试工具MEFX来进行问题的快速定位。具体使用方法参考如下:

源码获取

   GMFrameworkForBlog2.zip

  为了让大家能第一时间获取到本架构的最新代码,也为了方便我对代码的管理,本系列的源码已加入微软的开源项目网站 http://www.codeplex.com,地址为:

  https://gmframework.codeplex.com/

  可以通过下列途径获取到最新代码:

  • 如果你是本项目的参与者,可以通过VS自带的团队TFS直接连接到 https://tfs.codeplex.com:443/tfs/TFS17 获取最新代码
  • 如果你安装有SVN客户端(亲测TortoiseSVN 1.6.7可用),可以连接到 https://gmframework.svn.codeplex.com/svn 获取最新代码
  • 如果以上条件都不满足,你可以进入页面 https://gmframework.codeplex.com/SourceControl/latest 查看最新代码,也可以点击页面上的 Download 链接进行压缩包的下载,你还可以点击页面上的 History 链接获取到历史版本的源代码

使用MEF应用IOC(依赖倒置)的更多相关文章

  1. [Unity 设计模式]IOC依赖倒置

    1.前言 最近在看<游戏开发与设计模式>一书,看到控制反转设计模式,作者说:上层模块不应该依赖于下层模块,上层模块和下层模块都应该依赖于接口,这样能减少耦合.然后附带举了个例子,我觉得特别 ...

  2. Unity Ioc 依赖倒置及Untity AOP被动拦截/自动拦截

    各位博友金安,首先声明这是一篇转载的博客,原文链接:https://www.cnblogs.com/scottpei/archive/2013/01/08/2851087.html 十年河东,十年河西 ...

  3. IoC模式(依赖、依赖倒置、依赖注入、控制反转)

    1.依赖 依赖就是有联系,有地方使用到它就是有依赖它,一个系统不可能完全避免依赖.如果你的一个类或者模块在项目中没有用到它,恭喜你,可以从项目中剔除它或者排除它了,因为没有一个地方会依赖它.下面看一个 ...

  4. 对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解

    1.概述 所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模 ...

  5. 对依赖倒置原则(DIP)及Ioc、DI、Ioc容器的一些理解(转)

    所谓依赖倒置原则(Dependence Inversion Principle)就是要依赖于抽象,不要依赖于具体.简单的说就是要求对抽象进行编程,不要对实现进行编程,这样就降低了客户与实现模块间的耦合 ...

  6. 个人对【依赖倒置(DIP)】、【控制反转(IOC)】、【依赖注入(DI)】浅显理解

    一.依赖倒置(Dependency Inversion Principle) 依赖倒置是面向对象设计领域的一种软件设计原则.(其他的设计原则还有:单一职责原则.开放封闭原则.里式替换原则.接口分离原则 ...

  7. 【转载】浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)

    原文地址 http://blog.csdn.net/briblue/article/details/75093382 写这篇文章的原因是这两天在编写关于 Dagger2 主题的博文时,花了大量的精力来 ...

  8. 依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI)

    原文: https://blog.csdn.net/briblue/article/details/75093382 写这篇文章的原因是这两天在编写关于 Dagger2 主题的博文时,花了大量的精力来 ...

  9. 轻松学,浅析依赖倒置(DIP)、控制反转(IOC)和依赖注入(DI) 依赖注入和控制反转的理解,写的太好了。

    轻松学,浅析依赖倒置(DIP).控制反转(IOC)和依赖注入(DI) 2017年07月13日 22:04:39 frank909 阅读数:14269更多 所属专栏: Java 反射基础知识与实战   ...

随机推荐

  1. 【琉忆分享】新手如何学习PHP?附上PHP知识导图。

    你好,是我--琉忆.PHP程序员面试系列图书作者. 作为一名PHP开发者过来人,也是经历了菜鸟到老手的过程,在此给那些想学PHP的同学指条路,即使你是转行学PHP一样可以学会PHP. (如果觉得下面这 ...

  2. kubernetes集群之Pod说能不能让我体面的消亡呀?

    kubernetes集群之Pod说能不能让我体面的消亡呀? 由于 Pod 所代表的是在集群中节点上运行的进程,当不再需要这些进程时允许其体面地终止. 1.如果 preStop 回调所需要的时间长于默认 ...

  3. OpenHarmony移植:如何适配utils子系统之KV存储部件

    摘要:本文介绍移植开发板时如何适配utils子系统之KV存储部件,并介绍相关的运行机制原理. 本文分享自华为云社区<OpenHarmony移植案例与原理 - utils子系统之KV存储部件> ...

  4. Vulhub-漏洞环境的搭建(详细版)

    安装Vulhub需要的基础环境 更新现有的软件 复制代码 1 2 sudo apt-get update sudo apt-get upgrade 安装Docker 复制代码 1 2 3 4 5 6 ...

  5. 【C#】COM线程模型-套间 ApartmentState

    线程模式是微软的COM基础中的极其重要的概念.一定要吃透!初始一个STA套间实际上是相当于开了一个消息窗口,所有调用经此窗口过程调度到组件内. [STAThread] 可以理解成CoInitializ ...

  6. Spring5框架学习笔记(详细)

    目录 01 Spring框架概述 02 IOC容器 IOC概念和原理 IOC BeanFactory接口 IOC操作 Bean管理(概念) IOC操作 Bean管理(基于xml方式) IOC操作 Be ...

  7. 如何给远程桌面发送“Ctrl+Alt+Delete”命令

    转至:https://www.yunweiku.com/forum.php?mod=viewthread&tid=925 当我们远程桌面需要是用ctrl+alt+delete组合键时无法使用, ...

  8. Python 爬取 "王者荣耀.英雄壁纸" 过程中的矛和盾

    1. 前言 学习爬虫,最好的方式就是自己编写爬虫程序. 爬取目标网站上的数据,理论上讲是简单的,无非就是分析页面中的资源链接.然后下载.最后保存. 但是在实施过程却会遇到一些阻碍. 很多网站为了阻止爬 ...

  9. 《Selenium+Pytest Web自动化实战》视频试听课程

    环境准备 1.1 python3环境安装 1.2 selenium3和chrome环境 1.3 pycharm安装 webdriver API 2.1基本操作 2.2元素定位id_name_class ...

  10. Context包源码解析(附面经)

    Context包源码解析 Context就相当于一个树状结构 最后请回答一下这个问题:context包中的方法是线程安全吗? Context包中主要有一个接口和三个结构体 Context接口 type ...