I'm currently having what i think is a threading issue with action filters, on my app i'm using an ActionFilter to perform tracing of each action, this trace will provide statistical information such as the duration of the call and also log the parameters being sent to the action.
The actual trace implementation (which was done by other team) works with an IDisposable object, basically when creating the instance initializes the start time, and when disposing the object sets the end date, both calls create an entry in a custom log, the code is bellow (remove some code for simplicity purposes):
public class TraceActionAttribute : ActionFilterAttribute
{
private IDisposable logManagerBeginTrace;
/// <summary>
/// Called by the ASP.NET MVC framework before the action method executes.
/// </summary>
/// <param name="filterContext">The filter context.</param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
List<object> parameters = new List<object>();
string actionName = filterContext.ActionDescriptor.ActionName;
Type controllerType = filterContext.Controller.GetType();
foreach (KeyValuePair<string, object> currentParameter in filterContext.ActionParameters)
{
parameters.Add(currentParameter.Value);
}
this.logManagerBeginTrace = LogManager.BeginMethodTrace(ApplicationConstants.ApplicationName, controllerType, actionName, Guid.NewGuid(), parameters.ToArray());
}
/// <summary>
/// Called by the ASP.NET MVC framework after the action method executes.
/// </summary>
/// <param name="filterContext">The filter context.</param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
this.logManagerBeginTrace.Dispose();
}
}
The exception is not telling me much, basically that it is trying to dispose elements while others are still active, i still have to look into the tracer code... but i found this post which says the following:
In previous versions of ASP.NET MVC, action filters were created per request except in a few cases. This behavior was never a guaranteed behavior but merely an implementation detail and the contract for filters was to consider them stateless. In ASP.NET MVC 3, filters are cached more aggressively. Therefore, any custom action filters which improperly store instance state might be broken.
Which for me appears very weirds, since action filters should be peer request, that's the reason we place public properties on it, and configure its behavior for specific Actions, don't we?
I appreciate any help on this, Regards.
One possible workaround for this is to store the object instance in HttpContext.Items
instead of in a private variable on the ActionFilter
class.
HttpContext.Items
is a per-request storage mechanism, which sounds like what you need.
This is what your modified code would roughly look like:
public class TraceActionAttribute : ActionFilterAttribute
{
private IDisposable logManagerBeginTrace;
/// <summary>
/// Called by the ASP.NET MVC framework before the action method executes.
/// </summary>
/// <param name="filterContext">The filter context.</param>
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
base.OnActionExecuting(filterContext);
List<object> parameters = new List<object>();
string actionName = filterContext.ActionDescriptor.ActionName;
Type controllerType = filterContext.Controller.GetType();
foreach (KeyValuePair<string, object> currentParameter in filterContext.ActionParameters)
{
parameters.Add(currentParameter.Value);
}
filterContext.HttpContext.Items["TraceActionKey"] = LogManager.BeginMethodTrace(ApplicationConstants.ApplicationName, controllerType, actionName, Guid.NewGuid(), parameters.ToArray());
}
/// <summary>
/// Called by the ASP.NET MVC framework after the action method executes.
/// </summary>
/// <param name="filterContext">The filter context.</param>
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
base.OnActionExecuted(filterContext);
((IDisposable)filterContext.HttpContext.Items["TraceActionKey"]).Dispose();
}
}
In MVC 5 at least the same behavior is true, action attributes are cached and reused. BUT not only that, be careful as the same instance can be used in multiple threads simultaneously.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With