Ссылка на Ninject в библиотеке классов в ASP.NET Приложение MVC 3

#asp.net-mvc #ninject

#asp.net-mvc #ninject

Вопрос:

У меня есть ASP.NET Приложение MVC 3, которое использует расширение Ninject.MVC3 для настройки DI в моем приложении MVC. А именно NinjectMVC3.cs , в App_Start папке, где определены мои привязки, есть файл. Это хорошо работает для DI в контроллерах моего приложения.

В дополнение к ASP.NET Веб-приложение MVC, мое решение, также содержит библиотеку классов projects, назовем ее MyProject.Core projects , в которой есть моя модель домена, сервисы и так далее. Там у меня есть вызываемый класс UserServices , который использует вызываемую службу EmailServices , например:

 public class UserServices : IUserServices
{        
    private readonly IEmailServices _emailServices = new EmailServices();

    public void CreateUser(...)
    {
        // Create user...

        _emailServices.SendWelcomeEmail(newUser);
    }
}
  

Как вы можете видеть, UserServices класс имеет жестко запрограммированную зависимость от EmailServices , но я бы хотел, чтобы он также был настроен на использование DI. А именно, в моем ASP.NET Приложение MVC (или проект модульного тестирования, или где угодно еще) Я хочу иметь возможность сказать «Привязать IEmailServices к использованию TestEmailServices » и UserServices использовать класс TestEmailServices вместо EmailServices .

Как мне это сделать? Я хотел бы иметь возможность делать что-то вроде:

 public class UserServices : IUserServices
{        
    private readonly IEmailServices _emailServices = kernel.Get<EmailServices>();

    ...
}
  

Но я не уверен kernel , откуда это произойдет в данном случае. Имеет ли смысл то, что я спрашиваю, или я лаю здесь не то дерево?

Спасибо

Ответ №1:

Вы должны быть в состоянии сделать это с помощью внедрения конструктора, например:

 private readonly IEmailServices _emailServices;

public UserServices(IEmailServices emailServices)
{
    _emailServices = emailServices;
}
  

Внедрение службы в ваши контроллеры должно обрабатываться автоматически фабрикой пользовательских контроллеров Ninject, которая будет использовать настроенный контейнер IoC для разрешения IUserServices объекта при создании контроллера, которому, в свою очередь, будет предоставлен IEmailServices объект контейнером:

 public class MyController : Controller
{
    private readonly IUserServices _userServices;

    public MyController(IUserServices userServices)
    {
        _userServices = userServices;
    }
}
  

При модульном тестировании вы можете вручную внедрить поддельную или фиктивную службу электронной почты в пользовательскую службу.

Комментарии:

1. Да, это ответ. Не используйте ядро Ninject в качестве локатора служб. Не связывайте свои реализации библиотеки с Ninject. Просто позвольте ему построить все дерево объектов в основном приложении.

2. Ах, я должен был просто попробовать это! <хлопает по лбу /> 🙂 Спасибо

Ответ №2:

Если вы все равно используете MVC 3, как насчет регистрации Ninject с помощью встроенного DependencyResolver?

 System.Web.Mvc.DependencyResolver.SetResolver(yourKernel);
  

Затем вы можете просто использовать

 var svc = System.Web.Mvc.DependencyResolver.Current.GetService<bla>();
  

там, где вам это нужно. Я не знаю, принимает ли SetResolver ядро Ninject напрямую или вам нужен класс-оболочка, но я бы счел это самым чистым решением.

Комментарии:

1. Это будет работать, но оно по-прежнему зависит от антишаблона Service Locator и совершенно не нужно в MVC, где вы можете просто зарегистрировать Ninject как фабрику контроллеров и использовать обычную инъекцию конструктора везде. Не говоря уже о том, что он связывает библиотеку классов с System.Web.Mvc , что кажется неправильным…

2. @Aaronaught Да и нет. DependencyResolver в MVC 3 обрабатывает все (поэтому больше нет необходимости в фабрике контроллеров, он использует DI автоматически, если DependencyResolver может его разрешить). Однако могут быть ситуации, когда просто невозможно внедрить все зависимости (например, у меня есть HtmlHelper, который использует службу, которая обрабатывает некоторые вещи форматирования — это МОЖНО сделать в контроллере и поместить в модель, но это было бы излишним для службы исключительно для функций отображения)

3. DependencyResolver это буквально локатор служб с новым именем. Я действительно не вижу никаких преимуществ в его использовании над самим ядром … это просто еще один уровень косвенности? Я не уверен, что в этом есть законная необходимость, помимо того, что есть во внутренних компонентах MVC, но я полагаю, что там могут быть какие-то неясные примеры.

4. @Aaronaught Это действительно просто еще один уровень. Если у вас есть ядро, отлично. Особенность DR в том, что он работает независимо от того, какой DI (поэтому, если вы хотите использовать Unity, AutoFac или что-то еще, что является прозрачным), и что он дает вам глобальный / статический. Но да, это буквально Service Locator. Для внутренних компонентов MVC он заменяет различные механизмы (например, ранее существовала фабрика контроллеров, механизм просмотра, modelbinder. Теперь сначала он будет запрашивать DR для всего)