【问题标题】:Dependency Inversion with compile time configured Dependency Injection in an ASP.NET MVC 4 Solution在 ASP.NET MVC 4 解决方案中使用编译时配置的依赖注入的依赖反转
【发布时间】:2013-05-23 20:52:34
【问题描述】:

我一直在研究如何设计一个 MVC 4 Web 解决方案,该解决方案遵循依赖倒置原则并利用配置流畅的依赖注入 (DI) 容器(即通过编译时类型检查)。

ASP.NET MVC 4 Dependency Injection 的许多示例都集中在将 DI 实现到 MVC 框架提供的入口点的细节上。我发现自己倾向于产生的分层方法(用红色箭头显示的依赖项): 不幸的是,它重复了传统的依赖模型,其中高级模块依赖于低级模块。遵循依赖倒置的原则,IService 接口被移动到 WebProject 中。不幸的是,这在两个项目之间创建了一个循环引用: 为了避免循环引用,CompositionRoot 被移动到它自己的项目中:

给我留下了如何引导容器的问题(现在我不能直接从 WebProject 中引用它)?

the best way to get the directory from which an assembly is executing的一点帮助下,可以通过反射实现初始化。

var assembly = System.Reflection.Assembly.LoadFile(Helper.AssemblyDirectory + "/DependencyInjectionProject.dll");
var type = assembly.GetType("DependencyInjectionProject.Bootstrapper");
IDependencyResolver resolver = (IDependencyResolver)type.GetMethod("Initialise").Invoke(null, null);
DependencyResolver.SetResolver(resolver);

为了简化构建,我将 DependencyInjectionProject 的构建目标设置为 WebProject bin 目录。我已经实现了我的目标;反向依赖项和编译时间检查容器配置,但我对这种方法并不完全满意,因为在 IIS Express 中运行 WebProject 时经常发生构建目标冲突。

我很想听听其他满足这些要求的经验和方法。我应该放弃编译时配置并采用基于文本的配置吗?是否有明显的层结构来避免我没有看到的循环依赖的陷阱?

【问题讨论】:

    标签: c# asp.net-mvc-4 dependency-injection circular-dependency solid-principles


    【解决方案1】:

    Composition Root 与 DI Container 不同。组合根是可执行文件的非常早期的入口层,DI Container 放置在那里。

    DI Container 放在那里的原因之一是,入口级别没有被任何其他类调用。所以当你在那里创建 DI Container 时,它会确保 DI Container 在最早的调用时被构建,并防止空引用异常。可能还有其他好处,但我就是不知道。

    我通常使用的是分层设计,包括:

    1. 域模型(实体)
    2. 接口(参考 1)
    3. 服务(参考 1 和 2)
    4. dal(参考 1 和 2)
    5. UI(参考 1,2,3,4)

    这种设计必须优先选择好的 SOC 并且可以防止 UI 直接访问存储库。 dal 不了解服务和 UI。该服务不知道 DAL 和 UI。

    我知道的一个缺点是这种设计允许 UI 无需服务直接访问 DAL。

    我仍然不知道这种设计的其他缺点。

    【讨论】:

    • 我可能在使用“组合根”这个词时弄混了水。用容器配置代替它可能会更好地传达我的意图。您在此处概述的依赖项遵循更传统的方法;一个不是“倒置”的。我考虑过在它们自己的层上分离接口,但遇到了相同的循环依赖问题。
    • 好吧,如果是这种情况,我很抱歉,我无能为力。在我看来,倒置方法根本没有任何好处(请随时纠正我),并且它会增加复杂性流程(UI -> SL -> DI Container -> UI 再次)。它是一个循环依赖,使用服务定位器只是隐藏依赖,而不是消除它。
    【解决方案2】:

    您绝不会希望将 IService 移至 Web 项目中。除了循环引用(这不一定是交易破坏者,尽管 VS 不会让你这样做.. 它可以通过其他几种方式完成),这只是糟糕的设计。

    高级模块依赖于低级模块绝对没有错。我不确定你为什么认为这是一个问题。依赖倒置实际上并不是指模块或程序集依赖,而是指接口依赖。 (我在这里泛泛而谈,而不是专门针对 interface 关键字)。事实上,你可以在没有任何单独程序集的情况下拥有 DI……它们都可以在同一个程序集中。

    但是,如果您确实选择将实现拆分为单独的程序集,则存在某些限制。这些主要是装配系统的实现问题。

    只有两个模块时,模块依赖的问题并没有真正表现出来。当你有 3 个或更多时,问题就更大了。

    我自己喜欢使用洋葱架构。在此方法中,您有一个单独的程序集,其中包含您的接口和常用类型(例如实体)。这个程序集没有很多代码,主要是定义。

    上面是您的数据层和业务层。这些取决于实体和接口的通用程序集。然后你有你的 UI 层,它取决于你的业务(或服务层,它通常只是你的业务层的一个外观)和公共层。

    使用这种方法,用户界面无法与 DAL 对话,反之亦然。业务层与 DAL 和 UI 对话。一切都依赖于具有很少功能代码的通用接口程序集,只有实体、DTO 和接口。因此,通用性只依赖于核心 .NET 东西。

        UI <--------> Service/Business <--------> DAL
         |                   |                     |
         +----------> Common (IService) <----------+
    

    没有循环引用,每个人都得到他们想要的,只与他们需要的人交谈。

    【讨论】:

    • 哪些项目引用了容器,它在哪里引导?
    • 恭敬地;我认为您对依赖倒置原则有一个常见的误解。请考虑:“虽然对接口而不是实现进行编程代表了良好的设计实践,但依赖倒置原则不仅关注接口的使用,还关注高级组件与依赖包的解耦。” - aspiringcraftsman.com/2008/12/28/examining-dependency-inversion
    • @RichardTeviotdale - 这是一个令人困惑的话题,因为该术语经常被误用、过度使用或过载。您正在谈论“系统”所需的依赖项(即程序集引用)。这些文章的粒度要细得多,并讨论 UML 意义上的“组件”和“包”,这并不一定意味着 .net 中的组装级别。 UML 意义上的包可以同时存在于同一个程序集中。不要混淆这两个独立的问题。在阅读 Martin 或其他人的文章时,它们通常不是针对技术的。
    • @RichardTeviotdale - 请记住,依赖注入正在讨论类/接口级别的事情。这因技术特定问题(例如程序集引用)而变得复杂,但它们不是 DI 本身的问题。 DI 不会改变对象依赖于其他对象的事实,因为它们必须与它们通信(即使它是通过 DI 通过弱耦合接口)。您必须在 DI 容器中将这些对象耦合在一起。这仍然会在应用程序(或组合根)级别创建依赖关系,而不是在类级别。
    • @RichardTeviotdale - 依赖注入、依赖倒置和 IoC 也不是可互换的术语。它们的含义都略有不同,有些含义比其他含义更广泛。例如,Injection 和 Inversion 都是 IoC,但 IoC 不一定是其中任何一个。
    猜你喜欢
    • 1970-01-01
    • 2010-11-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-07
    • 2015-09-02
    相关资源
    最近更新 更多