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

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

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

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

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

Ответ 1

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

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

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

Deep Graph

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

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

Shallow Graph

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

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

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

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

Ответ 2

Если бы я не использовал 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 с процессом сборки, он может предупредить вас, когда кто-то проверит код, нарушающий такое архитектурное правило.

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

Ответ 4

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

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

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

Если вы хотите скрыть замок 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 (в Windsor он должен быть установщиком).

Ответ 5

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