Подтвердить что ты не робот

Что такое Ninject и когда вы его используете?

Я помогал нескольким друзьям в проекте, и есть класс, который использует Ninject. Я новичок в С#, и я понятия не имею, что делает этот класс, поэтому мне нужно понять Ninject. Может ли кто-нибудь объяснить, что такое Ninject, и когда он его использует (с примером, если это возможно)? Или, если вы можете указать на некоторые ссылки, которые тоже будут хороши.

Я пробовал этот вопрос: Ninject tutorials/documentsations?, но это не помогло начинающему, как я.

4b9b3361

Ответ 1

Ninject - это инжектор зависимостей для .NET, практическая реализация шаблона Injection Dependency (форма инверсии шаблона управления).

Предположим, что у вас есть два класса DbRepository и Controller:

class Controller {
   private DbRepository _repository;

   // ... some methods that uses _repository
}

class DbRepository {
   // ... some bussiness logic here ...
}

Итак, теперь у вас есть две проблемы:

  • Чтобы использовать его, вы должны инициализировать _repository. У вас есть такие способы:

    • В конструкторе вручную. Но что, если изменяется конструктор DbRepository? Вам нужно переписать класс Controller, потому что была изменена другая часть кода. Это не сложно, если у вас есть только один Controller, но если у вас есть пара классов, которые зависят от вашего Repository, у вас есть настоящая проблема.
    • Вы можете использовать сервисный локатор или factory или smth. Теперь у вас есть зависимость от вашего локатора сервисов. У вас есть глобальный сервисный локатор, и весь код должен его использовать. Как вы измените поведение локатора сервисов, когда вам нужно будет использовать в одной части логики активации кода один код и что-то другое в другой части кода? Существует только один способ - пропустить сервисный локатор через конструкторы. Но с большим количеством занятий вам нужно будет проходить все больше и больше. Во всяком случае, это хорошая идея, но это плохая идея.

      class Controller {
         private DbRepository _repository;
      
         public Controller() {
           _repository = GlobalServiceLocator.Get<DbRepository>()
         }
      
         // ... some methods that uses _repository
      }
      
    • Вы можете использовать инъекцию зависимостей. Посмотрите на код:

      class Controller {
         private IRepository _repository;
      
         public Controller(IRepository repository) {
            _repository = repository;
         }
      }
      

    Теперь, когда вам нужен ваш контроллер, вы пишете: ninjectDevKernel.Get<Controller>(); или ninjectTestKernel.Get<Controller>();. Вы можете переключаться между зависимыми преобразователями так быстро, как хотите. Видеть? Это просто, вам не нужно много писать.

  • Вы не можете выполнять на нем модульные тесты. Ваш Controller имеет зависимость от DbRepository, и если вы хотите протестировать какой-либо метод, который использует репозиторий, ваш код пойдет в базу данных и запросит у него данные. Это медленно, очень медленно. Если ваш код в DbRepository изменится, ваш unit test на Controller упадет. В этом случае только интеграционный тест должен сказать "проблемы". Что вам нужно в модульных тестах - изолировать ваши классы и протестировать только один класс в одном тесте (в идеале - только один метод). Если ваш код DbRepository завершился неудачно, вы подумаете, что код Controller завершился неудачно - и это плохо (даже если у вас есть тесты для DbRepository и Controller), они оба потерпят неудачу, и вы можете начать с неправильного места). Требуется много времени, чтобы определить, где ошибка. Вам нужно знать, что какой-то класс в порядке, а другой класс не прошел.

  • Если вы хотите заменить DbRepository на что-то другое во всех ваших классах, вам нужно много работать.

  • Вы не можете легко управлять временем жизни DbRepository. Объект этого класса создается при инициализации Controller и удаляется при удалении Controller. Между разными экземплярами класса Controller не существует совместного использования, и между другими классами не существует совместного использования. С помощью Ninject вы можете просто написать:

    kernel.Bind<IRepository>().To<DbRepository>().InSingletonScope();
    

И особая особенность инъекции зависимостей - гибкое развитие! Вы описываете, что ваш контроллер использует репозиторий с интерфейсом IRepository. Вам не нужно писать DbRepository, вы можете написать простой класс MemoryRepository и разработать Controller, в то время как другие ребята разрабатывают DbRepository. Когда DbRepository будет завершено, вы просто перепечатываете в своем преобразователе зависимостей значение по умолчанию IRepository DbRepository. Вы набрали много контроллеров? Все они будут использовать теперь DbRepository. Это здорово.

Подробнее:

Ответ 2

Ninject - это инверсия контейнера Control.

Что он делает?

Предположим, что у вас есть класс Car, который зависит от класса Driver.

public class Car 
{
   public Car(IDriver driver)
   {
      ///
   }
}

Чтобы использовать класс Car, вы создадите его так:

IDriver driver = new Driver();
var car = new Car(driver);

