ControllerFactory是基于路由的组件,它选择正确的controller并对其实例化。default factory会查找实现了IController并且以Controller结尾的类,然后通过反射使用无参构造函数进行实例化。
但如果您希望使用依赖注入,就不能再使用default factory,而必须使用支持IoC的controller factory。
使用支持IoC(依赖注入)的controller factory, 优点在于松耦合,可测试性很好。如果在Controller里面想要使用某些Service,要么new出来,要么用单例的形式,如UserService.Instance,这样想对Controller写单元测试都不容易,它和这些Service耦合太紧密,无法将这些Service替换成Stub实现。因此,松耦合是必须的。要实现这个功能,必须让依赖注入框架来创建Controller,才有可能注入依赖并组装对象。MVC里面有一个ControllerFactory的东西,可以使用来达到这个目的。
方法:
1、写一个类,继承自DefaultControllerFactory,例如 SpringControllerFactory : DefaultControllerFactory
2、覆盖方法GetControllerInstance,使用依赖注入框架来创建Conroller
3、修改Global.asax.cs, 在Application_Start内注册使用自己的ControllerFactory,
ControllerBuilder.Current.SetControllerFactory(new SpringControllerFactory())
4、对Controller进行构造函数注入,例如:
public class AccountController : Controller
{
private IRepository repository;
public IRepository Repository
{
get
{
return repository;
}
set
{
repository = value;
}
}
}
从现在开始,所有的Controller都是通过依赖注入框架来创建的,新增的Service就在依赖注入框架里面注册,Controller要使用哪些Service就往构造函数里加,反正框架会注入进来。
在“使用Spring.Net 1.3.2实现Container(IoC)(一)”已使用Spring.NET实现了一个简单的IoC容器ClassicContainer,故实现支持IoC的ControllerFactory
手到擒来。
MVCQuick的ControllerFactory实现代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using System.Collections;
using MVCQuick.Framework.Container;
namespace MVCQuick.Framework.Mvc
{
public class ClassicControllerFactory : DefaultControllerFactory
{
public override IController CreateController(RequestContext requestContext, string controllerName)
{
IController controller = ClassicContainer.GetObject(controllerName) as IController;
if (controller == null)
{
controller = base.CreateController(requestContext, controllerName);
}
AddActionInvokerTo(controller);
return controller;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
IController controller = null;
if (controllerType != null)
{
var controllers = ClassicContainer.GetObjects(controllerType);
if (controllers.Count > 0)
{
controller = (IController)controllers.Cast<DictionaryEntry>().First().Value;
}
}
else
{
controller = base.GetControllerInstance(requestContext, controllerType);
}
AddActionInvokerTo(controller);
return controller;
}
protected virtual void AddActionInvokerTo(IController controller)
{
if (controller == null)
return;
if (typeof(Controller).IsAssignableFrom(controller.GetType()))
{
((Controller)controller).ActionInvoker = new ClassicActionInvoker();
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using MVCQuick.Framework.Container;
namespace MVCQuick.Framework.Mvc
{
///<summary>
/// ActionInvoker implementation that enables the <see cref="IApplicationContext"/>to satisfy dependencies on ActionFilter attributes.
///</summary>
public class ClassicActionInvoker : ControllerActionInvoker
{
///<summary>
///
///</summary>
public ClassicActionInvoker()
{
}
///<summary>
/// Retrieves information about the action filters.
///</summary>
///<param name="controllerContext">The controller context.</param>
///<param name="actionDescriptor">The action descriptor.</param>
///<returns>Information about the action filters.</returns>
protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
//let the base class do the actual work as usual
var filterInfo = base.GetFilters(controllerContext, actionDescriptor);
//configure each collection of filters using the IApplicationContext
foreach (IActionFilter filter in filterInfo.ActionFilters.Where(f => f != null))
{
ClassicContainer.ConfigureObject(filter, filter.GetType().FullName);
}
foreach (IAuthorizationFilter filter in filterInfo.AuthorizationFilters.Where(f => f != null))
{
ClassicContainer.ConfigureObject(filter, filter.GetType().FullName);
}
foreach (IExceptionFilter filter in filterInfo.ExceptionFilters.Where(f => f != null))
{
ClassicContainer.ConfigureObject(filter, filter.GetType().FullName);
}
foreach (IResultFilter filter in filterInfo.ResultFilters.Where(f => f != null))
{
ClassicContainer.ConfigureObject(filter, filter.GetType().FullName);
}
return filterInfo;
}
}
}
ClassicControllerFactory使用方法
在Global.asax.cs类Application_Start()添加代码
//Register Repository
ClassicContainer.Register<NHibernateRepository>("NHibernateRepository");
//Register Controller
IDictionary properties = new Dictionary<String, Object>();
properties.Add("Repository", ClassicContainer.GetObject("NHibernateRepository"));
ClassicContainer.Register<AccountController>("Account", properties);
ClassicContainer.Register<CheckoutController>("Checkout", properties);
ClassicContainer.Register<HomeController>("Home", properties);
ClassicContainer.Register<ShoppingCartController>("ShoppingCart", properties);
ClassicContainer.Register<StoreController>("Store", properties);
ClassicContainer.Register<StoreManagerController>("StoreManager", properties);
ClassicContainer.Register<SampleDataController>("SampleData", properties);
//Set ControllerFactory
ControllerBuilder.Current.SetControllerFactory(typeof(ClassicControllerFactory));