简体   繁体   English

如何使用Ninject

[英]How to Use Ninject

I have been trying to use Ninject today and have a couple of questions. 我今天一直试图使用Ninject,并有几个问题。 First of all do I need to use the Inject attribute on all constructors that I want to use injection for. 首先,我需要在我想要使用注入的所有构造函数上使用Inject属性。 This seems like a really lame design? 这看起来像是一个非常蹩脚的设计? Do I need to create a Kernel then use that everywhere I pass in an injected class? 我是否需要创建一个内核然后在注入的类中传递的所有内容?

The best way to get started with Ninject is to start small. 开始使用Ninject的最佳方法是从小处着手。 Look for a new . 寻找new

Somewhere in the middle of your application, you're creating a class inside another class. 在应用程序中间的某个地方,您正在另一个类中创建一个类。 That means you're creating a dependency . 这意味着你正在创建一个依赖项 Dependency Injection is about passing in those dependencies, usually through the constructor, instead of embedding them. 依赖注入是指通常通过构造函数传递这些依赖项,而不是嵌入它们。

Say you have a class like this, used to automatically create a specific type of note in Word. 假设您有一个这样的类,用于在Word中自动创建特定类型的注释。 (This is similar to a project I've done at work recently.) (这类似于我最近在工作中完成的一个项目。)

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

WordDocumentCreator is a class that handles the specifics of creating a new document in Microsoft Word (create an instance of Word, etc.). WordDocumentCreator是一个类,用于处理在Microsoft Word中创建新文档的细节(创建Word的实例等)。 My class, NoteCreator , depends on WordDocumentCreator to perform its work. 我的类NoteCreator 依赖WordDocumentCreator来执行其工作。

The trouble is, if someday we decide to move to a superior word processor, I have to go find all the places where WordDocumentCreator is instantiated and change them to instantiate WordPerfectDocumentCreator instead. 问题是,如果有一天我们决定转移到一个优秀的文字处理器,我必须找到实例化WordDocumentCreator所有地方并改变它们来实例化WordPerfectDocumentCreator

Now imagine that I change my class to look like this: 现在想象我改变我的类看起来像这样:

class NoteCreator
{
    WordDocumentCreator docCreator;

    public NoteCreator(WordDocumentCreator docCreator)  // constructor injection
    {
        this.docCreator = docCreator;
    }

    public NoteHost Create()
    {
        docCreator.CreateNewDocument();
        [etc.]

My code hasn't changed that much; 我的代码没有那么大改变; all I've done within the Create method is remove the line with the new . 我在Create方法中所做的就是删除new But now I'm injecting my dependency. 但现在我正在注入我的依赖。 Let's make one more small change: 让我们做一个小小的改变:

class NoteCreator
{
    IDocumentCreator docCreator;

    public NoteCreator(IDocumentCreator docCreator)  // change to interface
    {
        this.docCreator = docCreator;
    }

    public NoteHost Create()
    {
        docCreator.CreateNewDocument();
        [etc.]

Instead of passing in a concrete WordDocumentCreator , I've extracted an IDocumentCreator interface with a CreateNewDocument method. 我没有传入具体的 WordDocumentCreator ,而是使用CreateNewDocument方法提取了一个IDocumentCreator 接口 Now I can pass in any class that implements that interface, and all NoteCreator has to do is call the method it knows about. 现在我可以传入任何实现该接口的类,而NoteCreator所要做的就是调用它知道的方法。

Now the tricky part . 现在是棘手的部分 I should now have a compile error in my app, because somewhere I was creating NoteCreator with a parameterless constructor that no longer exists . 我现在应该在我的应用程序中出现编译错误,因为我正在创建一个不再存在的无参数构造函数的NoteCreator Now I need to pull out that dependency as well. 现在我也需要提取这种依赖性。 In other words, I go through the same process as above , but now I'm applying it to the class that creates a new NoteCreator . 换句话说,我经历了与上面相同的过程 ,但现在我将它应用于创建新NoteCreator的类。 When you start extracting dependencies, you'll find that they tend to "bubble up" to the root of your application, which is the only place where you should have a reference to your DI container (eg Ninject). 当您开始提取依赖项时,您会发现它们会“冒泡”到应用程序的根目录,这是您应该引用DI容器的唯一位置(例如Ninject)。

The other thing I need to do is configure Ninject . 我需要做的另一件事是配置Ninject The essential piece is a class that looks like this: 必不可少的是一个看起来像这样的类:

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

This tells Ninject that when I attempt to create a class that, somewhere down the line, requires an IDocumentCreator , it should create a WordDocumentCreator and use that. 这告诉Ninject当我尝试创建一个类,在某个地方,需要一个IDocumentCreator ,它应该创建一个WordDocumentCreator并使用它。 The process Ninject goes through looks something like this: Ninject经历的过程看起来像这样:

  • Create the application's MainWindow . 创建应用程序的MainWindow Its constructor requires a NoteCreator . 它的构造函数需要一个NoteCreator
  • OK, so create a NoteCreator. 好的,所以创建一个NoteCreator。 But its constructor requires an IDocumentCreator . 它的构造函数需要一个IDocumentCreator
  • My configuration says that for an IDocumentCreator , I should use WordDocumentCreator . 我的配置说,对于IDocumentCreator ,我应该使用WordDocumentCreator So create a WordDocumentCreator . 因此,创建一个WordDocumentCreator
  • Now I can pass the WordDocumentCreator to the NoteCreator. 现在我可以将WordDocumentCreator传递给NoteCreator。
  • And now I can pass that NoteCreator to the MainWindow . 现在我可以将NoteCreator传递给MainWindow

The beauty of this system is threefold. 这个系统的美丽有三个方面。

First, if you fail to configure something, you'll know right away, because your objects are created as soon as your application is run. 首先,如果您未能配置某些内容,您将立即知道,因为您的对象是在应用程序运行后立即创建的。 Ninject will give you a helpful error message saying that your IDocumentCreator (for instance) can't be resolved. Ninject将为您提供一条有用的错误消息,指出您的IDocumentCreator (例如)无法解析。

Second, if management later mandates the user of a superior word processor, all you have to do is 其次,如果管理层后来要求用户使用优质文字处理器,那么您所要做的就是

  • Write a WordPerfectDocumentCreator that implements IDocumentCreator . 编写一个实现IDocumentCreatorWordPerfectDocumentCreator
  • Change MyAppModule above, binding IDocumentCreator to WordPerfectDocumentCreator instead. 更改上面的MyAppModule ,将IDocumentCreator绑定到WordPerfectDocumentCreator

Third, if I want to test my NoteCreator , I don't have to pass in a real WordDocumentCreator (or whatever I'm using). 第三,如果我想测试我的NoteCreator ,我不必传入真正的 WordDocumentCreator (或者我正在使用的任何东西)。 I can pass in a fake one. 我可以传递一个假的 That way I can write a test that assumes my IDocumentCreator works correctly, and only tests the moving parts in NoteCreator itself. 这样我就可以编写一个假设我的IDocumentCreator正常工作的测试,并且只测试NoteCreator本身的移动部分。 My fake IDocumentCreator will do nothing but return the correct response, and my test will make sure that NoteCreator does the right thing. 我的假IDocumentCreator只会返回正确的响应,我的测试将确保NoteCreator做正确的事情。

For more information about how to structure your applications this way, have a look at Mark Seemann's recent book, Dependency Injection in .NET . 有关如何以这种方式构建应用程序的更多信息,请查看Mark Seemann最近出版的书“ .NET中的依赖注入” Unfortunately, it doesn't cover Ninject, but it does cover a number of other DI frameworks, and it talks about how to structure your application in the way I've described above. 不幸的是,它没有涵盖Ninject,但它确实涵盖了许多其他DI框架,它讨论了如何按照我上面描述的方式构建应用程序。

Also have a look at Working Effectively With Legacy Code , by Michael Feathers. 另请参阅 Michael Feathers的“有效使用遗留代码” He talks about the testing side of the above: how to break out interfaces and pass in fakes for the purpose of isolating behavior and getting it under test. 他谈到了上面的测试方面:如何打破接口并传递假货,以隔离行为并使其受到测试。

First of all do I need to use the Inject attribute on all constructors that I want to use injection for. 首先,我需要在我想要使用注入的所有构造函数上使用Inject属性。 This seems like a really lame design? 这看起来像是一个非常蹩脚的设计?

No you shouldn't have to do this at all actually. 不,你根本不应该这样做。 Since you work with ASP.NET MVC you can just install the Ninject.MVC3 Nuget package. 由于您使用ASP.NET MVC,您只需安装Ninject.MVC3 Nuget包。 This will get you started with a NinjectMVC3 class in the App_Start folder. 这将使您开始使用App_Start文件夹中的NinjectMVC3类。 You can use the RegisterServices method to register your interfaces/classes with Ninject. 您可以使用RegisterServices方法向Ninject注册接口/类。 All controllers that have dependencies to those interfaces will then be automatically resolved by Ninject, there is no need for the Inject attribute. 然后,所有与这些接口具有依赖关系的控制器将由Ninject自动解析,不需要Inject属性。

Do I need to create a Kernel then use that everywhere I pass in an injected class? 我是否需要创建一个内核然后在注入的类中传递的所有内容?

No - what you are describing sounds more like the Service Locator pattern , not dependency injection - you will want to pass in your dependencies ideally in the constructor, instead of resolving them within particular classes using the kernel. 不 - 你所描述的内容听起来更像是服务定位器模式 ,而不是依赖注入 - 你需要在构造函数中理想地传递依赖关系,而不是使用内核在特定类中解析它们。 There should be just one central composition root where the resolving is done, which is within the composition root in either the RegisterServices method mentioned above or a separate Ninject module instantiated there - the later approach will allow you a little more flexibility and modularity (no pun intended) in changing how you resolve your dependencies. 应该只有一个中心组合根,其中解析完成,这是在上面提到的RegisterServices方法的组合根目录或在那里实例化的单独的Ninject模块 - 后面的方法将允许你更多的灵活性和模块性(没有双关语)意在改变你如何解决你的依赖关系。

Here's a good beginner's tutorial on dependency injection with Ninject and MVC3 . 这是一个很好的初学者关于Ninject和MVC3依赖注入的教程

Don't forget there are docs, including an intro I feel would be very appropriate given the sort of questions you are asking on the Ninject Wiki . 不要忘记有文档,包括一个介绍我觉得非常合适,因为你在Ninject Wiki上提出了一些问题。 You're just annoying yourself if you're trying to use Ninject without reading it end to end. 如果您试图使用Ninject而不是端到端地阅读它,那么您只会烦恼自己。

Stick the table of contents on your bookmark bar for a bit. 目录粘贴在书签栏上一会儿。

I can also highly recommend Mark Seemann 's Dependency Injection in .Net as a companion book for DI based architecture (even though it doesnt directly cover Ninject). 我还强烈推荐Mark Seemann 在.Net中依赖注入作为基于DI的体系结构的配套书(尽管它没有直接涵盖Ninject)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM