Основной пользовательский фильтр Asp.net, реализующий IActionModelConvention и IFilterFactory - PullRequest
0 голосов
/ 28 ноября 2018

Мне нужно создать фильтр настраиваемых действий, который реализует как IActionModelConvention, так и IFilterFactory.

. Я использую IActionModelConvention для настройки нескольких маршрутов одновременно и IFilterFactory для вводанекоторые службы, которые мне нужно использовать.

Проблема заключается в том, что метод Apply() из IActionModelConvention вызывается перед методом CreateInstance() из IFilterFactory, и янужно, чтобы внедренные сервисы были доступны в Apply().

Мой вопрос : как мне внедрить сервисы до вызова метода Apply()?и я также предпочитаю использовать IFilterFactory для внедрения служб, потому что это не заставляет меня обернуть фактический атрибут атрибутами [ServiceFilter] или [TypeFilter].

Вот мой код:

public class Contains2RoutesAttribute : Attribute, IActionModelConvention, IFilterFactory
{
    public ISomeService SomeService{ get; set; }

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        ISomeService someService = serviceProvider.GetService<ISomeService>();
        return new Contains2RoutesAttribute() { SomeService = someService };
    }

    public void Apply(ActionModel action)
    {
        // Here I need to use the service injected:
        this.SomeService.DoSomething(); // ERROR: The service here is null.

        action.Selectors.Clear();

        // Adding route 1:
        action.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel { Template = "~/index1" }
        });

        // Adding route 2:
        action.Selectors.Add(new SelectorModel
        {
            AttributeRouteModel = new AttributeRouteModel { Template = "~/index2" }
        });
    }
}

1 Ответ

0 голосов
/ 30 ноября 2018

Ваша реализация IActionModelConvention будет запущена только один раз при запуске.Apply будет вызываться один раз для каждого действия.Чтобы использовать ISomeService внутри функции Apply, вы можете передать ее в качестве аргумента конструктора.Ваш класс Contains2RoutesAttribute не обязательно должен быть атрибутом или реализацией IFilterFactory, как вы подтвердили в комментариях, что он не участвует в конвейере фильтра .Вот пример кода, где я также переименовал класс, чтобы лучше представлять, что он делает (он больше не является атрибутом):

public class Contains2RoutesConvention : IActionModelConvention
{
    private readonly ISomeService someService;

    public Contains2RoutesConvention(ISomeService someService)
    {
        this.someService = someService;
    }

    public void Apply(ActionModel actionModel)
    {
        someService.DoSomething();

        ...
    }
}

Вы регистрируете это соглашение в Startup.ConfigureServices, используя что-то вроде этого:

services.AddMvc(options =>
{
    options.Conventions.Add(new Contains2RoutesConvention(new SomeService()));
});

Здесь все становится немного интереснее.Вы не можете использовать внедрение зависимостей с конвенцией , поэтому в этом примере я создал экземпляр SomeService inline при создании Contains2RoutesConvention.Если вы хотите, чтобы этот экземпляр был синглтоном, который может использоваться в другом месте вашего приложения, вы можете сделать что-то подобное в ConfigureServices:

var someService = new SomeService();

services.AddMvc(options =>
{
    options.Conventions.Add(new Contains2RoutesConvention(someService));
});

services.AddSingleton<ISomeService>(someService);

Конечно, это зависит от того, SomeServiceимеет свои собственные зависимости, но если это так, они не могут быть разрешены из контейнера DI, так как это слишком рано в конвейере.

...