Как использовать Ninject - PullRequest
18 голосов
/ 13 января 2012

Сегодня я пытался использовать Ninject и у меня есть пара вопросов.Прежде всего мне нужно использовать атрибут Inject для всех конструкторов, для которых я хочу использовать инъекцию.Это похоже на действительно отстойный дизайн?Нужно ли создавать ядро ​​и использовать его везде, где я передаю внедренный класс?

Ответы [ 3 ]

58 голосов
/ 13 января 2012

Лучший способ начать работу с 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.]

Вместо ввода бетона WordDocumentCreator, я извлек интерфейс IDocumentCreator с помощью CreateNewDocument метод.Теперь я могу передать любой класс, который реализует этот интерфейс, и все, что NoteCreator должен сделать, это вызвать метод, о котором он знает.

Теперь сложная часть ,Теперь у меня должна быть ошибка компиляции в моем приложении, потому что где-то я создавал NoteCreator с конструктором без параметров, которого больше не существует .Теперь мне нужно вытащить эту зависимость .Другими словами, я выполняю тот же процесс, что и выше , но теперь я применяю его к классу, который создает новый NoteCreator.Когда вы начнете извлекать зависимости, вы обнаружите, что они имеют тенденцию «пузыриться» до корня вашего приложения, которое является only местом, где у вас должна быть ссылка на ваш контейнер 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 (например) не может быть разрешен.

Во-вторых, если позднее управление предписывает пользователю превосходный текстовый процессор, все, что вам нужно сделать, это

  • Напишите WordPerfectDocumentCreator, который реализует IDocumentCreator.
  • Измените MyAppModule выше, связывая IDocumentCreator с WordPerfectDocumentCreator вместо.

В-третьих, если я хочу проверить свой NoteCreator, мне не нужно передавать real WordDocumentCreator (или что-то еще, что я использую).Я могу передать в поддельные один.Таким образом, я могу написать тест, который предполагает, что my IDocumentCreator работает правильно, и проверяет только движущиеся части в самом NoteCreator.Моя фальшивка IDocumentCreator ничего не сделает, только вернет правильный ответ, и мой тест убедится, что NoteCreator делает правильные вещи.

Для получения дополнительной информации о том, как структурировать ваши приложения таким образом, взгляните на недавнюю книгу Марка Симанна, Внедрение зависимостей в .NET . К сожалению, он не охватывает Ninject, но охватывает ряд других структур DI и говорит о том, как структурировать ваше приложение так, как я описал выше.

Также взгляните на Эффективно работающий с устаревшим кодом , автор Michael Feathers. Он говорит о стороне тестирования, описанной выше: как разбивать интерфейсы и передавать фальшивку с целью изоляции поведения и проверки его.

22 голосов
/ 13 января 2012

Прежде всего мне нужно использовать атрибут Inject для всех конструкторов, для которых я хочу использовать инъекцию.Это похоже на действительно неубедительный дизайн?

Нет, вам вообще не нужно было этого делать.Поскольку вы работаете с ASP.NET MVC, вы можете просто установить пакет Ninject.MVC3 Nuget.Это поможет вам начать работу с классом NinjectMVC3 в папке App_Start.Вы можете использовать метод RegisterServices для регистрации ваших интерфейсов / классов в Ninject.Все контроллеры, имеющие зависимости от этих интерфейсов, будут автоматически разрешены Ninject, атрибут Inject не требуется.

Нужно ли создавать ядро, а затем использовать его везде, где я передаю инъецированныйкласс?

Нет - то, что вы описываете, звучит больше похоже на шаблон Service Locator , а не на внедрение зависимостей - вы захотите передать ваши зависимости в идеале вместо конструктораразрешая их в определенных классах, используя ядро.Должен быть только один центральный корень композиции, в котором выполняется разрешение, который находится в корне композиции в методе RegisterServices, упомянутом выше, или в отдельном модуле Ninject, созданном там - более поздний подход позволит вам немного больше гибкости и модульности (не нужно каламбур) изменить способ разрешения ваших зависимостей.

Вот хорошее руководство для начинающих по внедрению зависимостей с помощью Ninject и MVC3 .

7 голосов
/ 15 января 2012

Не забывайте, что есть документы, в том числе вступление, которое, я считаю, было бы очень уместным, учитывая тип вопросов, которые вы задаете в Ninject Wiki .Вы просто раздражаете себя, если пытаетесь использовать Ninject, не читая его из конца в конец.

Добавьте немного оглавление на панель закладок.

Я также настоятельно рекомендую Mark Seemann Inpendency Injection в .Net в качестве сопроводительного пособия для архитектуры на основе DI (даже если она не охватывает непосредственно Ninject).

...