3

我需要创建一个自定义操作过滤器来实现IActionModelConventionIFilterFactory

IActionModelConvention用来同时设置多条路由,我IFilterFactory用来注入一些我需要使用的服务。

问题Apply()来自 的方法在来自的方法IActionModelConvention之前被调用,并且我需要注入的服务在.CreateInstance()IFilterFactoryApply()

我的问题是如何在Apply()调用方法之前注入服务?而且我也更喜欢使用IFilterFactory注入服务,因为它不会强迫我用[ServiceFilter]or属性包装实际[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" }
        });
    }
}
4

1 回答 1

3

您的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 容器中解析,因为它在管道中还为时过早。

于 2018-11-30T08:04:33.303 回答