Entity Framework 6 数据库优先和洋葱架构

14
我正在使用Entity Framework 6数据库优先。我正在将项目转换为实现洋葱架构,以朝着更好的关注点分离方向发展。我已经阅读了许多文章并观看了许多视频,但在决定解决方案结构时遇到了一些问题。
我有4个项目:核心,基础设施,Web和测试。
从我所学到的知识来看,.edmx文件应放置在我的“基础设施”文件夹下。然而,我也读过关于使用存储库和工作单元模式来帮助EF解耦和使用依赖注入的文章。
基于这个前提:
  • 我需要在CORE下为模型中的所有实体创建存储库接口吗?如果是这样,如何在庞大的数据库上维护它?我已经研究了自动映射器,但发现它存在IEnumererables与IQueryables之间的问题,但有一个扩展可用于帮助解决。我可以深入尝试此路线,但想先听听回复。

  • 作为替代方案,我应该将我的edmx留在Infrastructure中,并将实体的.tt T4文件移动到CORE中吗?这会产生任何紧密耦合或良好的解决方案吗?

  • 通用存储库接口是否能很好地与您提供的建议配合使用?或者EF6已经解决了存储库和UoW模式问题?

感谢您查看我的问题,请提出任何其他替代方案。

我在这里找到了一个类似的帖子,但没有得到答复: EF6和洋葱架构-基于数据库而不是存储库模式


2
接口和实体应该在核心中。对于大型数据库,请查看有界上下文和领域驱动设计。洋葱架构的目标是使您的核心项目不引用外部框架,如EF、AutoMapper、ASP.NET、WCF等。对于EF来说,如果您使用EDMX,则更难将实体和EF本身分离。 - Anthony Chu
关于AnthonyChu的评论,如果您想使用有界上下文和约束的DbContext,则EF Power Tools很难使用。看看新的EF 6.1 Designer。这是我在beta版发布时写的一篇文章,虽然现在已经发布了。http://thedatafarm.com/data-access/first-look-at-beta-of-ef-6-1-designer/ - Julie Lerman
1
你为什么要考虑使用洋葱架构?它能解决哪些问题?听起来你正在以封装的名义下创建许多层和抽象。 - Jimmy Bogard
@JimmyBogard主要是因为它是一个很好的框架,可以让我在UI和单元测试中都使用依赖注入。此外,我可能不会永远使用EF或相同的UI框架,所以想要有这种SoC。你有其他的选择吗?我现在正在学习DDD。 - Sean Merron
DDD不是一个项目结构,事实上,Eric Evans弱化了结构模式,倾向于书中的最后一节。 - Jimmy Bogard
显示剩余5条评论
2个回答

10

Database first并不完全排除洋葱架构(也称为端口和适配器或六边形架构,所以如果您看到这些内容的参考资料,它们是相同的),但它肯定更困难。洋葱架构及其允许的关注点分离非常适合领域驱动设计(我想你在Twitter上已经看过我在Pluralsight上有关此主题的一些视频)。

您应该绝对避免将EDMX放入核心或Web项目中 - 基础设施是正确的位置。此时,使用数据库优先方法,您将在基础设施中拥有EF实体。但是,您希望您的业务对象/域实体位于核心位置。此时,如果您想继续沿着这条路走,您基本上有两个选择:

1)从数据库优先转换为代码优先(可能使用工具),以便您可以在核心中拥有POCO实体。

2)在基础设施实体和核心对象之间进行映射,可能使用AutoMapper之类的东西。在EF支持POCO实体之前,这是我在使用它时采用的方法,并且我会编写仅处理核心对象但在内部将其映射到EF特定实体的存储库。

关于仓储库和工作单元的问题,已经有很多关于此的文章在SO和其他地方发表过。您可以使用通用的仓储库实现来轻松进行对大量实体的CRUD访问,这似乎是您在当前情况下快速前进的一种方式。然而,我的总体建议是避免将通用仓储库作为访问您的业务对象的主要手段,而是使用聚合根(请参见DDD或我与Julie Lerman在Pluralsight上的DDD课程)并使用一个具体的仓储库来管理每个聚合根。您还可以将复杂的业务实体与CRUD操作分离开来,并仅在必要时采用聚合方法。这种方法的好处是您可以限制对象的访问方式,并获得类似于Facade覆盖您(大量)数据库实体的好处。
不要觉得你的应用程序只能有一个dbcontext。听起来你是在不断地演化这个设计,而不是从零开始开发一个全新的应用程序。因此,你可以保留.edmx文件和通用存储库以进行CRUD操作,但是为了特定的一组操作,需要使用新的Code First dbcontext,以获得POCO实体、关注点分离、增加可测试性等优点。随着时间的推移,你可以将大部分重要代码转移到使用新的dbcontext上,同时保留现有的dbcontext,以便不会失去任何当前功能。

感谢@ssmith,看起来我可能需要更深入地了解DDD和CF,而不是继续沿着db first的道路前进。我对此还很陌生,肯定会有很多问题,但如果我朝这个方向发展,你个人如何处理将数据库更改集成到POCO类中?您是否严格强制迁移,或者仍然遇到必须将这些数据库更改传播到代码中的情况,就像db first模型更新所做的那样? - Sean Merron
如果您正在遵循DDD方法,数据库应该支持对模型的更改,而不是相反。不应该有任何需要您更改模型的数据库更改。理想情况下,您拥有一个完全控制的数据库,仅供您的应用程序使用,并通过服务或某种防腐层与组织中的其他系统或数据库集成,而不是直接与数据库交互。希望这可以帮助到您。 - ssmith
1
正是我所需要的,@ssmith 再次感谢!我刚刚打开了你和朱莉一起的 DDD 课程,开始学习更多关于这个概念的知识。 - Sean Merron
EDMX文件是一种资源;但是,如果您使用实体部署构建它,则将元数据嵌入到其所在的程序集中,因此它具有运行时功能。 您可以通过修改TextTemplate并更新文本中的edmx文件路径来将生成实体和dbcontext的T4 TextTemplate文件迁移到其他程序集/项目中。注意:现在您的项目之间的相对路径成为一个因素。EDMX文件还可以被WebAPI 2 OData DataServiceContext用于LINQ到服务操作,因此从这个意义上说,它实际上是一种共享资源。 - Brett Caswell
无论 edmx 最终在哪个程序集/项目中,都必须作为依赖项添加到初始化 dbcontext 实例的任何项目中。连接字符串元数据声明将尝试在使用 dbcontext 的时候解析/使用嵌入式实体元数据,我想这将在您的基础设施服务层中使用通用存储库进行初始化... - Brett Caswell

1
我在我的DDD项目中使用实体框架6.1。如果你想要进行洋葱架构,那么Code first非常适用。
在我的项目中,我们完全将Repository与Domain Model隔离开来。Application Service是使用repository从数据库加载聚合并将聚合持久化到数据库的地方。因此,在domain(核心)中没有存储库接口。
使用T4在单独的程序集中生成POCO的第二个选项是一个好主意。请记住,您的domain model(核心)应该忽略持久性。
尽管通用存储库对于强制执行聚合级别操作很好,但我更喜欢使用特定存储库,因为不是每个聚合都需要所有这些“{{link1:generic repository operations}}”。

http://codingcraft.wordpress.com/


谢谢,自从发帖以来,我已经对DDD进行了大量研究,并将采用类似的方法。听到别人实施DDD的经验很棒。 - Sean Merron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接