【问题标题】:Extending ActionDescriptorFilterProvider to allow dependency injection of class level filters扩展 ActionDescriptorFilterProvider 以允许类级别过滤器的依赖注入
【发布时间】:2017-05-29 19:25:31
【问题描述】:

跟进Authorization Filter Dependency Injection with ASP.New MVC 4 Web Api。有没有办法在所有控制器类上全局设置的过滤器上使用依赖注入:

config.Filters.Add(new WebApplicationApiAuthorizeAttribute());  

看起来ActionDescriptorFilterProvider 中的GetFilters 方法仅适用于方法级别的过滤器。

public class UnityWebApiFilterAttributeFilterProvider : ActionDescriptorFilterProvider,
    System.Web.Http.Filters.IFilterProvider
{
private readonly IUnityContainer _container;

public UnityWebApiFilterAttributeFilterProvider(IUnityContainer container)
{
    _container = container;
}

public IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, 
    HttpActionDescriptor actionDescriptor)
{
    var filters = base.GetFilters(configuration, actionDescriptor);

    this.BuildUpAttributes(filters);

    return filters;
}

private void BuildUpAttributes(IEnumerable filterInfo)
{
    foreach (FilterInfo filter in filterInfo)
    {
        object o = _container.BuildUp(filter.GetType(), filter);
    }
}
}

【问题讨论】:

  • 好问题。我打算回答它,之前已经多次触及这个话题,但我仍然觉得我没有一个优雅的解决方案。现在,请查看Injecting dependencies into ASP.NET MVC 3 action filters. What's wrong with this approach?,希望Mark Seemann 会发现这个问题。
  • 所以@RowanFreeman,根据您的评论,我不清楚是否必须使用 ServiceLocator 来注入类级别的依赖。是否有其他方法或者我是否坚持使用您链接到的文章中提到的反模式?
  • 就其价值而言,ServiceLocator 是我在项目中使用的。我不知道将依赖项注入全局过滤器的任何其他优雅方式。 大声思考: 我想知道您是否可以创建一个新的过滤器接口(例如IInjectableActionFilter),然后使用工厂来创建它们。工厂实际上将由 IoC 容器处理,就像他们通常处理控制器工厂一样。然后容器将负责创建过滤器、应用它并注入依赖项。

标签: asp.net-mvc asp.net-web-api dependency-injection unity-container


【解决方案1】:

如果您希望注入这些全局过滤器,则必须从容器中解析它们并将它们添加到过滤器集合中:

GlobalFilters.Filters.Add(container.Resolve<MyFilter>());

或者做类似的事情:

var filter = WebApplicationApiAuthorizeAttribute();
container.BuildUp(filter.Gettype(), filter);
GlobalFilters.Filters.Add(filter);

但是关于使用全局过滤器的一个重要警告。全局过滤器是……全局的。或者用 IoC 的术语来说:它们是singleton。这意味着它的所有依赖项也将有效地变成单例,这可能会导致各种并发错误,因为它们预计不会在应用程序的持续时间内存在。

因此,只有当过滤器的所有直接和间接依赖项都是单例时,才应该这样做,如果你能做到这一点,那就太好了,但通常情况并非如此。因此,另一种选择是创建一个允许即时解析真实实例的代理:

public sealed class UnityActionFilterProxy<TActionFilter> : IActionFilter
    where TActionFilter : IActionFilter
{
    private readonly IUnityContainer container;
    public UnityActionFilterProxy(IUnityContainer container) {
        this.container = container;
    }

    public Task<HttpResponseMessage> ExecuteActionFilterAsync(HttpActionContext context,
        CancellationToken token, Func<Task<HttpResponseMessage>> continuation) {
        return this.container.Resolve<TActionFilter>().ExecuteActionFilterAsync(
            context, token, continuation);
    }

    public bool AllowMultiple { get { return false; } }
}

这个代理可以作为单例注入到全局过滤器集合中,如下所示:

GlobalFilters.Filters.Add(
    container.Resolve<UnityActionFilterProxy<MyFilter>>());

全局过滤器并不是 Web API 中唯一一个设计有点……难闻的地方。看看 this related question 关于 DelegatingHandlers。

【讨论】:

  • 你知道有什么好的文章描述了我在使用全局过滤器时可能遇到的并发问题类型吗?
  • @AdamGreene:看看this Q/A。这是关于DbContext 的生命周期。如果您将DbContext 注入到您的全局过滤器引用的服务中,那么您就完蛋了。
猜你喜欢
  • 2016-06-22
  • 2014-06-05
  • 2018-12-18
  • 2022-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多