Повторно ли используются атрибуты 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
    }
}
Другие вопросы по тегам