执行Action方法
对于执行同步Action的SyncMvcHandler,其实现十分简单而直接:
public class SyncMvcHandler : IHttpHandler, IRequiresSessionState
{
public SyncMvcHandler(
IController controller,
IControllerFactory controllerFactory,
RequestContext requestContext)
{
this.Controller = controller;
this.ControllerFactory = controllerFactory;
this.RequestContext = requestContext;
}
public IController Controller { get; private set; }
public RequestContext RequestContext { get; private set; }
public IControllerFactory ControllerFactory { get; private set; }
public virtual bool IsReusable { get { return false; } }
public virtual void ProcessRequest(HttpContext context)
{
try
{
this.Controller.Execute(this.RequestContext);
}
finally
{
this.ControllerFactory.ReleaseController(this.Controller);
}
}
}
而对于异步Action,我之前一直思考着怎么将框架的默认实现,也就是单个方法调用,转化成两个方法(BeginXxx/EndXxx)调用。曾经我想过自己实现一个新的ActionInvoker,但是这就涉及到了大量的工作,尤其是如果希望保持框架现有的功能(ActionFilter,ActionSelector等等),最省力的方法可能就是继承ControllerActionInvoker,并设法使用框架已经实现的各种辅助方法。但是在分析了框架代码之后我发现复用也非常困难,举例来说,ControllerActionInvoker判定一个方法为Action的依据之一是这个方法返回的是ActionResult类型或其子类,这意味着我无法直接使用这个方法来获取一个返回IAsyncResult的BeginXxx方法;同理,对于查找EndXxx方法,我可能需要在请求名为Abc的异步Action时,将EndAbc作为查找依据交由现成的方法来查询——但是,如果又有一个请求是直接针对一个名为EndAbc的同步Action的那又怎么办呢
由于这些问题存在,我在去年设法实现异步Action时几乎重写了整个ActionInvoker——其复杂程度可见一斑。而且那个实现对于一些特殊情况的处理依旧不甚友好,需要开发人员在一定程度上做出妥协。这个实现在TechED 2008 China的Session中公布时我就承认它并不能让我满意,建议大家不要将其投入生产环境中。而现在的实现,则非常顺利地解决了整个问题。虽然从理论上讲还不够“完美”,虽然还做出了一些让步。
带来如此多问题的原因就在于我们在设法颠覆框架内部的关键性设计,也就是从单一的Action方法调用,转变为“符合APM的”二段式调用。等等,您是否感觉到了解决问题的关键没错,那就是“符合APM的”。APM要求我们将一个行为分为BeginXxx和EndXxx两个方法,可是既然ASP.NET MVC框架只能让我们返回一个ActionResult对象……那么我们为什么不在这个对象里包含方法的引用——也就是一个委托对象呢这虽然不符合正统的APM签名,但是完全可行,不是吗
public class AsyncActionResult : ActionResult
{
public AsyncActionResult(
IAsyncResult asyncResult,
Func<IAsyncResult, ActionResult> endDelegate)
{
this.AsyncResult = asyncResult;
this.EndDelegate = endDelegate;
}
public IAsyncResult AsyncResult { get; private set; }
public Func<IAsyncResult, ActionResult> EndDelegate { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
context.Controller
.SetAsyncResult(this.AsyncResult)
.SetAsyncEndDelegate(this.EndDelegate);
}
}
由于在Action方法中可以调用BeginXxx方法,我们在AsyncActionResult中只需保留Begin方法返回的IAsyncResult,以及另一个对于EndXxx方法的引用。在AsyncActionResult的ExecuteResult方法中将会保存这两个对象,以便在AsyncMvcHandler的EndProcessRequest方法中重新获取并使用。根据“惯例”,