技术开发 频道

为ASP.NET MVC扩展异步Action功能

  由于在Action方法中可以调用BeginXxx方法,我们在AsyncActionResult中只需保留Begin方法返回的IAsyncResult,以及另一个对于EndXxx方法的引用。在AsyncActionResult的ExecuteResult方法中将会保存这两个对象,以便在AsyncMvcHandler的EndProcessRequest方法中重新获取并使用。根据“惯例”,我们还需要定义一个扩展方法,方便开发人员在Action方法中返回一个AsyncActionResult。具体实现非常容易,在这里就展示一下异步Action的编写方式:

[AsyncAction]
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 class AsyncMvcHandler : IHttpAsyncHandler, IRequiresSessionState
{
    
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对象进行释放。

0
相关文章