Как получить доступ к Ninject.Kernel без использования шаблона Locator

Я прочитал десятки сообщений по этой теме, не найдя четких указаний о том, как получить доступ к Ninject.Kernel без использования шаблона Locator службы.

В настоящее время у меня есть следующие классы, которые должны использовать CustomerBusiness (это моя служба), и он отлично работает, но я хорошо знаю, что это не рекомендуемый способ сделать это.

private CustomerBusiness _customerBusiness;

private ICustomerRepository CustomerRepository
{
    get { return NinjectWebCommon.Kernel.Get<IAccountRepository>(); }
}

private CustomerBusiness CustomerBusiness
{
    get
    {
        if (_customerBusiness == null)
        {
            _customerBusiness = new CustomerBusiness(AccountRepository);
        }

        return _customerBusiness;
    }
}

public Customer GetCustomer(int id)
{
    return CustomerBusiness.GetCustomer(id);
}

Это свойство ядра, доступное в коде выше:

public static IKernel Kernel
{
    get
    {
        return CreateKernel();
    }
}

Я читал много предложений об использовании factory для этого, но ни один из них не объясняет, как использовать этот factory. Я был бы очень признателен, если бы кто-нибудь мог показать мне "CustomerFactory" или любой другой рекомендуемый подход , включая, как использовать.

Обновление

Я использую веб-формы ASP.NET и должен получить доступ к CustomerBusiness из CodeBehind.

Решение

Последнее решение, которое я нашел для работы, было ответом с большинством голосов, найденных на этом посту: Как я могу реализовать Ninject или DI на веб-формах asp.net?

Похоже, что это (Наследование наследования из PageBase, которое является частью Ninject.Web - это ключ!):

public partial class Edit : PageBase
{
    [Inject]
    public ICustomerBusiness CustomerBusiness { get; set; }
    ...

Принятый ответ ниже косвенно приводит меня к поиску этого решения.

Ответ 1

Поскольку вы используете NinjectWebCommon, я предполагаю, что у вас есть какое-то веб-приложение. Вам действительно нужно получить доступ к ядру Ninject в одном месте - в корневой каталог. Это место, где вы строите графический объект и единственное место, где вам когда-либо понадобится доступ к контейнеру IoC. Чтобы получить нужные зависимости, вы обычно используете впрыск конструктора.

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

Чтобы расширить вашу конкретную ситуацию, ваш класс примет ICustomerBusiness в своем конструкторе, объявив, что ему нужен экземпляр ICustomerBusiness как его зависимость:

class CustomerBusinessConsumer : ICustomerBusinessConsumer
{
    private readonly ICustomerBusiness customerBusiness;

    public CustomerBusinessConsumer(ICustomerBusiness customerBusiness)
    {
        this.customerBusiness = customerBusiness;
    }
    ...
}

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

Затем вам нужно только убедиться, что ваши классы получают свои зависимости, и это этот корневой состав, где вы это делаете. То, что именно является составным корнем, зависит от типа приложения, которое вы пишете (консольное приложение, приложение WPF, веб-сервис, веб-приложение MVC...)


ИЗМЕНИТЬ. Чтобы ознакомиться с ситуацией в области ASP.NET WebForms Мне пришлось искать детали, потому что я никогда не использовал его. К сожалению, WebForms требует, чтобы у вас был произвольный конструктор на каждом из ваших классов страниц, поэтому вы не можете использовать инсталляцию конструктора полностью из верхней части графика объекта вниз.

Однако, после консультации с Mark Seeman о создании объектов в WebForms, я могу перефразировать, как бороться с этой неэффективностью системы, но все еще действуя в линии с хорошими практиками DI:

  • Имейте класс, ответственный за разрешение зависимостей, используя установленное вами ядро ​​Ninject. Это может быть очень тонкая оболочка вокруг ядра. Позвольте называть его DependencyContainer.

  • Создайте свой контейнер и сохраните его в контексте приложения, чтобы он был готов, когда вам это нужно

    protected void Application_Start(object sender, EventArgs e)
    {
       this.Application["container"] = new DependencyContainer();
    }
    
  • Предположим, что ваш класс страницы (пусть называется HomePage) имеет зависимость от ICustomerBusinessConsumer. Тогда DependencyContainer должно позволить нам получить экземпляр ICustomerBusinessConsumer:

    public ICustomerBusinessConsumer ResolveCustomerBusinessConsumer()
    {
        return Kernel.Get<ICustomerBusinessConsumer>();
    }
    
  • Чем в самом классе MainPage вы разрешите его зависимости в конструкторе по умолчанию:

    public MainPage()
    {
        var container = (DependencyContainer) HttpContext.Current.Application["container"];
        this.customerBusinessConsumer = container.ResolveCustomerBusinessConsumer();
    }
    

Несколько заметок:

  • наличие контейнера зависимостей, доступного в HttpContext, не должно заманчиво рассматривать его как локатор службы. Фактически, лучшая практика здесь (по крайней мере, с точки зрения истины DI) заключается в том, чтобы иметь какие-то "классы-разработчики", к которым вы будете передавать функциональность своих классов страниц.

    Например, каждое действие, обработанное MainPage, будет передано только его классу-разработчику. Этот класс-реализатор будет зависеть от MainPage и, как и все другие зависимости, будет разрешен с использованием контейнера.

    Важная роль заключается в том, что эти классы-разработчики должны быть в сборках, не ссылающихся на сборки ASP.NET и, таким образом, без возможности доступа к HttpContext

  • чтобы написать столько кода для достижения этого, конечно, не идеально, но это только следствие ограничения рамки. Например, в приложениях ASP.NET MVC это рассматривается намного лучше. Там у вас есть одна точка, где вы можете составлять графы объектов, и вам не нужно разрешать их в каждом классе верхнего уровня, как это необходимо в WebForms.

  • Хорошо, что, хотя вам нужно написать некоторый сантехнический код в конструкторах класса страниц, оттуда вниз по графу объектов вы можете использовать конструкторскую инъекцию

Ответ 2

Инъектор конструктора является предпочтительным методом для DI с ninject, однако он также поддерживает инъекцию свойств. Прочитайте страницу ninject вокруг рисунков инъекций здесь https://github.com/ninject/ninject/wiki/Injection-Patterns.

Оба из них - это шаблоны инъекций, в отличие от местоположения службы, на основе запроса, а не на самом деле инъекции.

Двойная инъекция - это то, что вам нужно контролировать конструкцию. При использовании MVC вся проводка для этого встроена в пакет ninject MVC на nuget