Ioc/DI - Почему я должен ссылаться на все слои / сборки в приложении ввода?

(В связи с этим вопросом EF4: почему создание прокси должно быть включено, если включена отложенная загрузка?).

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

Если бы я не использовал контейнер DI, мне не пришлось бы ссылаться на библиотеку EntityFramework в моем приложении MVC3, только на мой бизнес-уровень, который ссылался бы на мой слой DAL/Repo.

Я знаю, что в конце дня все библиотеки DLL включены в папку bin, но моя проблема заключается в том, чтобы явно ссылаться на нее через "добавить ссылку" в VS, чтобы иметь возможность публиковать WAP со всеми необходимыми файлами.

4 ответа

Решение

Если бы я не использовал контейнер DI, мне не пришлось бы ссылаться на библиотеку EntityFramework в моем приложении MVC3, только на мой бизнес-уровень, который ссылался бы на мой уровень DAL/Repo.

Да, это именно та ситуация, которую я стараюсь избегать:)

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

Глубокий график

Поскольку граф зависимостей является глубоким, это означает, что большинство библиотек перемещаются по множеству других зависимостей - например, на диаграмме библиотека C перемещается вдоль библиотеки H, библиотеки E, библиотеки J, библиотеки M, библиотеки K и библиотеки N. Это затрудняет повторное использование каждой библиотеки независимо от остальных - например, в модульном тестировании.

Однако в слабосвязанном приложении, перемещая все ссылки на корень композиции, граф зависимостей сильно выравнивается:

Мелкий График

Как показано зеленым цветом, теперь можно повторно использовать библиотеку C, не перетаскивая ненужные зависимости.

Однако, несмотря на это, со многими DI-контейнерами вам не нужно добавлять жесткие ссылки на все необходимые библиотеки. Вместо этого вы можете использовать позднюю привязку в форме обычного сканирования на ассемблере (предпочтительно) или конфигурации XML.

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

Более сложную версию этого ответа можно найти в этом отрывке из Dependency Injection в.NET, второе издание.

Если бы я не использовал контейнер DI, мне не пришлось бы ссылаться на библиотеку EntityFramework в моем приложении MVC3

Даже при использовании DI-контейнера вы не должны позволять вашему проекту MVC3 ссылаться на EF, но вы (неявно) решаете сделать это путем реализации корня компоновки (пути запуска, где вы создаете свои графы объектов) внутри своего проекта MVC3. Если вы очень строго относитесь к защите своих архитектурных границ с помощью сборок, вы можете переместить корень композиции или презентацию (MVC) в библиотеку классов.

В первом варианте вы позволите вашему проекту MVC3 ссылаться на эту отдельную сборку "начальной загрузки", и она будет ссылаться на все другие сборки в вашем решении, а также на вашу библиотеку контейнеров DI. Проблема в том, что этот проект начальной загрузки не может ссылаться на типы, расположенные в проекте MVC3 (потому что это вызовет зависимость циклической сборки). Эти типы должны быть перемещены в проект загрузчика (который, возможно, должен ссылаться на System.Web.Mvc), или вам нужно сохранить небольшую часть конфигурации контейнера в приложении MVC3. Также обратите внимание, что ваш проект MVC по-прежнему ссылается на все другие сборки косвенно через новую сборку загрузчика, поскольку зависимости сборки являются переходными.

Хотя размещение корня композиции в отдельной сборке является допустимой вещью, большинство пуристов DI (включая меня) обычно перемещают корень композиции в библиотеку классов только при наличии нескольких конечных приложений (т. Е. Веб-приложение + веб-служба + служба Windows).), которые используют один и тот же бизнес-уровень. Когда у меня есть одно приложение, я сохраняю Composition Root внутри моего конечного приложения.

Второй вариант - переместить все связанные с MVC классы (представления, контроллеры и т. Д.) Из стартового проекта в библиотеку классов. Это позволяет этой новой сборке уровня представления оставаться отключенной от остальной части приложения. Сам проект вашего веб-приложения станет очень тонкой оболочкой с необходимой логикой запуска. Проект веб-приложения будет корнем компоновки, который ссылается на все остальные сборки.

Извлечение логики представления в библиотеку классов может усложнить работу с MVC. Будет все сложнее соединить, так как контроллеров и представлений, изображений, CSS-файлов и т. Д. Нет в стартовом проекте. Это, вероятно, выполнимо, но займет больше времени для настройки.

Оба варианта имеют свои недостатки, и поэтому я обычно советую оставить корень композиции в веб-проекте. Многие разработчики не хотят, чтобы их сборка MVC зависела от сборки DAL, но на самом деле это не проблема. Не забывайте, что сборки являются артефактом развертывания; Вы разбиваете код на несколько сборок, чтобы код мог быть развернут отдельно. С другой стороны, архитектурный слой - это логический артефакт. Очень хорошо (и часто) иметь несколько слоев в одной сборке.

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

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

Если бы я не использовал контейнер DI, мне не пришлось бы ссылаться на библиотеку EntityFramework в моем приложении MVC3, только на мой бизнес-уровень, который ссылался бы на мой уровень DAL/Repo.

Вы можете создать отдельный проект под названием "DependencyResolver". В этом проекте вы должны ссылаться на все ваши библиотеки.

Теперь для слоя UI не требуется NHibernate/EF или любая другая не относящаяся к интерфейсу библиотека, кроме Castle Windsor, на которую можно ссылаться.

Если вы хотите скрыть Castle Windsor и DependencyResolver от своего уровня пользовательского интерфейса, вы можете написать HttpModule, который вызывает содержимое реестра IoC.

У меня есть только пример для StructureMap:

public class DependencyRegistrarModule : IHttpModule
{
    private static bool _dependenciesRegistered;
    private static readonly object Lock = new object();

    public void Init(HttpApplication context)
    {
        context.BeginRequest += (sender, args) => EnsureDependenciesRegistered();
    }

    public void Dispose() { }

    private static void EnsureDependenciesRegistered()
    {
        if (!_dependenciesRegistered)
        {
            lock (Lock)
            {
                if (!_dependenciesRegistered)
                {
                    ObjectFactory.ResetDefaults();

                    // Register all you dependencies here
                    ObjectFactory.Initialize(x => x.AddRegistry(new DependencyRegistry()));

                    new InitiailizeDefaultFactories().Configure();
                    _dependenciesRegistered = true;
                }
            }
        }
    }
}

public class InitiailizeDefaultFactories
{
    public void Configure()
    {
        StructureMapControllerFactory.GetController = type => ObjectFactory.GetInstance(type);
          ...
    }
 }

DefaultControllerFactory не использует контейнер IoC напрямую, но делегирует методы контейнера IoC.

public class StructureMapControllerFactory : DefaultControllerFactory
{
    public static Func<Type, object> GetController = type =>
    {
        throw new  InvalidOperationException("The dependency callback for the StructureMapControllerFactory is not configured!");
    };

    protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
    {
        if (controllerType == null)
        {
            return base.GetControllerInstance(requestContext, controllerType);
        }
        return GetController(controllerType) as Controller;
    }
}

GetController делегат устанавливается в реестре StructureMap (в Виндзоре это должен быть установщик).

+ Существует зависимость: если объект создает экземпляр другого объекта. + Нет зависимости: если объект ожидает абстракцию (внедрение конструктора, внедрение метода...)

+ Ссылки на сборку (ссылающиеся на dll, webservices..) не зависят от концепции зависимостей, потому что для разрешения абстракции и возможности компилирования кода слой должен ссылаться на него.

Другие вопросы по тегам