Как разрешить внедрение зависимостей в атрибутах фильтра MVC

У меня есть класс настраиваемых атрибутов, производный от AuthorizationAttribute, который обеспечивает настраиваемую безопасность действий контроллера. Метод OnAuthorizationCore зависит от различных других компонентов (например, DAL), чтобы определить, может ли пользователь вызывать действие.

Я использую Autofac для внедрения зависимостей. ExtensibleActionInvoker утверждает, что может выполнять внедрение свойств в фильтры действий. Установка свойств атрибута во время выполнения (что кажется плохой идеей) будет работать в простом модульном тесте, но на загруженном многопоточном веб-сервере это обязательно пойдет не так, и поэтому эта идея кажется анти-шаблоном. Отсюда этот вопрос:

Если мой AuthorizationAttribute зависит от других компонентов для правильной работы, какой правильный шаблон [архитектуры] для достижения этого?

то есть AuthorizationAttribute зависит от IUserRepository... как следует разрешить эту связь?


person Mark    schedule 12.11.2010    source источник


Ответы (4)


ExtensibleActionInvoker утверждает, что может выполнять внедрение свойств в фильтры действий.

Правильно, но не путайте фильтры действий с атрибутами, которые могут их не реализовывать. Самый простой способ приблизиться к этому в ASP.NET MVC — разделить обязанности, даже несмотря на то, что среда MVC позволяет их комбинировать.

Например, используйте пару классов — класс атрибута, который содержит только данные:

// Just a regular old attribute with data values
class SomeAttribute : Attribute { ... }

И фильтр с внедренными зависимостями:

// Gets dependencies injected
class SomeFilter : IActionFilter { ... }

SomeFilter просто использует типичный подход получения атрибута SomeAttribute от контроллера или метода действия через GetCustomAttributes() для выполнения любой необходимой работы.

Затем вы можете использовать ExtensibleActionInvoker для подключения фильтра:

builder.RegisterControllers(...).InjectActionInvoker();
builder.RegisterType<ExtensibleActionInvoker>().As<IActionInvoker>();
builder.RegisterType<SomeFilter>().As<IActionFilter>();

Это может быть немного больше кода, чем вы написали бы, используя подход «атрибут как фильтр», но качество кода будет лучше в долгосрочной перспективе (например, избегая ограничений атрибутов и неудобных решений Service Locator). .)

person Nicholas Blumhardt    schedule 12.11.2010
comment
Спасибо Ник - и за обстоятельный ответ, и за блестящий фреймворк! - person Mark; 15.11.2010

Мне кажется, что самый простой способ добиться этого — стиснуть зубы и принять зависимость от самого autofac. Хотя зависимость от IoC сама по себе является антишаблоном, она несколько более приемлема. Вы можете реализовать свойство следующим образом:

public class UserAuthorizeAttribute : AuthorizeAttribute
{            
    public IUserRepository CurrentUserService
    {
        get
        {
            var cpa = (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
            var cp = cpa.ContainerProvider;
            return cp.RequestLifetime.Resolve<IUserRepository>();
        }
    }
}
     ...
person Mark    schedule 12.11.2010

Нет прямого способа сделать это до MVC2. Здесь подробно описан интересный метод: http://www.mattlong.com.au/?p=154. Я предлагаю использовать Common Service Locator, чтобы абстрагироваться от этого и найти ваш контейнер внедрения зависимостей.

Если вы используете MVC 3, вы можете использовать службу MVC. Местоположение

person David Neale    schedule 12.11.2010
comment
Какой смысл использовать CSL, если это все равно сервисный локатор? CSL обычно используется, когда вы знаете, что ваш код будет повторно использоваться кем-то другим, кто может предпочесть другой контейнер. Не думайте, что есть еще одна причина для его абстрагирования. - person Arnis Lapsa; 12.11.2010
comment
Тот заспорен до смерти - дело всегда сводится к идеологической точке зрения, - person David Neale; 12.11.2010

Внедрение конструктора кажется невозможным без изменения способа регистрации фильтра.

Даже в Asp.Net Mvc3:

Одно из мест, где внедрение зависимостей было затруднено в прошлом, — это сами атрибуты фильтра. Поскольку среда выполнения .NET Framework фактически отвечает за создание этих экземпляров атрибутов, мы не можем использовать традиционную стратегию внедрения зависимостей.

Итак, следующая лучшая вещь - это внедрение свойств (Mvc3 предоставляет некоторую поддержку для этого из коробки).

Вот как сделать это вручную.

Я лично использую MvcExtensions. Я могу зарегистрировать их в по-другому. Вот использование.

Вы также можете изучить проект MvcTurbine. В отличие от проекта MvcExtensions, который является более общим, MvcTurbine в первую очередь предназначен для обеспечения поддержки внедрения зависимостей.

person Arnis Lapsa    schedule 12.11.2010