Я пытаюсь использовать Ninject сегодня и задать пару вопросов. Прежде всего, мне нужно использовать атрибут Inject для всех конструкторов, для которых я хочу использовать инъекцию. Это похоже на действительно хромой дизайн? Нужно ли мне создавать ядро, а затем использовать это везде, где я сдаю вложенный класс?
Как использовать Ninject
Ответ 1
Прежде всего, мне нужно использовать атрибут Inject для всех конструкторов что я хочу использовать для инъекций. Это кажется очень хромым дизайн?
Нет, вам не нужно вообще этого делать. Поскольку вы работаете с ASP.NET MVC, вы можете просто установить пакет Ninject.MVC3 Nuget. Это позволит вам начать с класса NinjectMVC3
в папке App_Start. Вы можете использовать метод RegisterServices
для регистрации ваших интерфейсов/классов с помощью Ninject. Все контроллеры, которые имеют зависимости от этих интерфейсов, затем будут автоматически разрешены Ninject, нет необходимости в атрибуте Inject.
Нужно ли мне создавать Ядро, а затем использовать это везде, где я введенный класс?
Нет - то, что вы описываете, больше похоже на шаблон локатора службы, а не на инъекцию зависимостей - вы должны в идеале передать свои зависимости в конструкторе вместо разрешая их в определенных классах с использованием ядра. Должен быть только один центральный корневой состав, где выполняется разрешение, которое находится в корне композиции по методу RegisterServices
, упомянутому выше, или отдельному модулю Ninject, созданному там - более поздний подход позволит вам немного больше гибкости и модульности ( каламбур не предназначен) при изменении способа разрешения зависимостей.
Здесь хороший учебник для начинающих по инъекции зависимостей с Ninject и MVC3.
Ответ 2
Лучший способ начать работу с 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
. Когда вы начнете извлекать зависимости, вы обнаружите, что они имеют тенденцию "пузыриться" к корню вашего приложения, и это единственное место, где вы должны иметь ссылку на ваш контейнер 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
, мне не нужно передавать реальный WordDocumentCreator
(или то, что я использую). Я могу пройти поддельную. Таким образом, я могу написать тест, предполагающий, что мой IDocumentCreator
работает правильно и проверяет только движущиеся части в NoteCreator
. Мой подделка IDocumentCreator
ничего не даст, но вернет правильный ответ, и мой тест будет убедиться, что NoteCreator
делает правильные вещи.
Для получения дополнительной информации о том, как структурировать ваши приложения таким образом, взгляните на недавнюю книгу Марка Seemann, Injection Dependency в .NET. К сожалению, он не охватывает Ninject, но он охватывает целый ряд других фреймворков DI, и он рассказывает о том, как структурировать ваше приложение так, как я описал выше.
Также смотрите Эффективно работая с устаревшим кодом, Майкл Перс. Он говорит об испытательной стороне вышеперечисленного: как разрывать интерфейсы и пропускать подделки с целью изолировать поведение и тестировать его.
Ответ 3
Не забывайте, что есть документы, включая ввод, который я считаю очень подходящим, учитывая те вопросы, которые вы задаете в Ninject Wiki. Вы просто раздражаете себя, если пытаетесь использовать Ninject, не читая его до конца.
Немного поместите оглавление на панели закладок.
Я также могу рекомендовать Mark Seemann Инъекция зависимостей в .Net в качестве сопутствующей книги для архитектуры на основе DI (хотя она не распространяется непосредственно на Ninject).