Code Outside of the Box

« Improving Async Support in ASP.NET MVC - Part 2 - Refactoring AsyncControllerActionInvoker Hydrogen Extensions for ASP.NET MVC 5 Alpha Release »

Improving Async Support in ASP.NET MVC - Part 3 - Async Filter API

First published on March 18, 2017

The obvious solution is to just copy the API from ASP.NET MVC Core.

public interface IAsyncAuthorizationFilter : IAuthorizationFilter
{
    Task OnAuthorizationAsync(AuthorizationContext context);
}

public interface IAsyncExceptionFilter : IExceptionFilter
{
    Task OnExceptionAsync(ExceptionContext context);
}

public interface IAsyncActionFilter : IActionFilter
{
    Task OnActionExecutionAsync(ActionExecutingContext context
        , ActionExecutionDelegate next);
}

public delegate Task<ActionExecutedContext> ActionExecutionDelegate();

public interface IAsyncResultFilter : IResultFilter
{
    Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next);
}

public delegate Task<ResultExecutedContext> ResultExecutionDelegate();

And that’s basically it.

But you’ll notice that these async interfaces must also implement their non-async counterparts. Unfortunately MVC5 doesn’t have a shared marker interface as exists in MVC Core. And so the framework (and many 3rd party extensions) directly reference the filter types for various reasons. This means that implementations of the async interfaces will just need to implement the non-async methods as a NOOP - annoying but not the end of the world.

Here’s an example implementation based on FilterAttribute:

public class AsyncActionFilterAttribute : FilterAttribute
    , IAsyncActionFilter
    , IAsyncResultFilter {

    public virtual void OnActionExecuting(ActionExecutingContext filterContext) {
    }

    public virtual void OnActionExecuted(ActionExecutedContext filterContext) {
    }

    public virtual async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) {
        OnActionExecuting(context);
        if (context.Result == null) {
            OnActionExecuted(await next().ConfigureAwait(false));
        }
    }

    public virtual void OnResultExecuting(ResultExecutingContext filterContext) {
    }

    public virtual void OnResultExecuted(ResultExecutedContext filterContext) {
    }

    public virtual async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) {
        OnResultExecuting(context);
        if (!context.Cancel) {
            OnResultExecuted(await next().ConfigureAwait(false));
        }
    }
}

Next time, putting everything together.

Comments

Comments are not moderated. But I reserve the right to delete anything hostile, offensive, or SPAMy.