【问题标题】:Ninject: Choosing the wrong constructorNinject:选择错误的构造函数
【发布时间】:2012-10-01 23:13:21
【问题描述】:

我有一个带有 Ninject v2.2.1.4 的 ASP.NET MVC 3 应用程序。一切都很好,然后突然我们开始看到 Ninject 尝试使用带有参数的构造函数而不是无参数构造函数来创建我们的 DbContext。以下是绑定:

kernel.Bind<MyContext>().ToSelf().InRequestScope();

kernel.Bind<IUnitOfWork>().ToMethod(ctx => ctx.Kernel.Get<MyContext>());
kernel.Bind<DbContext>().ToMethod(ctx => ctx.Kernel.Get<MyContext>());

MyContext 是一个 DbContext 对象,它也实现了 IUnitOfWork 接口。我以这种方式进行了设置,因此将相同的上下文注入到单个请求中使用的多个存储库中。 MyContext 构造函数如下所示:

public MyContext() { }
public MyContext(string connectionString) { }
public MyContext (long accountID) { }
public MyContext (Connection connection) { }

不同的应用程序有不同的构造函数,因为它们都使用相同的 MyContext 类。查看绑定时,您会认为当请求 MyContext 类时会调用无参数构造函数,但无论出于何种原因,它都不是。即使没有指定 accountID,也会调用具有长 accountID 参数的那个。这显然会抛出异常声明“没有匹配的绑定可用,并且类型不可自绑定”它实际上在尝试生成 IUnitOfWork 时抛出了异常。

如果我注释掉最后三个构造函数,一切正常,并使用无参数构造函数。如果我注释掉任何两个参数化构造函数,它会尝试使用另一个而不是无参数的构造函数。

Ninject 提供的建议是:

Suggestions:
  1) Ensure that you have defined a binding for long.
  2) If the binding was defined in a module, ensure that the module has been loaded into the kernel.
  3) Ensure you have not accidentally created more than one kernel.
  4) If you are using constructor arguments, ensure that the parameter name matches the constructors parameter name.
  5) If you are using automatic module loading, ensure the search path and filters are correct.

我们不想要 1 的任何东西。我不确定 2 和 5 是什么意思。我不相信我们已经完成了 3 并且我们没有完成 4。

关于为什么在这种情况下不使用无参数构造函数的任何想法。

【问题讨论】:

标签: asp.net-mvc-3 ninject


【解决方案1】:

@Xander 的回答总体上是正确的,但 Ninject 在 V3 中有一些非常具体的解决方案。 Ninject 通过一种特定算法对构造函数进行评分,该算法是找到带有the most parameters it knows how to resolve as documented in this wiki article 的构造函数[声称适用于 V2.4,实际上标记为 3.0]。 See the code。我想这也在wiki上。如果不是,应该有人把它放在那里。

RE 你看到的行为变化,可能是隐式自绑定正在改变球门柱(在解析过程中添加了新注册),或者你添加了一个绑定,使其他构造函数之一更具吸引力.

[Inject] 属性胜过您所追求的所有其他标准(尽管您实际上并不希望在代码中包含特定于容器的属性)。

建议的WithConstructorArgument 技术实际上是通过使用ToConstructor 来实现的 - 进行 WCA 不会影响选择(而且我认为您不会收到有关冗余规范的投诉。

真正的底线是,你永远不应该像@Mark Seemann 对this related question 的评论中提到的那样陷入混乱。


可悲的是,以上都是谎言。如果您离开 v2.2,这个答案将变得正确。如果你不能或不会,你需要查看等效的源代码和测试以找出之前的规则(从内存(以及我研究中搜索结果中出现的一些谷歌代码),它是基于构造函数计数,但不确定如何消除相等的分数。

可以肯定的是,在 2.2 中,添加 [Inject] 是快速的出路。

【讨论】:

    【解决方案2】:

    默认情况下,Ninject 以及其他类似的 IoC 框架会选择参数最多的构造函数。通过WithConstructorArgument扩展方法指定初始化时使用哪个构造函数。

    kernel.Bind<DbContext>()
          .WithConstructorArgument("connectionString",
                 ConfigurationManager.ConnectionStrings["connection"]
                      .ConnectionString)
          .ToMethod(ctx => ctx.Kernel.Get<MyContext>());
    

    要强制 Ninject 使用 默认构造函数,请将 [Inject] 属性放在构造函数上:

    [Inject]
    public MyContext() { }
    

    【讨论】:

    • 但是我想使用没有参数的那个,从我对 Ninject 的研究来看,我认为它会选择它可以满足的最复杂的构造函数。就我而言,我没有指定任何值,因此它应该选择无参数的值。
    • 是否使用 [Inject] 属性将始终使用该属性,无论我是否指定 WithConstructorArgument?
    • 试试看,我没有答案,我用StructureMap。不过,我认为.WithConstructorArgument 优先...
    • 请参阅stackoverflow.com/a/2523243/11635 了解为什么WithConstructorArgument 的一般原则不适用于Ninject; (执行摘要是[Inject] trumps,WCA 对选择没有影响)
    猜你喜欢
    • 1970-01-01
    • 2014-02-14
    • 2014-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-29
    • 1970-01-01
    相关资源
    最近更新 更多