I am moving more and more of my aspect-oriented concerns into ActionFilters on my ASP.NET MVC site. As a result, I suspect some of my performance bottlenecks may have moved into ActionFilters with no visibility of what their performance is. I would like to hook into ActionFilter creation process of MVC to wrap each ActionFilter so that I can log how long the execution of that ActionFilter takes.
So f开发者_运维问答ar, I have found the following web links which hint at how to start down the road but am not sure how to actually wrap each ActionFilter.
- http://haacked.com/archive/2011/08/10/writing-an-asp-net-mvc-controller-inspector.aspx
- http://msdn.microsoft.com/en-us/magazine/gg309182.aspx
- Property injection without using attribute [Inject] with Ninject
You should derive and new class from ControllerActionInvoker and override the GetFilters method. In this method you should create a new instance of a diagnostic wrapper class implementing the same interface (IActionFilter) that wrap the real filter. GetFilters is called as part of the InvokeAction method (also can be overridden) but GetFilters is probably the cleanest.
Also you could also try overriding InvokeActionMethodWithFilters which is where the action filters are actually invoked however again this could be messy but may be ok if you just added something like:
protected override ActionExecutedContext InvokeActionMethodWithFilters(ControllerContext controllerContext, IList<IActionFilter> filters, ActionDescriptor actionDescriptor, IDictionary<string, object> parameters)
{
Stopwatch diagWatch = Stopwatch.StartNew();
var context = base.InvokeActionMethodWithFilters(controllerContext, filters, actionDescriptor, parameters);
diagWatch.Stop();
return context;
}
To implement an ControllerActionInvoker class see:
public class DiagControllerActionInvoker : ControllerActionInvoker
{
protected override FilterInfo GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
FilterInfo baseFilters = base.GetFilters(controllerContext, actionDescriptor);
//Add new filters
//e.g. baseFilters.ActionFilters.Insert(0, actionFilter);
return baseFilters;
}
}
To actually specify that your custom ControllerActionInvoker is called you need to create a DefaultControllerFactory implementation like:
public class SiteControllerFactory : DefaultControllerFactory
{
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
{
return null;
}
// Create controller class. Here.
IController controller = (IController)Activator.CreateInstance(controllerType)
if (typeof(Controller).IsAssignableFrom(controllerType))
{
Controller controllerInstance = controller as Controller;
if (controllerInstance != null)
{
controllerInstance.ActionInvoker = new DiagControllerActionInvoker();
}
}
return controller;
}
}
And like by setting is in the a your application code like so:
ControllerBuilder.Current.SetControllerFactory(new SiteControllerFactory());
- A simpler approach might be to write integration tests to measure performance for each action filter or combination.
- There are global action filters available in MVC3; you might want to write performance attribute which would start and stop timer and give you total time actions filter ran for.
- If you are looking to analyze application as a whole in production there are lot of commercial products available DotTrace, Dynatrace and so on which provide drill down for break down cost of each call.
精彩评论