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

Где найти модули Ninject в многоуровневом приложении

Мое приложение включает в себя ряд серверных сборок (включая уровень репозитория данных Entity Framework), которые совместно используются рядом интерфейсных сборок (включая службу Windows и веб-приложение MVC3).

Мое понимание процесса привязки Ninject заключается в том, что каждая сборка, содержащая типы инъекций, также должна содержать модуль Ninject, который определяет привязки по умолчанию для этих типов. Затем набор определенных модулей будет загружен в ядро ​​Ninject узлов потребления.

Однако я столкнулся с проблемами, поскольку требуемая область привязки не всегда согласована. Например, моему MVC-проекту необходимо привязать к контексту данных InRequestScope, тогда как служба Windows привязывается к тому же классу InThreadScope.

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

Есть ли наилучшая практика в отношении того, где модули должны располагаться в многоуровневом приложении, и как я могу смириться с этой необходимостью в привязке различий между проектами?

Большое спасибо за ваши предложения,

Тим

4b9b3361

Ответ 1

Для решения с одним приложением общим советом является регистрация вашего контейнера в проекте приложения (ваше веб-приложение или проект веб-службы). Для веб-приложения это обычно будет Global.asax Application_Start. Это место, где вы соединяете все вместе, называется Корень композиции в терминологии DI.

При использовании решения с несколькими приложениями у вас все равно будет один корень композиции для каждого приложения. Это должно быть, поскольку каждое приложение имеет свою уникальную конфигурацию. С другой стороны, дублированный код всегда плох. Вы не хотите менять три места, когда вводите новую абстракцию.

Хитрость заключается в перемещении всех регистраций вниз по иерархии проектов. Например, вы можете определить одну "сборку начальной сборки", которая зависит от ваших сборок бизнес-уровня (и ниже), и пусть у нее будут все регистрации для тех сборок, которые не изменяются. Корни композиций приложений затем могут использовать эту сборку для получения регистраций по умолчанию и расширения ее с зависимыми от приложения зависимостями.

Такая вещь может выглядеть так:

// MVC Composition root
public static void Bootstrap()
{
    var container = new Container();

    // Default registrations
    BusinessLayerBootstrapper.Bootstrap(container);

    // Application specific registrations
    container.Bind<IUserContext>().To<AspNetUserContext>();

    DependencyResolver.Current = 
        new ContainerDependencyResolver(container);
}

// Windows Service Composition root
public static void Bootstrap()
{
    var container = new Container();

    // Default registrations
    BusinessLayerBootstrapper.Bootstrap(container);

    // Application specific registrations
    container.Bind<IUserContext>().To<SystemUserContext>()
        .SingleScoped();

    // Store somewhere.
    Bootstrapper.Container = container;
}

// In the BL bootstrap assembly
public static class BusinessLayerBootstrapper
{
    public static void Bootstrap(Container container)
    {
        container.Bind<IDepenency>().To<RealThing>();
        // etc
    }
}

Хотя вам не нужно иметь отдельную сборку bootstrapper (вы можете поместить этот код в самом BL), это позволит вам не создавать сборки вашего бизнес-уровня без каких-либо зависимостей с вашим контейнером.

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

Ответ 2

Что касается области видимости, приложение MVC должно иметь другую CompositionRoot, чем службу Windows. Я предлагаю вам попытаться организовать как можно больше функциональными модулями (для тех частей, которые являются прикладными агностиками), и всеми другими связями непосредственно в CompositionRoot проекта MVC или WindowsService.

Другим очень хорошим подходом является определение общего набора соглашений, который поможет вам выразить самые актуальные проблемы в нескольких строках. Поэтому ваши приложения могут иметь следующие привязки:

MVC App

Bind(c => c.FromAssemblyContaining<IRepository>()
           .SelectAllClasses()
           .InheritedFrom<IRepository>()
           .Configure(b => b.InRequestScope()));

Приложение для Windows Service

Bind(c => c.FromAssemblyContaining<IRepository>()
           .SelectAllClasses()
           .InheritedFrom<IRepository>()
           .Configure(b => b.InThreadScope()));

В моей точке зрения в сочетании с ориентированной на объекты структурирование подход к соглашению является самым чистым.