Внедрение объектов области запроса в MVC ActionFilterAttribute

У меня есть служба корзины покупок IShoppingCartService, которая определяется с помощью Ninject как InRequestScope

  this.Bind<IShoppingCartService>().To<ShoppingCartService>().InRequestScope();

Он загружает корзину покупок пользователя и кэширует ее внутри. Как только запрос завершен, объект службы исчезает.

Мне нужно было получить доступ к корзине внутри ActionFilter, поэтому я использовал атрибут [Inject].

public class MyActionFilterAttribute : ActionFilterAttribute
{
    [Inject]
    public IShoppingCartService ShoppingCartService
    {
       get; set;
    }
}

Затем я могу использовать его в фильтре действий, как это

 public override void OnActionExecuted(ActionExecutedContext filterContext)
 {
       var cart = ShoppingCartService.ShoppingCart;
       ...
 }

Теперь о странной части. Это работает нормально в течение года или более, а затем внезапно (после недавнего обновления Ninject и MVC) я замечаю, что корзина кэшируется (т.е. не InRequestScope).

При дальнейшем исследовании я вижу, что ActionFilters являются синглтонами (я проверил это, учитывая, что конструктор вызывался только один раз). Таким образом, как только экземпляр корзины загружен, он блокируется навсегда.

Итак, мой вопрос: как, черт возьми, это когда-либо работало? Что-то недавно изменилось в Ninject или MVC, что могло бы позволить этому работать раньше.

Мое исправление прямо сейчас состоит в том, чтобы сделать следующее:

 public IShoppingCartService ShoppingCartService
 {
     get
     {
         var scs = (IShoppingCartService) DependencyResolver.Current.GetService(typeof(IShoppingCartService));
         return scs;
     }
 }

Я действительно сбит с толку тем, как это вообще могло работать - и это определенно сработало. Что я должен был сделать вместо этого?


person Simon_Weaver    schedule 02.11.2015    source источник


Ответы (1)


Возможно, хотя атрибут фильтра был создан только один раз, свойство [Injected] изменялось каждый раз при обращении к странице. Но подождем :)

В 2010 году в Ninject.MVCx (x = 3, 5,..) был реализован «новый» способ вставки элементов в фильтры действий. Ему не нужно полагаться на атрибуты, и он поддерживает внедрение конструктора. Вот как это выглядит:

(Справедливое предупреждение: обычно я не занимаюсь разработкой asp.net, и мне не довелось попробовать, если следующее что-то изменит в жизненном цикле IActionFilter, так что вам придется попробовать это самостоятельно. Если вы делаете, пожалуйста, дайте отзыв)

public class MyActionFilter : IActionFilter
{
    private readonly IShoppingCartService shoppingCartService;

    public MyActionFilter(IShoppingCartService shoppingCartService)
    {
        this.shoppingCartService = shoppingCartService;
    }

    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        var cart = ShoppingCartService.ShoppingCart;
        ...
    }
}

Затем фильтр необходимо привязать специальным методом BindFilter:

this.BindFilter<MyActionFilter>(FilterScope.Action, 0)
    .WhenControllerTypeIs<YourController>();

который применил бы его ко всем действиям YourController.

Есть и другие цели, к которым вы можете применить фильтр, используя:

// Is applied to all actions of controllers which have the FooAttribute
this.BindFilter<MyActionFilter>(FilterScope.Action, 0)
    .WhenControllerHas<FooAttribute>();

// Is applied to all actions which have the FooAttribute
this.BindFilter<MyActionFilter>(FilterScope.Action, 0)
    .WhenActionHas<FooAttribute>();

// Is applied to all actions named Index
this.BindFilter<MyActionFilter>(FilterScope.Action, 0)
    .When((controllerContext,  actionDescriptor) =>
        actionDescriptor.ActionName == "Index");

Дополнительную информацию об этом можно найти в отличной статье Remo Gloor: Официальное расширение Ninject MVC получает поддержку MVC3 (глава Внедрение зависимостей для фильтров)

person BatteryBackupUnit    schedule 03.11.2015