[转载]ASP.NET MVC中的扩展点(三)控制器工厂

[转载]MVC中的扩展点(三)控制器工厂 – xfrog – 博客园.

当路由系统找到与当前请求匹配的路由信息(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接收到请求到最终确定使用那个控制器,涉及到的类,如下图所示:

Controller

通过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对象:

mvc2

我们选用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
  1. using System;
    
  2. using System.Collections.Generic;
    
  3. using System.Linq;
    
  4. using System.Web;
    
  5. using System.Web.Mvc;
    
  6. namespace CnBlogs.ControllerFactory.Controllers
    
  7. {
    
  8.     public class ProductsController : Controller
    
  9.     {
    
  10.         private IProductsRepository _repository;
    
  11.         public ProductsController(IProductsRepository repository)
    
  12.         {
    
  13.             _repository = repository;
    
  14.         }
    
  15.         public ActionResult Index()
    
  16.         {
    
  17.             return View(_repository.GetProductsList());
    
  18.         }
    
  19.     }
    
  20. }
    

.src_container { background-color: rgb(231, 229, 220); width: 99%; overflow: hidden; margin: 12px 0pt ! important; padding: 0px 3px 3px 0px; }.src_container .titlebar { background-color: rgb(212, 223, 255); border-width: 1px 1px 0pt; border-style: solid solid none; border-color: rgb(79, 129, 189) rgb(79, 129, 189) -moz-use-text-color; padding: 3px 24px; margin: 0pt; width: auto; line-height: 120%; overflow: hidden; text-align: left; font-size: 12px; }.src_container .toolbar { display: inline; font-weight: normal; font-size: 100%; float: right; color: rgb(0, 0, 255); text-align: left; overflow: hidden; }.toolbar span.button { display: inline; font-weight: normal; font-size: 100%; color: rgb(0, 0, 255); text-align: left; overflow: hidden; cursor: pointer; }.src_container div.clientarea { background-color: white; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; height: auto; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,courier,monospace,serif; }.src_container ol.mainarea { padding: 0pt 0pt 0pt 52px; margin: 0pt; background-color: rgb(247, 247, 255) ! important; }.number_show { padding-left: 52px ! important; list-style: decimal outside none ! important; }.number_show li { list-style: decimal outside none ! important; border-left: 1px dotted rgb(79, 129, 189); }.number_hide { padding-left: 0px ! important; list-style-type: none ! important; }.number_hide li { list-style-type: none ! important; border-left: 0px none; }ol.mainarea li { display: list-item ! important; font-size: 12px ! important; margin: 0pt ! important; line-height: 18px ! important; padding: 0pt 0pt 0pt 0px ! important; background-color: rgb(247, 247, 255) ! important; color: rgb(79, 129, 189); }ol.mainarea li pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap ol.mainarea li pre { white-space: pre-wrap; word-wrap: break-word; }ol.mainarea li pre.alt { background-color: rgb(247, 247, 255) ! important; }7、创建与Index Action方法对应的视图Index.aspx

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

显示行号 复制代码 NinjectControllerFactory
  1. using System;
    
  2. using System.Collections.Generic;
    
  3. using System.Linq;
    
  4. using System.Web;
    
  5. using System.Web.Mvc;
    
  6. using Ninject.Modules;
    
  7. using Ninject;
    
  8. using CnBlogs.ControllerFactory.Models;
    
  9. using System.Configuration;
    
  10. namespace CnBlogs.ControllerFactory.Infrastructure
    
  11. {
    
  12.     public class NinjectControllerFactory : DefaultControllerFactory
    
  13.     {
    
  14.         private IKernel _kernel = new StandardKernel(new MyModule());
    
  15.         protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
    
  16.         {
    
  17.             if (controllerType == null)
    
  18.                 return null;
    
  19.             return (IController)_kernel.Get(controllerType);
    
  20.         }
    
  21.         private class MyModule : NinjectModule
    
  22.         {
    
  23.             public override void Load()
    
  24.             {
    
  25.                 //Bind<IProductsRepository>()
    
  26.                 //    .To<MockProductsRepository>();
    
  27.                 Bind<IProductsRepository>()
    
  28.                     .To<SqlProductsRepository>()
    
  29.                     .WithConstructorArgument("connectstring", ConfigurationManager.ConnectionStrings["TestDb"].ConnectionString);
    
  30.             }
    
  31.         }
    
  32.     }
    
  33. }
    

.src_container { background-color: rgb(231, 229, 220); width: 99%; overflow: hidden; margin: 12px 0pt ! important; padding: 0px 3px 3px 0px; }.src_container .titlebar { background-color: rgb(212, 223, 255); border-width: 1px 1px 0pt; border-style: solid solid none; border-color: rgb(79, 129, 189) rgb(79, 129, 189) -moz-use-text-color; padding: 3px 24px; margin: 0pt; width: auto; line-height: 120%; overflow: hidden; text-align: left; font-size: 12px; }.src_container .toolbar { display: inline; font-weight: normal; font-size: 100%; float: right; color: rgb(0, 0, 255); text-align: left; overflow: hidden; }.toolbar span.button { display: inline; font-weight: normal; font-size: 100%; color: rgb(0, 0, 255); text-align: left; overflow: hidden; cursor: pointer; }.src_container div.clientarea { background-color: white; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; height: auto; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,courier,monospace,serif; }.src_container ol.mainarea { padding: 0pt 0pt 0pt 52px; margin: 0pt; background-color: rgb(247, 247, 255) ! important; }.number_show { padding-left: 52px ! important; list-style: decimal outside none ! important; }.number_show li { list-style: decimal outside none ! important; border-left: 1px dotted rgb(79, 129, 189); }.number_hide { padding-left: 0px ! important; list-style-type: none ! important; }.number_hide li { list-style-type: none ! important; border-left: 0px none; }ol.mainarea li { display: list-item ! important; font-size: 12px ! important; margin: 0pt ! important; line-height: 18px ! important; padding: 0pt 0pt 0pt 0px ! important; background-color: rgb(247, 247, 255) ! important; color: rgb(79, 129, 189); }ol.mainarea li pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap ol.mainarea li pre { white-space: pre-wrap; word-wrap: break-word; }ol.mainarea li pre.alt { background-color: rgb(247, 247, 255) ! important; }9、在Global.asax.cs中设置自定义工厂

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    RegisterRoutes(RouteTable.Routes);

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

.codearea { color: black; background-color: white; line-height: 18px; border: 1px solid rgb(79, 129, 189); margin: 0pt; width: auto ! important; overflow: auto; text-align: left; font-size: 12px; font-family: “Courier New”,”Consolas”,”Fixedsys”,”BitStream Vera Sans Mono”,courier,monospace,serif; }.codearea pre { color: black; line-height: 18px; padding: 0pt 0pt 0pt 12px ! important; margin: 0em; background-color: rgb(255, 255, 255) ! important; }.linewrap pre { white-space: pre-wrap; word-wrap: break-word; }.codearea pre.alt { background-color: rgb(247, 247, 255) ! important; }.codearea .lnum { color: rgb(79, 129, 189); line-height: 18px; }

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

注意:本文涉及到有关的Linq、DI方面的知识,请参考相关资料。要运行本文示例,请先将源代码中的mdf文件附加到SqlServer Express中,并修改App.config中TestDb连接字符串的定义。

源代码下载

赞(0) 打赏
分享到: 更多 (0)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