Ioc / DI - Почему я должен ссылаться на все слои / сборки в точке входа приложения? - PullRequest
112 голосов
/ 29 февраля 2012

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

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

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

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

Ответы [ 4 ]

185 голосов
/ 29 февраля 2012

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

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

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

Deep Graph

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

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

Shallow Graph

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

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

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

Более подробную версию этого ответа можно найти в этом отрывке из моей книги Внедрение зависимостей, Принципы, Практика, Шаблоны .

63 голосов
/ 29 февраля 2012

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

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

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

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

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

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

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

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

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

Более подробное обсуждение того, как работает корень композиции, можно прочитать в главе 4 моей книги Внедрение зависимостей, принципы, практика, шаблоны .

5 голосов
/ 29 февраля 2012

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

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

Теперь для слоя пользовательского интерфейса не требуется 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 (в Виндзоре это должен быть установщик).

0 голосов
/ 15 марта 2013
  • Существует зависимость: если объект создает экземпляр другого объекта.
  • Нет зависимости: если объект ожидает абстракцию (внедрение в конструктор, внедрение метода ...)
  • Сборочные ссылки (ссылающиеся на dll, веб-сервисы ..) не зависят от концепции зависимостей, потому что для разрешения абстракции и возможности компилирования кода слой должен ссылаться на него.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...