zoukankan      html  css  js  c++  java
  • MVC中的扩展点(三)控制器工厂

        当路由系统找到与当前请求匹配的路由信息(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. 
      
    7. namespace CnBlogs.ControllerFactory.Controllers
      
    8. {
      
    9.     public class ProductsController : Controller
      
    10.     {
      
    11.         private IProductsRepository _repository;
      
    12. 
      
    13.         public ProductsController(IProductsRepository repository)
      
    14.         {
      
    15.             _repository = repository;
      
    16.         }
      
    17. 
      
    18.         public ActionResult Index()
      
    19.         {
      
    20.             return View(_repository.GetProductsList());
      
    21.         }
      
    22. 
      
    23.     }
      
    24. }
      
    25. 
      
    26. 
      

    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. 
      
    11. namespace CnBlogs.ControllerFactory.Infrastructure
      
    12. {
      
    13.     public class NinjectControllerFactory : DefaultControllerFactory
      
    14.     {
      
    15.         private IKernel _kernel = new StandardKernel(new MyModule());
      
    16. 
      
    17.         protected override IController GetControllerInstance(System.Web.Routing.RequestContext requestContext, Type controllerType)
      
    18.         {
      
    19.             if (controllerType == null)
      
    20.                 return null;
      
    21.             return (IController)_kernel.Get(controllerType);
      
    22.         }
      
    23. 
      
    24. 
      
    25.         private class MyModule : NinjectModule
      
    26.         {
      
    27.             public override void Load()
      
    28.             {
      
    29.                 //Bind<IProductsRepository>()
      
    30.                 //    .To<MockProductsRepository>();
      
    31. 
      
    32.                 Bind<IProductsRepository>()
      
    33.                     .To<SqlProductsRepository>()
      
    34.                     .WithConstructorArgument("connectstring", ConfigurationManager.ConnectionStrings["TestDb"].ConnectionString);
      
    35.             }
      
    36.         }
      
    37.     }
      
    38. }
      
    39. 
      

    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连接字符串的定义。

        源代码下载

  • 相关阅读:
    基于RSA securID的Radius二次验证java实现(PAP验证方式)
    一些指令 & 一些知识 (Linux Spring log4j...)
    RSA, ACS5.X 集成配置
    Python中的动态属性与描述符
    设计模式(一):单例模式
    JavaWeb
    动态规划_背包问题
    动态规划_最长上升子序列
    MySQL复习
    动态规划_数字三角形
  • 原文地址:https://www.cnblogs.com/xfrog/p/1916769.html
Copyright © 2011-2022 走看看