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浏览器(操作 ...
随机推荐
- webapi之权限验证
webapi之权限验证 一.概念: 二.demo: 1.登录时生成token: FormsAuthenticationTicket token = , account, DateTime.Now, D ...
- c# 求两个数中最大的值
1.三元运算符: class Program { static void Main(string[] args) { ,); Console.WriteLine("最大数:{0}" ...
- MongoDB复制集与Raft协议异同点分析
此文已由作者温正湖授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 一.日志复制流程: a.raft leader节点在接收client请求后,先将请求写到日志中,再将日志通过 ...
- PLSQL基本操作手册
第1章 用PLSQL连接Oracle数据库 PLSQL只能用来连接Oracle数据库(不象PB还可以连接JDBC.ODBC),所以必须首先安装并配置Oracle客户端. §1.1 初次登录PLSQL ...
- LINUX主机通过域名访问网络失败
故障现象: 用telnet IP+端口的方式测试正常,能够访问: 通过telnet域名+端口方式,提示:未知的名称或服务(Name or service not known). 测试百度的网络地址,p ...
- PHP内核研究:HASH表和变量 【转】
PHP HASH表 在PHP中,所有的数据 无论变量,常量,类,属性 都用Hash表来实现. 先要说说 HASH表 typedef struct bucket { ulong h; ...
- vue element-ui 表格筛选,点击筛选数据
<el-table-column prop="productEnvVersion" label="运行环境" :filters="this.ru ...
- 文本比较命令:diff
diff的三种模式,normal, context(-c), unified(-u) 一. 基本用法和例子 例1,比较2个文件的不同 root@hong-VirtualBox:~# dif ...
- 5104 I-country
5104 I-country 在 N*M 的矩阵中,每个格子有一个权值,要求寻找一个包含 K 个格子的凸连通块(连通块中间没有空缺,并且轮廓是凸的,如书中图片所示),使这个连通块中的格子的权值和最大. ...
- Eclipse中的常见设置
本文将移到下面的博客维护: 新的博客网址 当新建一个workspace时,习惯做下面的设置: 1. 在eclipse中,默认的Text file encoding是GBK(操作系统是中文简体):如果操 ...