MVC / Unity - Как внедрить зависимости в пользовательские атрибуты FilterAttributes? - PullRequest
7 голосов
/ 24 июня 2009

У меня есть пользовательский HandleErrorAttribute, который расширяет FilterAttribute.

Как я могу добавить зависимости Unity в этот атрибут в то же время, когда он сам вводит зависимости контроллера?

Ответы [ 3 ]

2 голосов
/ 24 июня 2009

Хорошо, разобрался.

В основном я использовал решение Бена, приведенное выше в сообщении в блоге, на которое он указал.

Проблема в том, что Unity ведет себя немного по-другому.

Нельзя напрямую вводить зависимости в фильтры, поскольку они имеют тип IActionFilter и IExceptionFilter соответственно. Это заставило меня поверить, что они были только для чтения, что не так. Просто Unity нужно знать явный тип для инъекции.

Итак, в переопределенном методе, представленном в статье, пользователям Unity нужно запросить фильтры для рассматриваемых типов, а затем создать их.

public UnityActionInvoker(IUnityContainer container, IList<Type> typesToInject)
        {
            _container = container;
            _typesToInject = typesToInject;
        }

А затем в переопределенном методе сделайте что-то вроде этого:

  var needsInjection = filters.Where(filter => typesToInject.Contains(filter.GetType()));

Немного грязно, но это нужно сделать только один раз, и все будет развязано, как предлагает Бен.

Другая проблема заключается в том, что вы не можете вызывать _container.BuildUp (фильтр) внутри цикла foreach, поскольку фильтр в этом контексте доступен только для чтения.

1 голос
/ 10 мая 2010

Я тоже сталкивался с этой проблемой и теперь у меня есть рабочее решение. Это похоже на решение, описанное выше, но с некоторыми небольшими отличиями, а также с добавлением полного кода Unity.

Сначала я буду использовать внедрение свойств по причине, описанной выше, и, как и выше, я буду использовать метод BuildUp в Unity для внедрения свойств в уже созданные фильтры.

Для этого все мои контроллеры наследуются от нового пользовательского базового класса. В этом базовом классе я переопределяю метод CreateActionInvoker, чтобы установить свой собственный ActionInvoker.

Protected Overrides Function CreateActionInvoker() As System.Web.Mvc.IActionInvoker
    Return CustomActionInvoker
End Function

Затем в моем CustomActionInvoker я переопределяю метод GetFilters.

Protected Overrides Function GetFilters(ByVal controllerContext As ControllerContext, ByVal actionDescriptor As ActionDescriptor) As FilterInfo
    Dim info = MyBase.GetFilters(controllerContext, actionDescriptor)

    For Each MyAuthorizationFilter In info.AuthorizationFilters
        MvcApplication.Container.BuildUp(MyAuthorizationFilter.GetType, MyAuthorizationFilter)
    Next

    For Each MyActionFilter In info.ActionFilters
        MvcApplication.Container.BuildUp(MyActionFilter.GetType, MyActionFilter)
    Next

    For Each MyResultFilter In info.ResultFilters
        MvcApplication.Container.BuildUp(MyResultFilter.GetType, MyResultFilter)
    Next

    For Each MyExceptionFilter In info.ExceptionFilters
        MvcApplication.Container.BuildUp(MyExceptionFilter.GetType, MyExceptionFilter)
    Next

    Return info
End Function

Вопреки сказанному выше, я не обнаружил, что выполнение наращивания внутри цикла For Each вызывало какие-либо проблемы. Я также преодолел первоначальную проблему, состоящую в том, чтобы ссылаться на объект только через интерфейс, используя одну из других перегрузок метода BuildUp, который принимает System.Type, а также существующий объект.

Теперь, когда все вышеперечисленное сделано, я могу вставлять зависимости прямо в мои Фильтры.

Любые комментарии и мысли очень ценятся.

ура Майк

1 голос
/ 24 июня 2009

У вас есть два варианта

Первый вариант - написать собственный ActionInvoker, который не так сложен, как кажется. Проверьте это сообщение в блоге . Он специально работает с NInject, но Unity поддерживает внедрение свойств, поэтому вы можете изменить пример для использования Unity.

Это опция, которая объединяет ваш контейнер IoC и не рекомендуется.

public class MyFilter 
{ 
  IMyService MyService {get; set;}

  MyFilter() : MyFilter(MyUnityContainer.Resolve<IMyService>())
  { }

  MyFilter(IMyService service)
  {
    MyService = service;
  }
}
...