Как разрешить инжекцию зависимостей в атрибутах фильтра MVC
У меня есть собственный класс атрибутов, полученный из AuthorizationAttribute, который выполняет пользовательскую безопасность при действиях контроллера. Метод OnAuthorizationCore зависит от различных других компонентов (например, DAL), чтобы судить, может ли пользователь вызывать действие.
Я использую Autofac для инъекций зависимостей. ExtensibleActionInvoker утверждает, что может выполнять инъекцию свойств в фильтрах действий. Настройка свойств атрибута во время выполнения (что кажется плохим идеей) будет работать в простой unit test, но на занятом многопоточном веб-сервере он обязательно пойдет не так, и поэтому эта идея кажется анти-шаблоном. Отсюда этот вопрос:
Если мой атрибут AuthorizationAttribute зависит от других компонентов, чтобы работать правильно, что это правильный шаблон [architecture] для достижения этого?
то есть. AuthorizationAttribute зависит от IUserRepository... как это должно быть разрешено?
Ответы
Ответ 1
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.)
Ответ 2
Нет прямого способа сделать это до MVC2. Здесь есть интересная техника: http://www.mattlong.com.au/?p=154. Я предлагаю использовать Общий локатор сервисов, чтобы абстрагироваться от этого и найти ваш контейнер DI.
Если вы используете MVC 3, вы можете использовать Местоположение службы MVC
Ответ 3
Казалось бы, самый простой способ добиться этого - укусить пулю и принять зависимость от самого автофака. В то время как зависимость от IoC сама по себе является анти-шаблоном, она несколько более паллатична. Вы можете реализовать свойство следующим образом:
public class UserAuthorizeAttribute : AuthorizeAttribute
{
public IUserRepository CurrentUserService
{
get
{
var cpa = (IContainerProviderAccessor)HttpContext.Current.ApplicationInstance;
var cp = cpa.ContainerProvider;
return cp.RequestLifetime.Resolve<IUserRepository>();
}
}
}
...
Ответ 4
Встраивание конструктора представляется невозможным без изменения способа регистрации фильтра.
Даже в Asp.Net Mvc3:
Одно место, где в прошлом было сложной инъекцией зависимостей, заключается в самих атрибутах фильтра. Поскольку среда выполнения .NET Framework фактически отвечает за создание этих экземпляров атрибутов, мы не можем использовать традиционную стратегию инъекций зависимостей.
Итак - следующая лучшая вещь - инъекция свойств (Mvc3 предоставляет некоторую поддержку этому из коробки).
Здесь как для этого вручную.
Я лично использую MvcExtensions. Я в порядке, зарегистрировав их в по-другому. Здесь использование.
Еще одна вещь, которую вы можете исследовать, - это MvcTurbine. В отличие от проекта MvcExtensions, который является более общим - MvcTurbine в первую очередь предназначен для поддержки поддержки впрыска.