007.ASP.NET MVC控制器依赖注入
原文链接:http://www.codeproject.com/Articles/560798/ASP-NET-MVC-Controller-Dependency-Injection-for-Be
前言:在这篇文章中,我将通过一个demo,直截了当地说明依赖注入在MVC框架中的使用。
内容列表:
1.介绍
2.为什么使用控制器依赖注入
3.控制器静态结构
4.自定义控制器
5.Framework中控制器的创建
6.为什么使用控制器工厂模式
7.控制器工厂模式
7.1.目标1
7.2.目标2
8.使用MEF实现控制器工厂模式
9.重点补充
一:介绍

首先简单地说明控制器在MVC框架中要做的几件事情
1.接收HTTP请求
2.处理HTTP请求
3.操作客户端输入的数据
4.发送回复给客户端
5.作为Model和View的中转站

MVC框架在运行时自己创建控制器对象,有一个先决条件,控制器类的构造函数是无参的。如果你想传递一个对象作为控制器的参数,这种情况我们又该如何处理?创建这种类型的控制器会失败,我们需要创建自己的控制器,将控制器参数注入到控制器。
有多种方式可以将参数注入到控制器的构造方法中
1.设置属性
2.通过方法
3.构造方法
在这篇文章中,我将解释如何使用控制器注入到MVC中的构造函数中。如果不使用自定义控制器工厂模式,控制器注入是无法实现的。当然我也会解释如何创建简单的控制器工厂,然后注册到MVC框架。我也会展示一种方法注入控制器,使用MEF。
二.为什么使用控制器注入
在现实的程序开发中,你会看到绝大多数的MVC程序需要注入它所依赖的组件。你可以直接创建组件在控制器中,而不需要注入它们。在这种情况下,组件和控制器紧密结合,如果一个组件的扩展发生了改变,或者一个新版本的组件要使用,你就需要改变控制器中的实现(PS:讲解为什么使用控制器注入)
当你想使用单元测试的时候,另一种困难你可能会遇到。你不能测试这些控制器在一个独立的单元。你不能模仿一些新的特性,如果不能模仿,你将不能成功运行你的代码在一个独立的环境。
三.控制器静态结构
MVC框架中的控制器结构是定义在一个叫Controller的抽象类中,如果你想创建一些控制器,首先你需要创建一个类,从抽象类Controller中继承,UML类图如下:

所有的控制器都有一个根接口IController,抽象类ControllerBase从它去实现自己的方法。另一个抽象类从ControllerBase中继承,这个类就是Controller,所有的自定义的控制器类都要从Controller中继承,或者从它的子类中继承。
四.简单的自定义控制器
如果你创建一个MVC工程,你将会得到两个控制器,AccountController和HomeController


如果你去看HomeController中的代码实现,你会发现它没有自己的构造方法。
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start your ASP.NET MVC application.";
return View();
}
public ActionResult About()
{
ViewBag.Message = "Your app description page.";
return View();
}
}
我们都知道这里没有一个构造方法,但在编译时会创建一个无参的构造方法。
ublic class HomeController : Controller
{
public HomeController()
{
}
}
现在我将要创建一个ILogger接口及它的一个实现类DefaultLogger类,Home控制器会使用ILogger类型的对象作为参数,注入到它的控制器构造方法中。
public interface ILogger
{
void Log(string logData);
}
public class DefaultLogger : ILogger
{
public void Log(string logData)
{
System.Diagnostics.Debug.WriteLine(logData, "default");
}
}
带参ILogger的Home控制器构造方法如下:
public class HomeController : Controller
{
private readonly ILogger _logger;
public HomeController(ILogger logger)
{
_logger = logger;
}
}
直到现在你也没找到我们在什么地方实例化了DefaultLogger对象,也不知道如何传递这个对象到控制器的构造方法中,在编写代码阶段程序不会报错,但是在运行代码会报错,如下:

看上面的线程记录,DefaultControllerActivator对象会抛出一个异常MissingMethonException。如果你到MSDN,找这个异常是如何引发的,你会发现找到不到适当的方法。看接下来的异常InvalidOperationException,它确实包含了MissingMethodException,将下来你会看到更加有用的信息,确保在控制器构造方法中有一个带参数的构造方法。如果想让代码工作正常,我必需要加带一个参数的构造方法,框架会创建控制器对象通过我们创建的那个构造方法。问题在于,我们如何传递一个一个DefaultLogger对象到这个控制器。请继续你好练习,我们接着往下看。
五.MVC框架是如何创建控制器对象
在我们开始注入DefaultLogger对象到HomeController之前,我们要有一个概念,MVC框架是如何创建一个控制器对象的。IControllerFactory接口的主要责任就是创建控制器对象。DefaultControllerFactory是框架默认提供的可扩展的类。如果你添加一个无参的构造方法,然后设置一个断点,你将会发现程序在这一刻,将停留在这里。

