如何使用Ninject

本文关键字:Ninject 何使用 | 更新日期: 2023-09-27 18:20:10

我今天一直在尝试使用Ninject,有几个问题。首先,我需要对所有要使用注入的构造函数使用Inject属性。这似乎是一个非常蹩脚的设计?我需要创建一个内核,然后在注入的类中的任何地方使用它吗?

如何使用Ninject

开始使用Ninject的最佳方法是从小处着手。查找new

在应用程序中间的某个位置,您正在另一个类中创建一个类。这意味着您正在创建一个依赖项。依赖项注入是关于传递这些依赖项,通常通过构造函数,而不是嵌入它们。

假设你有一个这样的类,用于在Word中自动创建特定类型的注释。(这类似于我最近在工作中做的一个项目。)

class NoteCreator
{
    public NoteHost Create()
    {
        var docCreator = new WordDocumentCreator();
        docCreator.CreateNewDocument();
        [etc.]

WordDocumentCreator是一个类,用于处理在Microsoft Word中创建新文档的细节(创建Word实例等)。我的类NoteCreator依赖于WordDocumentCreator来执行其工作。

问题是,如果有一天我们决定改用高级文字处理器,我必须找到所有实例化WordDocumentCreator的地方,并将它们改为实例化WordPerfectDocumentCreator

现在想象一下,我把我的课改成这样:

class NoteCreator
{
    WordDocumentCreator docCreator;
    public NoteCreator(WordDocumentCreator docCreator)  // constructor injection
    {
        this.docCreator = docCreator;
    }
    public NoteHost Create()
    {
        docCreator.CreateNewDocument();
        [etc.]

我的代码没有改变那么多;我在Create方法中所做的就是用new删除行。但现在我正在注入我的依赖。让我们再做一个小小的改变:

class NoteCreator
{
    IDocumentCreator docCreator;
    public NoteCreator(IDocumentCreator docCreator)  // change to interface
    {
        this.docCreator = docCreator;
    }
    public NoteHost Create()
    {
        docCreator.CreateNewDocument();
        [etc.]

我使用CreateNewDocument方法提取了一个IDocumentCreator接口,而不是传入混凝土WordDocumentCreator。现在,我可以传入任何实现该接口的类,而NoteCreator所要做的就是调用它所知道的方法。

现在是棘手的部分。现在我的应用程序中应该有一个编译错误,因为我在某个地方创建了一个NoteCreator,它使用的无参数构造函数已不存在。现在,我还需要拉出这个依赖项。换句话说,我经历了与上面相同的过程,但现在我将其应用于创建新NoteCreator的类。当你开始提取依赖项时,你会发现它们往往会"冒泡"到你的应用程序的根,这是唯一的地方,你应该在那里引用你的DI容器(例如Ninject)。

我需要做的另一件事是配置Ninject。关键部分是一个看起来像这样的类:

class MyAppModule : NinjectModule
{
    public override void Load()
    {
        Bind<IDocumentCreator>()
            .To<WordDocumentCreator>();

这篇文章告诉Ninject,当我试图创建一个需要IDocumentCreator的类时,它应该创建一个WordDocumentCreator并使用它。Ninject所经历的过程看起来像这样:

  • 创建应用程序的MainWindow。它的构造函数需要一个NoteCreator
  • 好的,创建一个NoteCreator。但是构造函数需要IDocumentCreator
  • 我的配置说,对于IDocumentCreator,我应该使用WordDocumentCreator。因此,创建一个WordDocumentCreator
  • 现在我可以将WordDocumentCreator传递给NoteCreator
  • 现在我可以把NoteCreator传递给MainWindow

这个系统的美妙之处有三个。

首先,如果您未能配置某些内容,您将立即知道,因为您的对象是在应用程序运行后立即创建的。Ninject会给您一条有用的错误消息,说明您的IDocumentCreator(例如)无法解决。

其次,如果管理层后来要求用户使用高级文字处理器,那么你所要做的就是

  • 编写一个实现IDocumentCreatorWordPerfectDocumentCreator
  • 更改上面的MyAppModule,将IDocumentCreator绑定到WordPerfectDocumentCreator

第三,如果我想测试我的NoteCreator,我不必通过真正的WordDocumentCreator(或我正在使用的任何东西)。我可以通过一个。这样,我就可以编写一个测试,假设我的IDocumentCreator工作正常,并且只测试NoteCreator本身中的移动部件。我的假IDocumentCreator只会返回正确的响应,我的测试将确保NoteCreator做正确的事情。

有关如何以这种方式构建应用程序的更多信息,请参阅Mark Seemann的新书《.NET中的依赖注入》。不幸的是,它没有涵盖Ninject,但它涵盖了许多其他DI框架,并谈到了如何以我上面描述的方式构建应用程序。

还可以看看Michael Feathers的《有效使用遗留代码》。他谈到了上面的测试方面:如何破解接口并传入伪造的内容,以隔离行为并对其进行测试。

首先,我需要在所有构造函数上使用Inject属性吗我想注射的。这看起来真的很差劲设计

不,实际上你根本不应该这么做。由于您使用ASP.NET MVC,您只需安装Ninject.MVC3 Nuget包即可。这将使您从App_Start文件夹中的NinjectMVC3类开始。您可以使用RegisterServices方法向Ninject注册接口/类。所有依赖这些接口的控制器都将由Ninject自动解析,不需要Inject属性。

我需要创建一个内核吗?然后在注入类?

不——你所描述的听起来更像是服务定位器模式,而不是依赖项注入——你会希望在构造函数中传递你的依赖项,而不是使用内核在特定的类中解析它们。应该只有一个中心的组合根来完成解析,它在上面提到的RegisterServices方法的组合根中,或者在那里实例化的一个单独的Ninject模块中——后面的方法将允许您在更改解析依赖关系的方式时有更多的灵活性和模块性(并非双关语)。

这里有一个很好的关于Ninject和MVC3依赖注入的初学者教程。

别忘了有一些文档,考虑到你在Ninject Wiki上提出的问题,包括一个我认为非常合适的介绍。如果你试图在不从头到尾阅读的情况下使用Ninject,你只是在惹恼自己。

把目录贴在书签栏上一会儿。

我还可以强烈推荐Mark Seemann的《.Net中的依赖注入》作为基于DI的体系结构的配套书籍(尽管它没有直接涵盖Ninject)。