3

我正在开始一个新的 MVC 项目,并且(几乎)决定尝试一下 Repository Pattern 和 Dependency Injection。筛选变化需要一段时间,但我为我的应用程序提出了以下结构:

  1. 表示层:ASP.Net MVC 前端(视图/控制器等)
  2. 服务层(业务层,如果您愿意):接口和 DTO。
  3. 数据层:接口实现和实体框架类。

在我的解决方案中,它们是 3 个独立的项目。表示层只有对服务层的引用。数据层也只有对服务层的引用——所以这基本上遵循领域驱动设计。

以这种方式构建事物的目的是为了关注点分离、松散耦合和可测试性。如果其中任何一项不合理,我很乐意就改进提出建议?

我遇到困难的部分是将接口实现对象从数据层注入到表示层,它只知道服务层中的接口。这似乎正是 DI 的用途,而 IoC 框架(据称!)使这更容易,所以我想我会尝试 MEF2。但是在过去几天我阅读的数十篇文章和问题和答案中,似乎没有什么能以适合我结构的方式真正解决这个问题。几乎所有这些都已弃用和/或都是简单的控制台应用程序示例,它们在同一个程序集中具有所有接口和类,彼此了解,完全无视松散耦合和 DI 的观点。

有一些解决方案探索基于属性的注册,但据说已经被基于约定的注册所取代。我还看到很多将对象注入控制器构造函数的示例,这引入了它自己要解决的一组问题。我不相信控制器实际上应该知道这一点,并且宁愿将对象注入模型中,但这可能是有原因的,因为很多示例似乎都遵循这条路径。我还没有深入研究这一点,因为我仍然坚持尝试将数据层对象放到任何地方的表示层中。

我相信我的主要问题之一是不了解各种 MEF2 需要在哪一层进行,因为我发现的每个示例都只使用一层。有容器、注册和目录以及导出和导入配置,我一直无法弄清楚所有这些代码应该放在哪里。

具有讽刺意味的是,现代设计模式本应抽象复杂性并简化我们的任务,但如果我刚刚从 PL 中引用 DAL 并着手处理应用程序的实际功能,我现在已经完成了一半。如果有人能说,‘是的,我明白你在做什么,但你缺少 xyz,我会非常感激。你需要做的是 abc'。

谢谢。

4

2 回答 2

0

是的,我明白你在做什么(或多或少)但是(据我所知)你错过了 a)将合同和实现类型分离到他们自己的项目/程序集中和 b)配置的概念DI 容器,即配置哪些实现应用于接口。

有无限的方法来处理这个问题,所以我给你的是我个人的最佳实践。我一直以这种方式工作了很长时间,并且仍然对此感到满意,因此我认为值得分享。

一种。总是要项目:MyNamespace.SomethingMyNamespace.Something.Contracts

一般来说,对于 DI,我有两个程序集:一个用于仅包含接口的合约,另一个用于实现这些接口。在您的情况下,我可能有五个程序集:Presentation.dllServices.dllServices.Contracts.dll和.DataAccess.dllDataAccess.Contracts.dll

(另一个有效的选择是将所有合同放在一个程序集中,我们称之为 Commons.dll)

显然,DataAccess.dll引用DataAccess.Contracts.dll,因为里面的类DataAccess.dll实现了里面的接口DataAccess.Contracts.dllServices.dll和相同Services.Contracts.dll

不,解耦部分:Presentation引用Services.ContractsData.Contracts. 服务参考Data.Contracts。如您所见,不依赖于具体的实现。这就是整个 DI 的内容。如果您决定交换数据访问层,则可以DataAccess.dllDataAccess.Contracts.dll保持不变的情况下进行交换。您的其他程序集都没有直接引用DataAccess.dll,因此没有断开的链接,版本冲突等。如果不清楚,请尝试绘制一点依赖关系图。您会看到,没有箭头指向任何名称中没有的程序集.Contracts

你能理解这个吗?请问有什么不清楚的地方。

湾。选择如何配置容器

您可以在显式配置(XML 等)、基于属性的配置和基于约定的注册之间进行选择。虽然出于显而易见的原因前者很痛苦,但我是后者的粉丝。我认为它比基于约定的配置更具可读性和易于调试,但这是一个品味问题。

当然,容器类型捆绑了所有依赖项,而您在应用程序架构中已经避免了这些依赖项。为了清楚我的意思,请考虑针对您的案例的 XML 配置:它将包含指向所有实现程序集的“链接” DataAccess.dll, ...。尽管如此,这并没有破坏脱钩的想法。很明显,当交换实现程序集时,您需要修改配置。

但是,使用基于属性或约定的配置时,您通常使用您提到的自动发现机制:“在位于 xyz 的所有程序集中搜索”。这确实需要将所有程序集放在应用程序bin目录中。它没有任何问题,因为代码需要在某个地方,对吧?

你有什么收获?假设您已经部署了应用程序并决定交换 DataAccess 层。假设您选择了基于约定的 DI 容器配置。你现在可以做的是在 VS 中打开一个新项目,引用现有的DataAccess.Contracts.dll并以任何你喜欢的方式实现所有接口,只要你遵循约定。然后构建库,调用它DataAccess.dll并将其复制并粘贴到原始应用程序的程序文件夹中,替换旧的DataAccess.dll. 完成后,您已经交换了整个实现,甚至没有注意到任何其他程序集。

我想你应该已经明白了。这确实是一个权衡,使用 IoC 和 DI。我强烈建议您在设计决策时要务实。不要接口所有东西,它只会变得混乱。自己决定,DI 和 IoC 真正有意义的地方,不要太受社区宗教讨论的影响。尽管如此,如果使用得当,IoC 和 DI 真的非常非常强大!

于 2014-06-14T14:28:12.113 回答
0

好吧,我在这方面又花了几天时间(现在总共大约一周),并没有取得什么进一步的进展。我相当确定我的容器设置正确,我的约定发现要映射的正确部分等,但我无法弄清楚让控制器 DI 激活的似乎缺少的链接 - 我经常收到错误消息指出我没有提供无参数构造函数。所以我已经完成了。

然而,我确实设法推进了我的结构和将 DI 与 IoC 一起使用的意图。如果有人碰到了我曾经遇到的同一堵墙并想要一个替代解决方案:放弃 MEF 2 并使用 Unity。最新版本(撰写本文时为 3.5)已按照惯例进行了发现,并且就像开箱即用的款待一样 - 它甚至有一个包含工作示例的相当详尽的手册。还有其他 IoC 框架,但我选择了 Unity,因为它支持 MS 并且在性能基准测试中表现良好。从 NuGet 安装引导程序包,大部分工作已为您完成。最后我只需要写一行代码来映射我的整个 DAL(他们甚至为你创建了一个存根,这样你就知道在哪里插入它):

        container.RegisterTypes(
            AllClasses.FromLoadedAssemblies().Where(t => t.Namespace == "xxx.DAL.Repository"),
            WithMappings.FromMatchingInterface,
            WithName.Default);
于 2014-06-18T13:57:36.157 回答