看上面的图片,你可以看到IControllerFactory类型的一个DefaultControllerFactory对象,DefaultControllerFactory有一些方法,如:Create,GetControllerInstance,CreateController,这些方法将会创建一个HomeController对象,MVC框架是开源的,如果你想知道更多的方法,可以下载官方的资源,自己去阅读。你看调试的代码,你可以看到DefaultControllerFactory对象被CurrentControllerFactory

六.为什么要自定义控制器工厂
现在我们知道默认的控制器工厂使用一个无参的构造方法创建一个控制器对象。我们可以注入自己的带参的控制器构造方法。
public class HomeController : Controller
{
private readonly ILogger _logger;
public HomeController():this(new DefaultLogger())
{
}
public HomeController(ILogger logger)
{
_logger = logger;
}
}
我发现许多的开发者都对上面依赖注入有所误解,它不是一个依赖注入的形式。这个确实违反了组件的原则。而这个原则的愿意是:上层的模块不能依赖于低层级的模块,双方都要依赖于抽象层,细节要在体现在抽象层。在上面的代码中,HomeController创建了自己的DefaultLogger对象。它直接依赖于ILogger接口的扩展(DefaultLogger),如果在将来一个新的扩展(扩展了ILogger接口),我们需要修改我们HomeController中的方法,所以我们需要使用适当的方法去注入我们的组件。我们要使用一个带参的构造方法去注入我们的ILogger 组件,但是默认的 DefaultControllerFactory不支持我们这么做,所以我们要创建自己的控制器工厂。
七.自定义控制器工厂
我使用两种方法展示如何创建自己的控制器工厂。
7.1途径1
我们可以创建一个新的控制器工厂,扩展了IControllerFactory接口,假定我们自定义的控制器工厂的名称叫CustomControllerFactory,如下
public class CustomControllerFactory : IControllerFactory
{
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
ILogger logger = new DefaultLogger();
var controller = new HomeController(logger);
return controller;
}
public System.Web.SessionState.SessionStateBehavior GetControllerSessionBehavior(
System.Web.Routing.RequestContext requestContext, string controllerName)
{
return SessionStateBehavior.Default;
}
public void ReleaseController(IController controller)
{
IDisposable disposable = controller as IDisposable;
if (disposable != null)
disposable.Dispose();
}
}
现在第一步,我们需要将CustomControllerFactory注册到MVC框架,完成这件事要在Application_Start事件中书写代码。
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
RegisterCustomControllerFactory ();
}
}
private void RegisterCustomControllerFactory ()
{
IControllerFactory factory = new CustomControllerFactory();
ControllerBuilder.Current.SetControllerFactory(factory);
}
如果你运行你的程序,你会发现那个无参的构造方法没有被执行,那个带参的构造方法执行了。你的问题就这么简单的解决了。

