在Ninject中注入接口数组

时间:2010-06-23 15:12:09

标签: c# .net arrays dependency-injection ninject

请考虑以下代码。

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)要求酒吧

如何在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 个答案:

答案 0 :(得分:9)

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

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..
    }
}

答案 1 :(得分:7)

这主要是对@bsnote的回答(我已经+ 1d)的重述,这可能有助于理解为什么它以这种方式运作。

Ninject(以及其他DI / addin框架)有两个不同的设施:

  1. 绑定到单个明确的服务实现(Get)的概念
  2. 允许用户获取一组服务的工具[然后以编程方式选择其中一种服务或以某种方式聚合](Ninject中的GetAll / ResolveAll
  3. 您的示例代码恰好使用与上面的2.相关联的语法。 (例如,在MEF中,通常使用[ImportMany]注释来明确这一点

    我需要查看示例(查看源代码 - 它非常简短,干净且易于理解),以找到解决方法。

    然而,正如@bsnote所说,重构您的需求的一种方法是将数组包装在容器中,或者拥有一个您要求它的对象(即工厂方法或存储库类型构造)

    你可以解释一下你的真实案例是什么 - 为什么有一个裸阵列?当然有一系列项目构建乞求被封装在所有这一点 - 这个问题肯定不会出现太多?

    编辑:扩展中有一组扫描示例,我想这会攻击你想要做的很多东西(在StructureMap这样的东西中,这种东西更加集成,显然有优点和缺点)。

    根据您是否尝试实现约定优于配置,您可能需要考虑在每种类型的插件上粘贴标记接口。然后你可以明确Bind每一个。或者,对于CoC,您可以在编辑中生成Module的{​​{1}}例程循环(即,许多单独的Load())。

    无论哪种方式,当您进行多次注册时,您都可以“请求”GetT[]并获得全套注册。如果你想明确地实现这一点(即,服务定位器和它所暗示的一切 - 就像你正在做的那样,你可以使用IEnumerable<T>来批量处理它们,这样你就不会像你那样隐含那种隐含的循环。做完了。

    不确定您是否已建立此连接,或者我是否遗漏了某些内容。无论哪种方式,我希望它教会你将一些代码粘贴到问题中,因为它说的是&gt; 1000字:P

答案 2 :(得分:4)

这对我来说也是一个问题。 Ninject注入数组的每个项而不是数组本身,因此您应该为数组项的类型定义映射。实际上,不可能将数组映射为具有当前版本的Ninject的类型。解决方案是在数组周围创建一个包装器。例如,如果它适合您,可以使用惰性类。或者你可以创建自己的包装器。

答案 3 :(得分: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) { ;}
   }