系列目录

回顾

系列的前4节深入剖析了ASP.NET URL路由机制,以及MVC在此基础上是如何实现Areas机制的,同时涉及到inbound和outbound很多细节部分。第2节中提到MvcRouteHandler是MVC框架的入口,这节开始,从MvcRouteHandler往下说开去。

 

Controller的创建过程:Builder和Factory

MvcRouteHandler的实现仅仅是通过GetHttpHandler方法返回一个MvcHandler实例,MvcHandler从RouteData中获得controller名字负责创建一个ControllerBuilder的实例,并通过ControllerBuilder的GetControllerFactory返回一个IControllerFactory的实例,这个实例就是DefaultControllerFactory,它的 CreateController方法负责创建需要的Controller实例。下面这段代码来自MvcHandler

// Get the controller type
string controllerName = RequestContext.RouteData.GetRequiredString("controller");
// Instantiate the controller and call Execute
factory = ControllerBuilder.GetControllerFactory();
controller = factory.CreateController(RequestContext, controllerName);

DefaultControllerFactory创建Controller分两步:

Type controllerType = GetControllerType(requestContext, controllerName);
IController controller = GetControllerInstance(requestContext, controllerType);

GetControllerInstance最终是这样创建controller的:

return (IController)Activator.CreateInstance(controllerType);

这就使得要使用DI设计模式十分困难。好在我们可以重写GetControllerInstance,并应用各种DI。受书中的影响,个人使用的是Ninject

另外,为了加快GetControllerType方法,DefaultControllerFactory内部运用缓存机制将当前程序和所有引用程序集的类型缓存成哈希表。

还可以通过如下方法,提供某些名字空间较高的优先级来优化搜索。

protected void Application_Start() 
{ 
    RegisterRoutes(RouteTable.Routes); 
    ControllerBuilder.Current.DefaultNamespaces.Add("MyApp.Controllers.*"); 
    ControllerBuilder.Current.DefaultNamespaces.Add("OtherAssembly.MyNamespace.*"); 
}

上面这种设置名字空间的优先级仍然低于在设置路由表时为某个路由指定的名字空间,Areas机制就是靠在设置路由表时指定名字空间实现的,详见深入理解ASP.NET MVC(4)

也可以在ControllerBuilder层面上替换ControllerFactory:

protected void Application_Start() 
{ 
    RegisterRoutes(RouteTable.Routes); 
    ControllerBuilder.Current.SetControllerFactory(new MyControllerFactory()); 
}

 

Controller的调用:Controller只是普通的.NET类

MvcHandler依靠ControllerBuilder和IControllerFactory获得Controller的实例后,调用Controller实例的Execute方法,在该方法返回后再调用IControllerFactory的ReleaseController收尾。由此看出Controller的Execute做了所有的事情,看似“功能强大”,其实它只是.NET的普通类, MVC框架赋予其“非凡”的能力:

所有的Controller只要实现一个接口,IController,接口的定义了Execute方法:

namespace System.Web.Mvc {
    using System.Web.Routing;

    public interface IController {
        void Execute(RequestContext requestContext);
    }
}

从接口定义可以看出,当Controller被“调用”的时候,应该负责完成Execute方法,参数RequestContext封装了HttpContext,所以可以像下面这样直接实现一个Controller,同样可以工作:

public class HelloWorldController : IController  
{ 
    public void Execute(RequestContext requestContext) 
    { 
        requestContext.HttpContext.Response.Write("Hello, world!");
    } 
}

然而,需要的逻辑和架构远比这个复杂的多,因此MVC框架提供了如下类关系:

IController->ControllerBase->Controller

其中ControllerBase实现Execute,Execute在内部调用ExecuteCore,ExecuteCore作为一个抽象方法,延迟到Controller中实现。ControllerBase只提供了诸如TempData、ViewData等,Controller的ExecuteCore方法真正invoke了action机制,是action的入口。下面的代码是ExecuteCore的实现:

PossiblyLoadTempData();
try {
 	string actionName = RouteData.GetRequiredString("action");
                if (!ActionInvoker.InvokeAction(ControllerContext, actionName)) {
                    HandleUnknownAction(actionName);
                }
     }
 finally {
                PossiblySaveTempData();
            }

可以看到RouteData在这里又提供了action参数,可以想象InvokeAction方法依靠这个action的名字调用action,并实现诸多验证机制。下节开始讨论action逻辑。

劳动果实,转载请注明出处:http://www.cnblogs.com/P_Chou/archive/2010/11/20/details-asp-net-mvc-05.html