Удерживающий IoC централизует знания о том, как создавать классы. Это центральный репозиторий, который знает несколько вещей. Например, он знает, что конкретный класс, который вам нужно использовать для сборки автомобиля, - это Driver, а не любой другой IDriver.

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

// Syntax for binding
Bind<IDriver>().To<Driver>();

Это полезно, потому что позволяет создавать системы, которые легче тестировать на единицу. Предположим, что Driver инкапсулирует весь доступ к базе данных для Car. В unit test для автомобиля вы можете сделать это:

IDriver driver = new TestDriver(); // a fake driver that does not go to the db
var car = new Car(driver);

Существуют целые рамки, которые заботятся о том, чтобы автоматически создавать классы тестирования для вас, и они называются mocking frameworks.

Для получения дополнительной информации:

Ответ 3

Другие ответы велики, но я также хотел бы указать на это Внедрение инъекции зависимостей с использованием статьи Ninject.
Это одна из лучших статей, которые я когда-либо читал, что объясняет Dependency Injection и Ninject с очень элегантным примером.

Вот фрагмент статьи:

Ниже Интерфейс будет реализован нашими (SMSService) и (MockSMSService), в основном новый интерфейс (ISMSService) будет выставлять одинаковое поведение обеих служб в виде кода ниже:

public interface ISMSService
 {
 void SendSMS(string phoneNumber, string body);
 }

(SMSService) для реализации интерфейса (ISMSService):

public class SMSService : ISMSService
 {
 public void SendSMS(string mobileNumber, string body)
 {
 SendSMSUsingGateway(mobileNumber, body);
 }




private void SendSMSUsingGateway(string mobileNumber, string body)
 {
 /*implementation for sending SMS using gateway*/
Console.WriteLine("Sending SMS using gateway to mobile: 
    {0}. SMS body: {1}", mobileNumber, body);
 }
 }

(MockSMSService) с совершенно другой реализацией с использованием того же интерфейса:

public class MockSMSService :ISMSService
 {
 public void SendSMS(string phoneNumber, string body)
 {
 SaveSMSToFile(phoneNumber,body);
 }

private void SaveSMSToFile(string mobileNumber, string body)
 {
 /*implementation for saving SMS to a file*/
Console.WriteLine("Mocking SMS using file to mobile: 
    {0}. SMS body: {1}", mobileNumber, body);
 }
 }

нам нужно реализовать изменение для нашего конструктора класса (UIHandler), чтобы передать ему через него зависимость, используя этот код, который использует (UIHandler), может определить, какую конкретную реализацию (ISMSService) использовать:

public class UIHandler
 {
 private readonly ISMSService _SMSService;

public UIHandler(ISMSService SMSService)
 {
 _SMSService = SMSService;
 }
 public void SendConfirmationMsg(string mobileNumber) {

 _SMSService.SendSMS(mobileNumber, "Your order has been shipped successfully!");
 }
 }

Теперь нам нужно создать отдельный класс (NinjectBindings), который наследуется от (NinjectModule). Этот класс будет отвечать за разрешение зависимостей во время выполнения, а затем переопределить событие загрузки, которое используется для настройки привязки в нем. Хорошая вещь о Ninject заключается в том, что нам не нужно менять наш код в (ISMSService), (SMSService) и (MockSMSService).

public class NinjectBindings : Ninject.Modules.NinjectModule
 {
 public override void Load()
 {
 Bind<ISMSService>().To<MockSMSService>();
 }
 }

Теперь в коде формы пользовательского интерфейса, используйте ссылку для Ninject, которая определит, какую реализацию использовать:

class Program
 {
 static void Main(string[] args)
 {
 IKernel _Kernal = new StandardKernel();
 _Kernal.Load(Assembly.GetExecutingAssembly());
 ISMSService _SMSService = _Kernal.Get<ISMSService>();

UIHandler _UIHandler = new UIHandler(_SMSService);
 _UIHandler.SendConfirmationMsg("96279544480");

Console.ReadLine();
 }
 }

Теперь код использует Ninject Kernal для решения всех цепочек зависимостей, если мы хотим использовать реальный сервис (SMSService) в режиме Release (в производственной среде) вместо макетного, нам нужно изменить на Ninject (NinjectBindings) только для использования правильной реализации или с помощью директивы #if DEBUG, как показано ниже:

public class NinjectBindings : Ninject.Modules.NinjectModule
 {
 public override void Load()
 {
#if DEBUG
 Bind<ISMSService>().To<MockSMSService>();
#else
 Bind<ISMSService>().To<SMSService>();
#endif

}
 }

Теперь наш связывающий класс (NinjectBindings) живет в верхней части всего нашего кода выполнения, и мы можем легко управлять конфигурацией в одном месте.


Также см. Что такое инверсия управления? некоторые очень простые примеры упоминаются для понимания IoC.