7

我有一个带有 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。

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

4

2 回答 2

5

@Xander 的回答总体上是正确的,但 Ninject 在 V3 中有一些非常具体的解决方案。Ninject 通过一种特定算法对构造函数进行评分,该算法是找到具有最多参数的构造函数,它知道如何解决,如本 wiki 文章中所述[声称适用于 V2.4,实际上标记为 3.0]。 见代码。我想这也在wiki上。如果不是,应该有人把它放在那里。

重新查看您所看到的行为变化,可能是隐式自绑定正在更改球门柱(在解析期间添加了新注册),或者您添加了一个使其他构造函数之一更具吸引力的绑定。

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

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

真正的底线是,您永远不应该像@Mark Seemann 对这个相关问题的评论中提到的那样陷入混乱。


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

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

于 2012-10-02T00:37:53.037 回答
4

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

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

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

[Inject]
public MyContext() { }
于 2012-10-01T23:19:25.683 回答