由于在Action方法中可以调用BeginXxx方法,我们在AsyncActionResult中只需保留Begin方法返回的IAsyncResult,以及另一个对于EndXxx方法的引用。在AsyncActionResult的ExecuteResult方法中将会保存这两个对象,以便在AsyncMvcHandler的EndProcessRequest方法中重新获取并使用。根据“惯例”,我们还需要定义一个扩展方法,方便开发人员在Action方法中返回一个AsyncActionResult。具体实现非常容易,在这里就展示一下异步Action的编写方式:
public ActionResult AsyncAction(AsyncCallback asyncCallback, object asyncState)
{
SqlConnection conn = new SqlConnection("...;Asynchronous Processing=true");
SqlCommand cmd = new SqlCommand("WAITFOR DELAY '00:00:03';", conn);
conn.Open();
return this.Async(
cmd.BeginExecuteNonQuery(asyncCallback, asyncState),
(ar) =>
{
int value = cmd.EndExecuteNonQuery(ar);
conn.Close();
return this.View();
});
}
至此,似乎AsyncMvcHandler也无甚秘密可言了:
{
public AsyncMvcHandler(
Controller controller,
IControllerFactory controllerFactory,
RequestContext requestContext)
{
this.Controller = controller;
this.ControllerFactory = controllerFactory;
this.RequestContext = requestContext;
}
public Controller Controller { get; private set; }
public RequestContext RequestContext { get; private set; }
public IControllerFactory ControllerFactory { get; private set; }
public HttpContext Context { get; private set; }
public IAsyncResult BeginProcessRequest(
HttpContext context,
AsyncCallback cb,
object extraData)
{
this.Context = context;
this.Controller.SetAsyncCallback(cb).SetAsyncState(extraData);
try
{
(this.Controller as IController).Execute(this.RequestContext);
return this.Controller.GetAsyncResult();
}
catch
{
this.ControllerFactory.ReleaseController(this.Controller);
throw;
}
}
public void EndProcessRequest(IAsyncResult result)
{
try
{
HttpContext.Current = this.Context;
ActionResult actionResult = this.Controller.GetAsyncEndDelegate()(result);
if (actionResult != null)
{
actionResult.ExecuteResult(this.Controller.ControllerContext);
}
}
finally
{
this.ControllerFactory.ReleaseController(this.Controller);
}
}
}
在BeginProcessRequest方法中将保存当前Context——这点很重要,HttpContext.Current是基于CallContext的,一旦经过一次异步回调HttpContext.Current就变成了null,我们必须重设。接着将接收到的AsyncCallback和AsyncState保留,并使用框架中现成的Execute方法执行控制器。当Execute方法返回时一整个Action方法的调用流程已经结束,这意味着其调用结果——即IAsyncResult和EndDelegate对象已经保留。于是将IAsyncResult对象取出并返回。至于EndProcessRequest方法,只是将BeginProcessRequest方法中保存下来的EndDelegate取出,调用,把得到的ActionResult再执行一遍即可。
以上的代码只涉及到普通情况下的逻辑,而在完整的代码中还会包括对于Action方法被某个Filter终止或替换等特殊情况下的处理。此外,无论在BeginProcessRequest还是EndProcessRequest中都需要对异常进行合适地处理,使得Controller Factory能够及时地对Controller对象进行释放。