你可以构建你的控制器工厂使用反射机制。
public class CustomControllerFactory : IControllerFactory
{
private readonly string _controllerNamespace;
public CustomControllerFactory(string controllerNamespace)
{
_controllerNamespace = controllerNamespace;
}
public IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
ILogger logger = new DefaultLogger();
Type controllerType = Type.GetType(string.Concat(_controllerNamespace, ".", controllerName, "Controller"));
IController controller = Activator.CreateInstance(controllerType, new[] { logger }) as Controller;
return controller;
}
}
7.2途径2
这里方法不是去扩展IControllerFactory接口,而是去继承DefaultControllerFactory类,通过修改其中的方法。当然也要将控制器工厂注入到程序启动的事件中。代码如下:
public class CustomControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
{
ILogger logger = new DefaultLogger();
IController controller = Activator.CreateInstance(controllerType, new[] { logger }) as Controller;
return controller;
}
}
(去掉了MEF创建自定义控制器工厂的方法,因为自己也实在不能理解,但是要看啊)。
007.ASP.NET MVC控制器依赖注入的更多相关文章
- ASP.NET MVC IOC依赖注入之Autofac系列(二)- WebForm当中应用
上一章主要介绍了Autofac在MVC当中的具体应用,本章将继续简单的介绍下Autofac在普通的WebForm当中的使用. PS:目前本人还不知道WebForm页面的构造函数要如何注入,以下在Web ...
- ASP.NET MVC IOC依赖注入之Autofac系列(一)- MVC当中应用
话不多说,直入主题看我们的解决方案结构: 分别对上面的工程进行简单的说明: 1.TianYa.DotNetShare.Model:为demo的实体层 2.TianYa.DotNetShare.Repo ...
- ASP.NET MVC实现依赖注入
在java的spring中有自动注入功能,使得代码变得更加简洁灵活,所以想把这个功能移植到c#中,接下来逐步分析实现过程 1.使用自动注入场景分析 在asp.net mvc中,无论是什么代码逻辑分层, ...
- ASP.NET MVC Autofac依赖注入的一点小心得(包含特性注入)
前言 IOC的重要性 大家都清楚..便利也都知道..新的ASP.NET Core也大量使用了这种手法.. 一直憋着没写ASP.NET Core的文章..还是怕误导大家.. 今天这篇也不是讲Core的 ...
- ASP.NET MVC IOC依赖注入之Autofac系列开篇
Autofac为IOC组件,实现控制反转,主要结合面向接口编程,完成较大程度的解耦工作. 使用IOC,必须面向接口编程,所谓的面向接口编程,即程序中依赖于抽象,而不依赖于具体实现. 需要所有的业务逻辑 ...
- ASP.NET MVC 控制器激活(一)
ASP.NET MVC 控制器激活(一) 前言 在路由的篇章中讲解了路由的作用,讲着讲着就到了控制器部分了,从本篇开始来讲解MVC中的控制器,控制器是怎么来的?MVC框架对它做了什么?以及前面有的篇幅 ...
- ASP.NET MVC 控制器激活(二)
ASP.NET MVC 控制器激活(二) 前言 在之前的篇幅中,用文字和图像来表示了控制器的激活过程,描述的角度都是从框架默认实现的角度去进行描述的,这样也使得大家都可以清楚的知道激活的过程以及其中涉 ...
- 详解ASP.NET MVC 控制器
1 概述 在阅读本篇博文时,建议结合上篇博文:详解ASP.NET MVC 路由 一起阅读,效果可能会更好些. Controller(控制器)在ASP.NET MVC中负责控制所有客户端与服务端的 ...
- 【ASP.NET MVC系列】浅谈ASP.NET MVC 控制器
ASP.NET MVC系列文章 [01]浅谈Google Chrome浏览器(理论篇) [02]浅谈Google Chrome浏览器(操作篇)(上) [03]浅谈Google Chrome浏览器(操作 ...
随机推荐
- pandas set_index() reset_index()
set_index() 官方定义: 使用一个或多个现有列设置索引, 默认情况下生成一个新对象 DataFrame.set_index(keys, drop=True, append=False, ...
- day04.1-三元表达式与列表解析
1. 三元表达式 name = input("请输入:") res = "英雄" if name=="令狐冲" else "伪君子 ...
- ubuntu14.04安装zabbix
1. apt-get updateapt-get install apache2 mysql-server libapache2-mod-php5 php5-gd php5-mysql php5-co ...
- PL/SQL那点事-->修改Oracle数据库里面的字段长度
在开发过程中,遇到有个问题:在Oracle数据库中,利用PL/SQL数据库开发工具来开发,某一字段的长度不能满足需求时候,采用下面的语法就行修改 alter table 表名 modify 字段名 长 ...
- Python导入模块Import和from+Import区别
在我们使用python的时候会发现使用Import可以导入模块,from+Import也可以,那么他们之间有什么区别,该用哪一种呢?让我们来看看 1.首先在demo.py中创建一个变量a,定义一个函数 ...
- POJ3074 Sudoku
POJ3074 Sudoku 与POJ2676相比,这一题搜索时每一步都找到最好确定的点进行枚举 对于每行.每列.每个九宫格,都分别用一个9位二进制数保存还有那些数还可以填 对于每个位置,将其所在行. ...
- Intellij IDEA 封装Jar包(提示错误: 找不到或无法加载主类)
封装的过程如下: 然后准备打包 选择Build或者ReBuild即可. 但这样就会引起开始第一个图的问题.提示无法加载主类,另外一个情况就是所有的外部第三方jar包都被封装到一个jar包里面了. 那么 ...
- 洛谷 P3204 [HNOI2010]公交线路
题面 luogu 题解 矩阵快速幂\(+dp\) 其实也不是很难 先考虑朴素状压\(dp\) \(f[i][S]\) 表示最慢的车走到了\(i\),\([i, p+i-1]\)的覆盖情况 状态第一位一 ...
- VS中的DataPager分页
微软的DataPager分页功能很强大,不要设置数据库存储过程,只要添加个DataPager控件,关联下要分页的控件,简单设置就可以有不错的分页效果.当然要有更理想的效果还是要前台和后台处理下. wi ...
- Apache 配置代理服务
1.根据项目需要,Apache服务下面有2个tomcat 分别指向不同的域名 2.修改 Apache-conf-httpd.conf LoadModule proxy_module modules/m ...