Повторно ли используются атрибуты ActionFilterAttributes в разных потоках? Как это работает?
Я провел некоторое тестирование со следующим кодом, чтобы попытаться выяснить, как работают ActionFilterAttributes:
public class TestAttribute : ActionFilterAttribute
{
private string _privateValue;
public string PublicValue { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_privateValue = DateTime.Now.ToString();
base.OnActionExecuting(filterContext);
}
}
Когда я запускаю приведенный выше код в двух параллельных потоках, поле _privateValue запутывается. Однако свойство PublicValue не запутывается.
Мне кажется, что ActionFilterAttributes повторно используются в потоках, но новые экземпляры создаются в зависимости от констант, указанных в открытых свойствах. Я прав?
Где я могу найти информацию об этом?
1 ответ
Это будет зависеть от версии ASP.NET MVC, но вы никогда не должны хранить состояние экземпляра в фильтре действий, который будет повторно использоваться между различными методами. Вот цитата, например, из одного из последних изменений в ASP.NET MVC 3:
В предыдущих версиях ASP.NET MVC фильтры действий создавались по запросу, за исключением нескольких случаев. Такое поведение никогда не было гарантированным поведением, а являлось лишь деталью реализации, и контракт на фильтры заключался в том, чтобы считать их лицами без состояния. В ASP.NET MVC 3 фильтры кэшируются более агрессивно. Поэтому любые фильтры пользовательских действий, которые неправильно хранят состояние экземпляра, могут быть повреждены.
По сути, это означает, что один и тот же экземпляр фильтра действий может быть повторно использован для различных действий, и если вы сохранили в нем состояние экземпляра, он, вероятно, сломается.
А с точки зрения кода это означает, что вы никогда не должны писать фильтр действий, подобный этому:
public class TestAttribute : ActionFilterAttribute
{
private string _privateValue;
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
_privateValue = ... some calculation
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// use _privateValue here
}
}
но вы должны написать это так:
public class TestAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var privateValue = ... some calculation
filterContext.HttpContext.Items["__private_value__"] = privateValue;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
var privateValue = filterContext.HttpContext.Items["__private_value__"];
// use privateValue safely here
}
}