Performance Tip : Measure performance of asp.net MVC Action Filters

Action Filtering is one of the “hidden parts” of asp.net MVC. When checking page execution time, it’s common to measure controller action or view generation, but it’s often uncommon to monitor filters (as for routing). After a few performance reviews, I now think it’s really important to not forget this part.

Just to remember, MVC filters are custom attributes that you put onto action methods/controllers/globally to add common functionality, with pre- and post-processing behaviors.

How filter works ? A good starting point is asp.net MVC source code and especially ControllerActionInvoker.

Using Miniprofiler

We all love great debugging tools and asp.net MVC MiniProfiler is one of them. However it does not come with a native way to instrument filter execution. But that’s however the tool we will use to report execution timings.

In order to evaluate performance of filters, a typical asp.net developer could create a new base filter attribute, and use this attribute for every filter in the target solution.

A basic implementation could be :

public void OnActionExecuted(ActionExecutedContext filterContext)
{
     using (MiniProfiler.StepStatic("Filter: " + actionFilter.GetType().Name + ".OnActionExecuted", ProfileLevel.Info))
     {
          actionFilter.OnActionExecuted(filterContext);
     }
}

Even if it’s a valid OOP approach, I don’t like to change the default base class for all of custom filters. Obviously, the will not work for third party filters coming from my favorites Nuget packages. The problem is not in this implementation but how we plugged it into asp.net MVC. However, I keep the code for the future.

An experimented asp.net MVC developer knows this is relatively easy to customize the default ControllerActionInvoker. As suggested in this post . The next step is to create an ActionInvoker that will wrap all ActionFilterAttributes with our wrapper class.

Especially, we will override the GetFilters method as follow :


protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
     FilterInfo baseFilters = base.GetFilters(controllerContext, actionDescriptor);

     WrapFilters(baseFilters.ActionFilters);
     WrapFilters(baseFilters.ResultFilters);

     return baseFilters;
}

Prior to ASP.NET MVC 4, the only way to switch out the action invoker was to write a custom controller factory. That’s not so complicated but that’s so long and annoying…

Since ASP.NET MVC 4, you can now simply inject an IActionInvoker using the dependency resolver. To understand how IoC/DI works in asp.net MVC, you could read the asp.net tutorial here. http://www.asp.net/mvc/tutorials/hands-on-labs/aspnet-mvc-4-dependency-injection

Using Ninject.MVC Nuget package, the final code is :


/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
     kernel.Bind<IActionInvoker>().To<ProfiledActionInvoker>();
}

That’s all, we did it ! Here is the result for the standard MVC 4 template and two custom global filters.

profiledglobalfilter

The complete Gist is available here.

Ok, I found a problem, how to solve it ?

The code in filter is generally easy to understand and you should use standard tools (Visual Studio Profiler). However there are two things you’ve got to be  carefully taught :

  • Filters instances

Using Filter Attributes, there is only a single attribute instance per application. Use an appropriate store for your filter state (see this post  from Brad Wilson)

Using FilterProviders, the filter provider will have a chance to decide whether a particular filter applies to the current request. If so, a new instance of the filter will be created  each time.

  • Canceling Filter Execution

According to MSDN, You can cancel filter execution in the OnActionExecuting and OnResultExecuting methods by setting the Result property to a non-null value.I’ve seen many cases where useless filters are invoked; remember everything has a cost.

What about Glimpse ?

In Glimpse, the Execution tab shows the ASP.NET MVC execution pipeline of actions, action results and action filters (including child actions) required to respond to the HTTP request. However, I find the execution tab not clear (too much data)  and I’m also less confident in Glimpse for production.

Because you have nothing to do, it’s not so funny :-)


profile for Cybermaxs - Betclic at Stack Overflow, Q&A for professional and enthusiast programmers

Share
facebooktwittergoogle_plusredditlinkedinmail
Tagged with: , ,
Posted in Development
Recent Comments
Newsletter