Ninject - 绑定类型列表

Ninject - bind a List of types

我正在使用 Ninject。我想要做的是映射一个 List 类型,然后将它注入到我的 class:

的构造函数中
private readonly IList<IDispatchFilter> m_Filters;

public DispatchFilteringManager(IList<IDispatchFilter> filters)
{
    m_Filters = filters;
}

我试过这个绑定:

Bind<IList<IDispatchFilter>>()
    .ToMethod(bindDecoyDispatchFilters)
    .InSingletonScope();

private IList<IDispatchFilter> bindDecoyDispatchFilters(IContext context)
{
    Bind<IDispatchFilter>().To<WindowsXpFilter>();

    IList<IDispatchFilter> result = context.Kernel.GetAll<IDispatchFilter>().ToList();

    return result;
}

但是在我的构造函数中我得到一个空的 List

我找不到解决这个简单任务的方法。

变化:

Bind<IList<IDispatchFilter>>()
    .ToMethod(bindDecoyDispatchFilters)
    .InSingletonScope();

private IList<IDispatchFilter> bindDecoyDispatchFilters(IContext context)
{
    Bind<IDispatchFilter>().To<WindowsXpFilter>();
    ...
}

收件人:

Bind<IDispatchFilter>().To<WindowsXpFilter>();
Bind<IList<IDispatchFilter>>()
    .ToMethod(bindDecoyDispatchFilters)
    .InSingletonScope();

private IList<IDispatchFilter> bindDecoyDispatchFilters(IContext context)
{
    ...
}

解释:

我们在 .ToMethod 绑定中使用的方法让我们说 T 将仅在我们调用 container.Get<T> 时执行,而不是在此之前执行。

当 Ninject 尝试在 bindDecoyDispatchFilters 方法中解析 IList<IDispatchFilter>> 时,它会查找 IDispatchFilter 之前注册的所有绑定 并找到 none。因此 ctor 参数被解析为一个空集合。

不幸的是,您在构造函数中没有收到任何项目的原因是 Ninject 的 Multi Injection works。 Ninject 对 IList<T> 的解析似乎非直观地寻找所有(独立)注册的 <T> 并将它们注入到你的 class 中 IList<T>,而不是实际上使用明确注册的方法来解析 IList<T>.

因此,bindDecoyDispatchFilters(受 .ToMethod(bindDecoyDispatchFilters) 约束)将永远不会被调用,因为 Ninject 将根据注册的类型解析 IList<T> T。 (这很容易测试 - 在方法中放置一个断点或 Assert.Fail() - 它从未被调用过)。

所以如果

Bind<IDispatchFilter>().To<WindowsXpFilter>();

IList中唯一需要解决的IDispatchFilter,那么你可以放弃注册,按照@Fabjan的,直接注册Bind<IDispatchFilter>().To<WindowsXpFilter>();。多重注入会将其解析为传递给构造函数的 IList<T> 中的单个元素。

然后您可以完全删除 IList<T> 绑定:

Bind<IList<IDispatchFilter>>() ... remove 
    .ToMethod(bindDecoyDispatchFilters)
    .InSingletonScope();

并完全放弃 bindDecoyDispatchFilters 方法。

但是,如果过滤器列表在引导后发生变化,并且您确实需要一个动态工厂方法来 return 向您的构造函数提供可用的过滤器,那么您可以求助于

或者,如果您没有很多 class 依赖于 IList<>,您也可以显式注册每个 class,这再次优先于多重注入,所以引导代码变成:

kernel.Bind<ResolveMe>()
     .ToSelf()
     .WithConstructorArgument<IEnumerable<IDispatchFilter>>(bindDecoyDispatchFilters);

private IList<IDispatchFilter> bindDecoyDispatchFilters(IContext context)
{
    // Contract.Assert(1 == 0); // .. .ensure the method is called during resolution!
    context.Kernel.Bind<IDispatchFilter>().To<WindowsXpFilter>();
    return context.Kernel.GetAll<IDispatchFilter>().ToList();
}

我用来测试的 classes 是:

public interface IDispatchFilter {}

public class WindowsXpFilter : IDispatchFilter { }

public class ResolveMe
{
    public IEnumerable<IDispatchFilter> Stuff { get; set; }

    public ResolveMe(IEnumerable<IDispatchFilter> stuff) { Stuff = stuff; }
}

还有一些测试:

  var y = kernel.Get<ResolveMe>();
  Assert.IsTrue(y.Stuff.Any());