技术开发 频道

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

   至于AsyncMvcRouteHandler的使用,只需在MapRoute时将Route Handler重新设置一下即可:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute(
"{resource}.axd/{*pathInfo}"
);

    routes.MapRoute(
        
"Default",                                              // Route name

        "{controller}/{action}/{id}",                           // URL with parameters
        new { controller = "Home", action = "Index", id = "" }  // Parameter defaults
    ).RouteHandler = new AsyncMvcRouteHandler();
}

  检查是否为异步Action
  
  从上面的代码中我们已经形成了一个约定:如果要执行一个异步Action,那么控制器对象必须为Controller类型。这个约定的目的是为了使用Controller类中包含的IActionInvoker——确切地说,是ControllerActionInvoker类型里的功能。因此,另一个约定便是Controller的ActionInvoker对象必须返回一个ControllerActionInvoker的实例。

  ControllerActionInvoker中有一些辅助方法,能够返回对于一个Controller或Action的描述对象。从一个Action描述对象中我们可以获取关于这个Action的各种信息,而它是否被标记了AsyncActionAttribute,就是我们判断这个Action是否应该被异步执行的依据。如下:

private static object s_methodInvokerMutex = new object();
private static MethodInvoker s_controllerDescriptorGetter;

internal static bool IsAsyncAction(
    Controller controller,
string actionName, RequestContext requestContext)
{
    var actionInvoker
= controller.ActionInvoker as ControllerActionInvoker;
    
if (actionInvoker == null) return false;

    
if (s_controllerDescriptorGetter == null)
    {
        
lock (s_methodInvokerMutex)
        {
            
if (s_controllerDescriptorGetter == null)
            {
                BindingFlags bindingFlags
= BindingFlags.Instance | BindingFlags.NonPublic;
                MethodInfo method
= typeof(ControllerActionInvoker).GetMethod(
                    
"GetControllerDescriptor", bindingFlags);
                s_controllerDescriptorGetter
= new MethodInvoker(method);
            }
        }
    }

    var controllerContext
= new ControllerContext(requestContext, controller);
    var controllerDescriptor
= (ControllerDescriptor)s_controllerDescriptorGetter.Invoke(
        actionInvoker, controllerContext);
    var actionDescriptor
= controllerDescriptor.FindAction(controllerContext, actionName);
    
return actionDescriptor == null ? false :
        actionDescriptor.GetCustomAttributes(
typeof(AsyncActionAttribute), false).Any();
}

  ControllerActionInvoker类型中有个protected方法GetControllerDescriptor,它接受一个ControllerContext类型的参数,并返回一个ControllerDescriptor对象来描述当前控制器,而从该描述对象中可以通过FindAction方法获得一个ActionDescriptor对象来描述即将执行的Action。如果是一个不存在的Action,那么就返回false,最后就通过SyncMvcHandler对象来执行默认的行为。当且仅当该Action上拥有AsyncActionAttribute标记时,才说明它应该被异步执行,返回true。此外,这段代码中用到了MethodInvoker,这是一个辅助类,它来源于Fast Reflection Library,它实现了反射调用功能,但是它的性能十分接近于方法的直接调用,我在这篇文章中详细描述了这个项目的功能和使用。

  这段代码便涉及到ASP.NET MVC RC版本在Beta版本基础上的改进。在原先的ControllerActionInvoker类中只有获取Action方法的MethodInfo,而没有RC中各描述对象这样的抽象类型。从目前的设计上来看,我们使用的都是基于反射的抽象描述类型的子类。例如默认情况下,我们通过ActionDescriptor抽象类型访问的实际上是ReflectedActionDescriptor类型的实例。这是一个很有用的改进,由于我们通过描述对象进行抽象,于是我们就可以:

  使用不同的实现方式来描述各对象,默认情况下是使用基于反射(也就是“约定”)的实现,如果需要的话我们也可以使用基于配置文件的方式替换现有实现。

  使用特定对象的描述方式可以不拘泥于内部细节,例如一个异步的Action可能就由两个方法组成。
有了特定的描述对象,也方便添加额外的属性,例如该Action是否应该异步执行,是否应该禁用Session State等等。

0
相关文章