Ioc / DI - 为什么我必须在应用程序的入口点引用所有层/组件?

问题描述 投票:111回答:4

(与此问题相关,EF4: Why does proxy creation have to be enabled when lazy loading is enabled?)。

我是DI的新手,所以请耐心等待。我知道容器负责实例化我所有已注册的类型,但为了做到这一点,它需要引用我的解决方案中的所有DLL及其引用。

如果我没有使用DI容器,我就不必在我的MVC3应用程序中引用EntityFramework库,只需引用我的业务层,它将引用我的DAL / Repo层。

我知道在一天结束时所有的DLL都包含在bin文件夹中,但我的问题是必须通过VS中的“添加引用”显式引用它,以便能够发布包含所有必需文件的WAP。

dependency-injection inversion-of-control castle-windsor
4个回答
185
投票

如果我没有使用DI容器,我不必在我的MVC3应用程序中引用EntityFramework库,只需要引用我的DAL / Repo层的业务层。

是的,这正是DI很难避免的情况:)

使用紧密耦合的代码,每个库可能只有一些引用,但这些引用还有其他引用,创建了依赖关系的深层图,如下所示:

因为依赖图是深的,这意味着大多数库拖动了许多其他依赖 - 例如在图中,库C沿着库H,库E,库J,库M,库K和库N拖动。这使得更难以独立于其余库重用每个库 - 例如in unit testing

但是,在松散耦合的应用程序中,通过将所有引用移动到Composition Root,依赖关系图被严格展平:

如绿色所示,现在可以重用库C而不拖动任何不需要的依赖项。

但是,所有这些,使用许多DI容器,您不必添加对所有必需库的硬引用。相反,您可以使用基于约定的程序集扫描(首选)或XML配置的形式进行后期绑定。

但是,当您这样做时,必须记住将程序集复制到应用程序的bin文件夹,因为它不再自动发生。就个人而言,我很少发现值得付出额外的努力。

这个答案的更精细版本可以在我的书this excerptDependency Injection, Principles, Practices, Patterns中找到。


63
投票

如果我没有使用DI容器,我就不必在我的MVC3应用程序中引用EntityFramework库

即使使用DI容器,您也不必让MVC3项目引用EF,但您(隐式)选择通过在MVC3项目中实现Composition Root(组成对象图的启动路径)来实现此目的。如果您非常严格地使用程序集来保护架构边界,则可以将Composition Root或您的(MVC)演示文稿移动到类库中。

在第一个选项中,您让MVC3项目引用这个单独的“引导程序”程序集,它将引用解决方案中的所有其他程序集,并引用您的DI容器库。这个问题是这个bootstrapper项目不能引用位于MVC3项目中的类型(因为它会导致循环程序集依赖)。必须将这些类型移动到引导程序项目(可能需要引用System.Web.Mvc),或者您需要在MVC3应用程序中保留容器配置的一小部分。另请注意,您的MVC项目仍然通过新的引导程序集程序间接引用所有其他程序集,因为程序集依赖项是可传递的。

虽然将Composition Root放在一个单独的程序集中是有效的,但是当有多个终端应用程序(即Web应用程序+ Web服务+ Windows服务)时,大多数DI purist(包括我)通常只将组合根移动到类库。 )使用相同的业务层。当我有一个应用程序时,我将Composition Root保留在我的最终应用程序中。

第二个选项是将所有与MVC相关的类(视图,控制器等)从启动项目移动到类库。这允许这个新的表示层组件与应用程序的其余部分保持断开连接。您的Web应用程序项目本身将成为一个非常薄的shell,具有所需的启动逻辑。 Web应用程序项目将是引用所有其他程序集的组合根。

在使用MVC时,将表示逻辑提取到类库会使事情变得复杂。由于控制器和视图,图像,css文件等不在启动项目中,因此将所有内容连接起来将更加困难。这可能是可行的,但需要更多时间来设置。

这两个选项都有它们的缺点,这就是为什么我通常建议将组合根保留在Web项目中。许多开发人员不希望他们的MVC程序集依赖于DAL程序集,但这不是一个真正的问题。不要忘记程序集是部署工件;您将代码拆分为多个程序集,以允许单独部署代码。另一方面,架构层是逻辑工件。在同一个程序集中拥有多个层是非常可能的(也是常见的)。

在这种情况下,我们最终会在同一个Web应用程序项目中具有组合根(层)和表示层(因此在同一个程序集中)。即使该程序集引用包含DAL的程序集,表示层仍然不引用数据访问层。这是一个很大的区别。

当然,当我们这样做时,我们失去了编译器在编译时检查这个架构规则的能力,但这应该不是问题。大多数架构规则实际上都不能由编译器检查,并且总是有类似常识的东西。如果您的团队中没有常识,您可以随时使用代码审查(每个团队都应该IMO总是这样做)。您还可以使用NDepend(商业版)等工具,它可以帮助您验证架构规则。当您将NDepend与构建过程集成时,它可以在有人检查违反此类架构规则的代码时发出警告。

您可以在我的书Dependency Injection, Principles, Practices, Patterns的第4章中阅读关于组合根如何工作的更详细的讨论。


5
投票

如果我没有使用DI容器,我不必在我的MVC3应用程序中引用EntityFramework库,只需要引用我的DAL / Repo层的业务层。

您可以创建一个名为“DependencyResolver”的单独项目。在这个项目中,您必须引用所有库。

现在UI层不需要NHibernate / EF或除了Castle Windsor之外的任何其他非UI相关库。

如果你想从UI层隐藏Castle Windsor和DependencyResolver,你可以编写一个调用IoC注册表内容的HttpModule。

我只有一个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中它应该是一个安装程序)。


0
投票
  • 存在依赖关系:如果对象实例化另一个对象。
  • 没有依赖:如果一个对象需要抽象(构造函数注入,方法注入......)
  • 程序集引用(引用dll,Web服务......)独立于依赖概念,因为要解析抽象并能够编译代码,该层必须引用它。
© www.soinside.com 2019 - 2024. All rights reserved.