C# ASP.NET MVC 4解决方案中具有编译时配置的依赖项注入的依赖项反转

C# ASP.NET MVC 4解决方案中具有编译时配置的依赖项注入的依赖项反转,c#,asp.net-mvc-4,dependency-injection,circular-dependency,solid-principles,C#,Asp.net Mvc 4,Dependency Injection,Circular Dependency,Solid Principles,我一直在研究如何设计一个MVC4Web解决方案,该解决方案遵循依赖倒置原则,并利用一个配置流畅的依赖注入(DI)容器(即编译时类型检查) 我发现许多示例都集中在MVC框架提供的入口点中实现DI的细节上。我发现自己被最终的分层方法所吸引(红色箭头所示的依赖关系): 不幸的是,它重复了传统的依赖模型,即高级模块依赖于低级模块。遵循依赖倒置的原则,iSeries设备接口被移动到WebProject中。不幸的是,这在两个项目之间创建了一个循环引用: 为避免循环引用,将CompositionRoot移动到

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

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

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

在初始化的帮助下,可以通过反射来实现

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时经常发生构建目标冲突


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

组合根与DI容器不同。compositionroot是可执行文件的早期入口级别,DI容器放在那里

放置DI容器的原因之一是,因为入口级别没有被任何其他类调用。因此,当您在那里创建DI容器时,它将确保在最早调用时构建DI容器,并防止出现空引用异常。也许还有其他好处,但我就是不知道

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

  • 域模型(实体)
  • 接口(参考1)
  • 服务(参考1和2)
  • dal(参考文献1和2)
  • 用户界面(参考1,2,3,4)
  • 这种设计必须倾向于良好的SOC,并且可以防止UI直接访问存储库。dal不了解服务和UI。该服务不了解DAL和UI

    我知道的一个缺点是这种设计允许用户界面直接访问DAL而不需要服务


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

    您永远不会想将iSeries应用到web项目中。除了循环引用(这不一定是一个交易破坏者,尽管VS不允许你这么做..它可以通过其他几种方式实现)之外,它只是一个糟糕的设计

    高级模块依赖于低级模块绝对没有问题。我不知道你为什么认为这是个问题。依赖项反转实际上并不是指模块或程序集依赖项,而是指接口依赖项。(我在这里是泛泛地说,而不是具体地说接口关键字)。事实上,您可以在没有任何单独程序集的情况下使用DI。。。它可能都在同一个组件中

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

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

    我喜欢自己使用洋葱结构。在该方法中,您有一个单独的程序集,其中包含接口和公共类型(如实体)。这个程序集中没有太多的代码,大部分只是定义

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

    使用这种方法,UI无法与DAL对话,反之亦然。业务层与DAL和UI对话。一切都取决于公共接口程序集,它只有很少的功能代码,只有实体、DTO和接口。因此,除了核心的.NET内容外,公共关系不依赖其他任何东西

        UI <--------> Service/Business <--------> DAL
         |                   |                     |
         +----------> Common (IService) <----------+
    
    UI服务/业务DAL
    |                   |                     |
    
    +---------->我可能用复合根这个词把水弄脏了。用容器配置代替它可能更好地表达我的意图。您在这里概述的依赖关系遵循更传统的方法;不是“倒转”的。我已经考虑过在自己的层上分离接口,但是遇到了同样的循环依赖问题。如果是这样,我很抱歉,我无能为力。在我看来,反向方法根本没有任何好处(请随意更正),它可以增加复杂的流程(UI->SL->DI容器->UI)。它是一个循环依赖项,使用服务定位器只是隐藏依赖项,而不是消除依赖项;我认为你对依赖倒置原则有一个常见的误解。请考虑:“虽然编程到接口而不是实现代表了良好的设计实践,但依赖倒置原则不仅关注接口的使用,而且关注高级组件与依赖包的解耦。”——@RichardTeviotdale——这是一个令人困惑的话题,因为术语