您的位置:首页 > 其它

MVC中的扩展点(三)控制器工厂

2018-02-03 16:12 309 查看
当路由系统找到与当前请求匹配的路由信息(
RouteData
),而路由信息中的
RouteHandler
MvcRouteHandler
,那么此请求将由
MvcRouteHandler
返回的
MvcHandler
来负责处理。默认情况下
MvcHandler
将根据请求上下文(
RequestContext
)中的信息找到对应的控制器和活动方法,通过调用活动方法,返回应答内容,将其返回给客户端。

MvcHandler
类通过使用单例类
ControllerBuilder
GetControllerFactory
方法获取当前指定的
IControllerFactory
对象,通过此对象来生成具体的
IController
控制器。对于
MvcHandler
来说,它只关心
IController
对象,即只负责调用IController的Execute方法来产生应答内容。
Action
方法是MVC框架默认
Controller
类中实现的一种机制,它通过路由信息中的action参数来确定调用
Controller
类中的某个对应方法,以此产生应答内容。

默认情况下,MVC框架使用
DefaultControllerFactory
控制器工厂,它使用路由信息中的
controller
参数来确定具体的控制器类,并通过反射机制生成控制器实例。

MvcRouteHandler
接收到请求到最终确定使用那个控制器,涉及到的类,如下图所示:



通过
ControllerBuilder
SetControllerFactory
方法我们可以指定自定义的控制器工厂,自定义工厂可以直接实现
IControllerFactory
接口,也可以从
DefaultControllerFactory
类继承,如果直接实现
IControllerFactory
,则我们必须自己实现一种机制将路由信息与控制器对应起来。所以为了使用
MVC
中默认的路由解析功能,我们通常从
DefaultControllerFactory
继承,然后根据实际情况覆写
GetControllerInstance
GetControllerType
方法,其中
GetControllerInstance
用于返回一个IController控制器,
GetControllerType
用于根据路由信息获取合适的控制器类型。显然,在默认控制器工厂中,
CreateController
方法先调用
GetControllerType
找出类型,然后调用
GetControllerInstance
方法创建一个控制器实例。

DefaultControllerFactory
使用
Activator.CreateInstance
方法来创建控制器对象,所以
MVC
默认的控制器必须具有无参构造函数。下面我们使用依赖注入(DI)技术使我们的自定义控制器工厂具有实例化有参控制器。

设想以下情景:领域模型中有个
Product
类,我们为其定义了一个
IProductsRepository
的存储管理类。首先,我们实现了一个
SqlProductsRepository
类,用于从
SqlServer
数据库中获取产品资料,然后,为方便测试,我们还实现了一个
MockProductsRepository
类,用于返回一个简单的产品列表。我们建立一个
ProductsController
控制器,为确定使用哪种存储实现,我们要求在生成控制器实例时,必须传入一个
IProductsRepository
对象:



我们选用NInject开源库来实现DI(下载地址:http://ninject.org/download):

1、下载NInject,并解压

2、新建一个空的MVC工程

3、添加引用Ninject.dll、System.Data.Linq

4、在SQL Server Management Studio Express中附加上源代码中的Test数据库

5、按上述类图实现各个接口及类


6、创建一个
ProductsController
,增加一个带
IProductsRepository
参数的构造函数

ProductsController

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

namespace CnBlogs.ControllerFactory.Controllers

{

public class ProductsController : Controller

{

private IProductsRepository _repository;

public ProductsController(IProductsRepository repository)

{

_repository = repository;

}

public ActionResult Index()

{

return View(_repository.GetProductsList());

}

}

}


7、创建与
Index Action
方法对应的视图
Index.aspx


8、创建一个控制器工厂:
NinjectControllerFactory
NinjectControllerFactory

using System;

using System.Collections.Generic;

using System.Linq;

using System.Web;

using System.Web.Mvc;

using Ninject.Modules;

using Ninject;

using CnBlogs.ControllerFactory.Models;

using System.Configuration;

namespace CnBlogs.ControllerFactory.Infrastructure

{

public class NinjectControllerFactory : DefaultControllerFactory

{

private IKernel _kernel = new StandardKernel(new MyModule());

protected override IController GetControllerInstance(System.Web.Routing.RequestContext
requestContext, Type controllerType)

{

if (controllerType == null)

return null;

return (IController)_kernel.Get(controllerType);

}

private class MyModule : NinjectModule

{

public override void Load()

{

//Bind<IProductsRepository>()

//    .To<MockProductsRepository>();

Bind<IProductsRepository>()

.To<SqlProductsRepository>()

.WithConstructorArgument("connectstring", ConfigurationManager.ConnectionStrings["TestDb"].ConnectionString);

}

}

}

} ```

9、在`Global.asax.cs`中设置自定义工厂


protected void Application_Start()

{

AreaRegistration.RegisterAllAreas();

RegisterRoutes(RouteTable.Routes);

ControllerBuilder.Current.SetControllerFactory(new NinjectControllerFactory());

}


“`

Ok,要在虚拟产品列表与真实的
SqlServer
中的产品列表中切换,我们只需修改
MyModule
中的
Load
方法,将
Controller
构造函数中的特定参数绑定到一个具体的实现。

注意:本文涉及到有关的
Linq、DI
方面的知识,请参考相关资料。要运行本文示例,请先将源代码中的mdf文件附加到
SqlServer Express
中,并修改
App.config
TestDb
连接字符串的定义。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: