如何使用 Ninject

发布于 2024-12-27 01:21:40 字数 112 浏览 2 评论 0原文

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

I have been trying to use Ninject today and have a couple of questions. First of all do I need to use the Inject attribute on all constructors that I want to use injection for. This seems like a really lame design? Do I need to create a Kernel then use that everywhere I pass in an injected class?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

征棹 2025-01-03 01:21:40

开始使用 Ninject 的最佳方法是从小事做起。寻找新的

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

假设您有一个这样的类,用于在 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.]

我提取了一个 IDocumentCreator 接口,而不是传入具体 WordDocumentCreator使用 CreateNewDocument 方法。现在我可以传入实现该接口的任何类,而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,我不必传入 real WordDocumentCreator (或我正在使用的任何东西) 。我可以传递一个。这样我就可以编写一个测试,假设我的 IDocumentCreator 工作正常,并且只测试 NoteCreator 本身的移动部分。我的假 IDocumentCreator 除了返回正确的响应之外什么也不做,我的测试将确保 NoteCreator 做正确的事情。

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

另请参阅 Michael Feathers 撰写的有效使用旧代码。他谈到了上面的测试方面:如何打破接口并传入伪造品以隔离行为并对其进行测试。

The best way to get started with Ninject is to start small. Look for a 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. (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.). My class, NoteCreator, depends on WordDocumentCreator to perform its work.

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.

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. 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. Now I can pass in any class that implements that interface, and all NoteCreator has to do is call the method it knows about.

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. 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. 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 (e.g. Ninject).

The other thing I need to do is configure 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. The process Ninject goes through looks something like this:

  • Create the application's MainWindow. Its constructor requires a NoteCreator.
  • OK, so create a NoteCreator. But its constructor requires an IDocumentCreator.
  • My configuration says that for an IDocumentCreator, I should use WordDocumentCreator. So create a WordDocumentCreator.
  • Now I can pass the WordDocumentCreator to the NoteCreator.
  • And now I can pass that NoteCreator to the 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.

Second, if management later mandates the user of a superior word processor, all you have to do is

  • Write a WordPerfectDocumentCreator that implements IDocumentCreator.
  • Change MyAppModule above, binding IDocumentCreator to WordPerfectDocumentCreator instead.

Third, if I want to test my NoteCreator, I don't have to pass in a real WordDocumentCreator (or whatever I'm using). 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. My fake IDocumentCreator will do nothing but return the correct response, and my test will make sure that NoteCreator does the right thing.

For more information about how to structure your applications this way, have a look at Mark Seemann's recent book, Dependency Injection in .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.

Also have a look at Working Effectively With Legacy Code, by 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.

负佳期 2025-01-03 01:21:40

首先,我是否需要在所有构造函数上使用 Inject 属性
我想用注射来实现。这看起来真的很蹩脚
设计?

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

我是否需要创建一个内核,然后在我传入的任何地方使用它
注入类?

不 - 您所描述的听起来更像 服务定位器模式,而不是依赖项注入 - 您需要传递依赖项理想情况下,在构造函数中,而不是使用内核在特定类中解析它们。应该只有一个完成解析的中央组合根,它位于上面提到的 RegisterServices 方法中的组合根内,或者在那里实例化一个单独的 Ninject 模块 - 后一种方法将允许您在改变解决依赖关系的方式方面具有更大的灵活性和模块化性(没有双关语)。

这是一个很好的关于使用 Ninject 和 MVC3 进行依赖注入的初学者教程

First of all do I need to use the Inject attribute on all constructors
that I want to use injection for. 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. This will get you started with a NinjectMVC3 class in the App_Start folder. You can use the RegisterServices method to register your interfaces/classes with Ninject. All controllers that have dependencies to those interfaces will then be automatically resolved by Ninject, there is no need for the Inject attribute.

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.

Here's a good beginner's tutorial on dependency injection with Ninject and MVC3.

蓝天白云 2025-01-03 01:21:40

不要忘记有文档,包括一个介绍,考虑到您提出的问题,我认为这是非常合适的 在 Ninject Wiki 上。如果您在没有从头到尾阅读 Ninject 的情况下尝试使用 Ninject,那么您只是在烦恼自己。

目录放在书签栏上一段时间。

我还强烈推荐 Mark SeemannDependency Injection in .Net 作为基于 DI 的架构的姊妹书(尽管它不直接涵盖 Ninject)。

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. You're just annoying yourself if you're trying to use Ninject without reading it end to end.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文