本文介绍一下ASP.NET mvc中与Controller相关的高级特性,下图展示了一个MVC请求从接收到响应的过程要经历的组件的流程。
下面我们主要就来谈谈ASP.NET MVC5中的Controller Factory和Action Invoker。这两个组件通过名字也能够知道这它们的用途。
controller factory主要是用来创建Contoller的实例,而action invoker是用来查找并调用一个Contoller类中的Action方法。MVC框架本自己就已经我们实现了这两个组件,下面我们就分别举例说明一下如何在ASP.NET MVC5中,自己实现一个Controller Factory、Action Invoker,并介绍一下使用内置组件Controller Factory、Action Invoker的方法。
一、创建一个自定义的Controller Factory
对于ASP.NET MVC中的大多数类或组件,我们想要真正的搞懂它,最好的方式就是继承对应的接口或抽象类,然后自己写一个它的实现类。同理要了解Controller Factory是怎么创建一个Controller实例的我们也可以自己写一个Controller Factory我类,所有的Controller Factory都使用了接口IControllerFactory。
首先,我们来看看IControllerFactory在MVC框架中是怎么定义的:
- using System.Web.Routing;
- using System.Web.SessionState;
- namespace System.Web.Mvc {
- public interface IControllerFactory {
- IController CreateController(RequestContext requestContext,
- string controllerName);
- SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext,
- string controllerName);
- void ReleaseController(IController controller);
- }
- }
1、CreateController:负责创建Controller实例,返回类型是IController类型
2、GetControllerSessionBehavior:负责设置Controller的Session行为
3、ReleaseController:可以释放Controlller相关的资源
下面我们看看一个自定义自定义的Controller Factory的完整例子:
1.1、创建一个自定义Controller Factory
- using System;
- using System.Web.Mvc;
- using System.Web.Routing;
- using System.Web.SessionState;
- using ControllerExtensibility.Controllers;
- namespace ControllerExtensibility.Infrastructure {
- public class CustomControllerFactory: IControllerFactory {
- public IController CreateController(RequestContext requestContext,
- string controllerName) {
- Type targetType = null;
- switch (controllerName) {
- case "Product":
- targetType = typeof(ProductController);
- break;
- case "Customer":
- targetType = typeof(CustomerController);
- break;
- default:
- requestContext.RouteData.Values["controller"] = "Product";
- targetType = typeof(ProductController);
- break;
- }
- return targetType == null ? null :
- (IController)DependencyResolver.Current.GetService(targetType);
- }
- public SessionStateBehavior GetControllerSessionBehavior(RequestContext
- requestContext, string controllerName) {
- return SessionStateBehavior.Default;
- }
- public void ReleaseController(IController controller) {
- IDisposable disposable = controller as IDisposable;
- if (disposable != null) {
- disposable.Dispose();
- }
- }
- }
- }
HttpContext:http请求上下文。
RouteData:MVC路由相关信息。
第二个是要创建的controller名。
在CreateController方法的最后使用了MVC内置的依赖注入容器解析类来创建一个类的实例:
- return targetType == null ? null :
- (IController)DependencyResolver.Current.GetService(targetType);
1.2、注册自定义Controller Factory类到MVC5框架,使其生效
自定义Controller Factory类在项目中要生效,必须要注册到MVC5框架中。注册自定义Controller Factory类会用到类ControllerBuilder类,如下:
- using System;
- using System.Collections.Generic;
- using System.linq;
- using System.Web;
- using System.Web.Http;
- using System.Web.Mvc;
- using System.Web.Routing;
- using ControllerExtensibility.Infrastructure;
- namespace ControllerExtensibility {
- public class MvcApplication : System.Web.HttpApplication {
- protected void Application_Start() {
- AreaRegistration.RegisterAllAreas();
- WebApiConfig.Register(GlobalConfiguration.Configuration);
- FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters)
- RouteConfig.RegisterRoutes(RouteTable.Routes);
- ControllerBuilder.Current.SetControllerFactory(new
- CustomControllerFactory());
- }
- }
- }
二、使用MVC5自带的Controller Factory即DefaultControllerFactory
注意:上面实现了一个自定义了的ControllerFactory,你应该明白了ControllerFactory的作用了,但是不建议你自己实现ControllerFactory,因为你负责处理一些潜在的问题,比如:有歧义的Controller名字--不同命名空间相同名称的Controller类,构造函数异常等等。其实最快速有效的方式是使用MVC框架的自带的Controller Factory,DefaultControllerFactory,因为这些问题都为我们解决了。但是要使用DefaultControllerFactory,Controller类必须要满足一下几个条件:
1、类必须是public的
2、类不能够是abstract抽象的
3、类不能够带有泛型参数
4、类名必须要以Controller结尾
5、类必须实现接口IController
DefaultControllerFactory自身会维护保持这一个满足以上条件的类列表,这样不用每次请求都去项目中搜索这样类。
2.1、设置Controller的命名空间的优先级
我们可以设置指定命名空间的Controller就先去匹配,如下:
- public class MvcApplication : System.Web.HttpApplication {
- protected void Application_Start() {
- AreaRegistration.RegisterAllAreas();
- WebApiConfig.Register(GlobalConfiguration.Configuration);
- FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
- RouteConfig.RegisterRoutes(RouteTable.Routes);
- ControllerBuilder.Current.DefaultNamespaces.Add("MyControllerNamespace");
- ControllerBuilder.Current.DefaultNamespaces.Add("MyProject.*");
- }
- }
注意:这里两个Add是不分先后顺序的,并不表示写在前面就先去对应命名空间搜索。
2.2、自定义DefaultControllerFactory创建Controlller的实例
自定义DefaultControllerFactory创建Controlller的实例的方式有很多种,有时为了把我们的项目耦合性降低,我们一般会引入DI(依赖注入)。
1、使用MVC自带的Dependency Resolver
当有Dependency Resolver可用时,DefaultControllerFactory会使用它来创建Controller实例。
定义一个Dependency Resolver
- using System;
- using System.Collections.Generic;
- using System.Web.Mvc;
- using Ninject;
- using Ninject.Parameters;
- using Ninject.Syntax;
- using System.Configuration;
- using EssentialTools.Models;
- namespace EssentialTools.Infrastructure {
- public class NinjectDependencyResolver : IDependencyResolver {
- private IKernel kernel;
- public NinjectDependencyResolver() {
- kernel = new StandardKernel();
- AddBindings();
- }
- public object GetService(Type serviceType) {
- return kernel.TryGet(serviceType);
- }
- public IEnumerable<object> GetServices(Type serviceType) {
- return kernel.GetAll(serviceType);
- }
- private void AddBindings() {
- kernel.Bind<IValueCalculator>().To<LinqValueCalculator>();
- }
- }
- }
上面使用依赖注入DI(Ioc框架)--Ninject,最后在Gloabl中注册上面的NinjectDependencyResolver
- using EssentialTools.Infrastructure;
- using System.Web.Http;
- using System.Web.Mvc;
- using System.Web.Routing;
- namespace EssentialTools {
- public class MvcApplication : System.Web.HttpApplication {
- protected void Application_Start() {
- AreaRegistration.RegisterAllAreas();
- DependencyResolver.SetResolver(new NinjectDependencyResolver());
- WebApiConfig.Register(GlobalConfiguration.Configuration);
- FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
- RouteConfig.RegisterRoutes(RouteTable.Routes);
- }
- }
- }
2、使用一个Controller Activator
除了上面使用DependencyResolver来控制DefaultControllerFactory创建Controller,还可以定义一个Controller Activator,然后把这个类的实例通过DefaultControllerFactory的构造函数传进去。
我们先来看一看定义一个Controller Activator要用到的接口IControllerActivator:
- namespace System.Web.Mvc {
- using System.Web.Routing;
- public interface IControllerActivator {
- IController Create(RequestContext requestContext, Type controllerType);
- }
- }
- using ControllerExtensibility.Controllers;
- using System;
- using System.Web.Mvc;
- using System.Web.Routing;
- namespace ControllerExtensibility.Infrastructure {
- public class CustomControllerActivator : IControllerActivator {
- public IController Create(RequestContext requestContext,
- Type controllerType) {
- if (controllerType == typeof(ProductController)) {
- controllerType = typeof(CustomerController);
- }
- return (IController)DependencyResolver.Current.GetService(controllerType);
- }
- }
- }
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Web.Http;
- using System.Web.Mvc;
- using System.Web.Routing;
- using ControllerExtensibility.Infrastructure;
- namespace ControllerExtensibility {
- public class MvcApplication : System.Web.HttpApplication {
- protected void Application_Start() {
- AreaRegistration.RegisterAllAreas();
- WebApiConfig.Register(GlobalConfiguration.Configuration);
- FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
- RouteConfig.RegisterRoutes(RouteTable.Routes);
- ControllerBuilder.Current.SetControllerFactory(new
- DefaultControllerFactory(new CustomControllerActivator()));
- }
- }
- }