Ninject to Simple Injector: зарегистрируйте фильтр

Я перехожу с Ninject на Simple Injector из-за лучшей производительности в моем приложении MVC 3. Я могу зарегистрировать репозитории и сервисы в порядке. Но есть фильтр, который прописан в Ninject с помощью

kernel.BindFilter<UserActivityAttribute>(FilterScope.Controller, 0).WhenControllerHas<UserActivityFilter>();

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


person Prithvi Ramana    schedule 17.07.2013    source источник


Ответы (1)


В Simple Injector нет эквивалента BindFilter. Поскольку я не знаком с этой функцией Ninject, я не могу рассказать вам, как ее смоделировать (хотя это, конечно, возможно).

Конечно, вы можете пометить свои контроллеры атрибутами фильтра, но, вероятно, это то, что вы пытаетесь предотвратить в первую очередь (если я понимаю, что делает эта функция Ninject BindFilter).

Лично я сторонник применения сквозных задач (таких как ведение журнала) с помощью декораторов. Вы можете либо применить декоратор на границе сервисного уровня (например, в этой статье показывает) или применить декораторы на уровне контроллера.

Применение декораторов вокруг контроллеров требует немного больше работы. Для этого вам нужно изменить способ регистрации контроллеров. Вероятно, это будет ваша текущая регистрация:

container.RegisterMvcControllers(Assembly.GetExecutingAssembly());

Чтобы иметь возможность применять декораторы, вам необходимо зарегистрировать контроллеры по их базовому типу (в данном случае IController). Есть несколько способов сделать это, но мне нравится этот подход:

// We get all controller types for the current assembly.
var controllerTypes = SimpleInjectorMvcExtensions
    .GetControllerTypesToRegister(container,
        Assembly.GetExecutingAssembly());

// Here we register a collection of controllers.
container.RegisterAll<IController>(controllerTypes);

// We register a custom IControllerActivator.
container.RegisterSingle<IControllerActivator>(() => 
    new SimpleInjectorControllerActivator(
        container.GetAllInstances<IController>(),
        controllerTypes));

Поскольку контроллеры регистрируются по их базовому типу IController, теперь мы можем применять декораторы вокруг контроллеров:

container.RegisterDecorator(typeof(IController),
    typeof(UserActivityControllerDecorator), c =>
    c.ImplementationType.GetCustomAttribute<UserActivityFilter>()
        .Any());

Вместо того, чтобы сопоставлять контроллеры с их конкретным типом (используя Register<HomeController>() или Bind<HomeController>().ToSelf() Ninject, мы теперь регистрируем их как коллекцию. Пользовательский IControllerActivator теперь может получать определенные элементы из коллекции:

// using System.Linq;

internal sealed class SimpleInjectorControllerActivator 
    : IControllerActivator
{
    private readonly IEnumerable<IController> controllers;
    private readonly Dictionary<Type, int> mapping;

    public SimpleInjectorControllerActivator(
        IEnumerable<IController> controllers, 
        Type[] controllerTypes)
    {
        this.controllers = controllers;

        // Here we make a mapping from the controller type to
        // the index in the controllers collection.
        this.mapping = controllerTypes
            .Select((type, index) => new { type, index })
            .ToDictionary(i => i.type, i => i.index);
    }

    public IController Create(RequestContext requestContext, 
        Type controllerType)
    {
        int index = this.mapping[controllerType];
        return controllers.ElementAt(index);
    }
}

Метод Create использует метод Enumerable.ElementAt для получения определенного (оформленного) контроллера из коллекции по его индексу. Коллекции, возвращаемые Simple Injector, реализуют IList<T>, и это позволяет ElementAt использовать индексатор IList<T>, что позволяет этой операции иметь характеристики производительности O(1). Производительность вызова ElementAt не ухудшится при увеличении количества контроллеров.

При этом вы можете написать свои декораторы следующим образом:

class UserActivityControllerDecorator : IController
{
    private readonly IController decoratee;
    private readonly ILogger logger;

    public UserActivityControllerDecorator(IController decoratee,
        ILogger logger)
    {
        this.decoratee = decoratee;
        this.logger = logger;        }

    public void Execute(RequestContext requestContext)
    {
        // do something before executing the controller
        this.decoratee.Execute(requestContext);
        // do something after executing the controller      
    }
}

Я надеюсь это имеет смысл.

person Steven    schedule 17.07.2013
comment
Спасибо. Я дам ему попробовать. Кажется, он не распознает РеализацияType.GetCustomAttribute - person Prithvi Ramana; 17.07.2013
comment
@PrithviRamana: В этом случае вы, вероятно, используете .NET 4.0. GetCustomAttribute‹TAttribute› — новая функция в .NET 4.5 (и одна из из небольших улучшений, добавленных Microsoft в этом выпуске). - person Steven; 17.07.2013
comment
Я добавил его как глобальный фильтр на данный момент и обработал все внутри атрибута UserActivityAttribute, поскольку мое приложение еще не может быть обновлено до 4.5. Спасибо. - person Prithvi Ramana; 19.07.2013
comment
.NET 4.0 содержит неуниверсальные методы GetCustomAttribute, поэтому отсутствие этого метода расширения не должно быть ограничивающим фактором. - person Steven; 19.07.2013