在Ninject中注入接口数组

15
考虑以下代码。
public interface IFoo { }


public class Bar
{
    public Bar(IFoo[] foos) { }
}


public class MyModule : NinjectModule
{
    public override void Load()
    {
        Bind<IFoo[]>().ToConstant(new IFoo[0]);
        // ToConstant() is just an example
    }
}


public class Program
{
    private static void Main(string[] args)
    {
        var kernel = new StandardKernel(new MyModule());
        var bar = kernel.Get<Bar>();
    }
}

当我尝试运行程序时,出现了以下异常。
错误激活IFoo 没有匹配的绑定可用,并且该类型不是自绑定的。 激活路径: 2)将依赖项IFoo注入到类型Bar的构造函数参数foos中 1)请求Bar 我如何在Ninject中注入/绑定数组?
谢谢你的时间。
编辑: 我的应用程序导入由第三方组件创建的数据。 导入过程应用不同种类的过滤器(例如不同过滤器接口的实现)。 过滤规则经常更改,但使用纯配置(和主过滤器)进行操作过于复杂。
我希望使添加/编辑过滤器最简单。 我拥有一个包含所有过滤器实现的程序集。 我尝试将每个过滤器接口绑定到以下方法(该方法提供该过滤器类型的每个实现的实例)。 基本上,我想避免在添加/删除过滤器类时需要更改我的Ninject模块。
    private IEnumerable<TInterface> GetInterfaceImplementations<TInterface>(IContext context)
    {
        return GetType().Assembly.GetTypes()
            .Where(t => typeof (TInterface).IsAssignableFrom(t) && IsConcreteClass(t))
            .Select(t => Kernel.Get(t)).Cast<TInterface>();
    }

我在绕过容器的 DI 机制方面感到有些内疚。这是一种不好的做法吗?有没有常见的做法来解决这些问题?

解决方案:
像 bsnote 建议的那样,我使用了一个包装类。

4个回答

11

Ninject支持多重注入,可以解决您的问题。https://github.com/ninject/ninject/wiki/Multi-injection


Ninject支持多重注入,可以解决您的问题。
public interface IFoo { }
public class FooA : IFoo {}
public class FooB : IFoo {}

public class Bar
{
    //array injected will contain [ FooA, FooB ] 
    public Bar(IFoo[] foos) { }
}

public class MyModule : NinjectModule
{
    public override void Load()
    {
        Bind<IFoo>().To<FooA>();
        Bind<IFoo>().To<FooB>();
        //etc..
    }
}

7
这基本上是对@bsnote答案的重申(我+1了),这可能有助于理解为什么以这种方式工作。
Ninject(以及其他DI / addin框架)有两个不同的设施:
1.绑定到服务的单个明确实现的概念(Get) 2.允许获取一组服务[然后以某种方式编程选择其中一个或聚合]的设施(Ninject中的GetAll / ResolveAll)
您的示例代码恰好使用与第2项相关的语法。 (例如,在MEF中,通常使用[ImportMany]注释来明确此点)
我需要查看示例(查看源 - 它非常简短,干净且易于跟踪)以找到解决方法。
但是,正如@bsnote所说,重构您的要求的一种方法是将数组包装在容器中,或者具有要求它的对象(即,工厂方法或存储库类型结构)
您还可以解释一下您的真实情况 - 为什么会有一个裸数组?当然,所有这些都需要封装在底层的项目集合中 - 这个问题肯定不经常出现?
编辑:扩展中有一组扫描示例,我想这些示例可以攻击您正在尝试执行的大部分操作(在StructureMap等内容中,这种操作更加集成,这显然有利有弊)。
根据您是否尝试实现约定优于配置,您可能需要考虑在每种插件类型上放置一个标记接口。然后,您可以明确地绑定每个插件。或者,对于CoC,您可以使Module的Load()例程循环遍历您生成的实现集合(即,您的编辑中的许多单独的Get)。
无论哪种方式,当您有多个注册时,都可以愉快地“请求”T []或IEnumerable 并获取完整集合。如果您想明确实现此操作(即服务定位器及其所涵盖的所有内容 - 就像您正在执行的操作一样),则可以使用GetAll批处理它们,以便不执行隐含在您执行的方式中的循环。
不确定您是否已建立此连接或我是否遗漏了某些内容。无论如何,我希望它教会了您将一些代码放入问题中,因为它可以表达> 1000个字:P

我选择这个作为答案,因为它更详细。也感谢bsnote的帮助!(+1) - Julian Lettner

4

这对我来说也是个问题。Ninject注入数组的每个项,而不是数组本身,因此您应该为数组项类型定义映射。实际上,使用当前版本的Ninject无法将数组映射为类型。解决方案是创建一个数组的包装器。例如,可以使用Lazy类。或者您可以创建自己的包装器。


0

由于Array实现了IReadOnlyList,因此以下代码可以正常工作。

   // Binding
   public sealed class FooModule: NinjectModule 
   {
     public opverride void Load() 
     {
        Bind<IReadOnlyList<IFoo>>().ToConstant(new IFoo[0]);
      }
   }

   // Injection target
   public class InjectedClass {
      public InjectedClass(IReadOnlyList<IFoo> foos) { ;}
   }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接