加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 程序设计 > 正文

我应该使用带有EF 4.1 LINQ的DDD聚合根存储库吗?

发布时间:2020-05-23 00:16:00 所属栏目:程序设计 来源:互联网
导读:我读过DDD Evans,我正在尝试使用C#和Entity Framework 4.1 LINQ进行聚合根存储库设计. 但是,我担心发送到数据库的实际查询.我正在使用SQL 2008 R2,并运行SQL事件探查器来检查数据库在响应LINQ代码时正在做什么. 考虑使用Person和EmailAddress的简单2实体设计.

我读过DDD Evans,我正在尝试使用C#和Entity Framework 4.1 LINQ进行聚合根存储库设计.

但是,我担心发送到数据库的实际查询.我正在使用SQL 2008 R2,并运行SQL事件探查器来检查数据库在响应LINQ代码时正在做什么.

考虑使用Person和EmailAddress的简单2实体设计.一个人可以拥有零到多个EmailAddresses,而EmailAddress必须只有一个Person. Person是聚合根,因此不应该有电子邮件地址的存储库.应从Person存储库中选择电子邮件地址(根据DDD Evans).

为了比较,我确实为电子邮件地址设置了临时存储库.以下代码行:

var emailString = "someone@somewhere.com";
var emailEntity = _tempEmailRepository.All.SingleOrDefault(e => 
    e.Value.Equals(emailString,StringComparison.OrdinalIgnoreCase));

…根据探查器执行一个很好的干净SQL查询:

SELECT 
[Extent1].[Id] AS [Id],[Extent1].[PersonId] AS [PersonId],[Extent1].[Value] AS [Value],[Extent1].[IsDefault] AS [IsDefault],[Extent1].[IsConfirmed] AS [IsConfirmed],FROM [dbo].[EmailAddress] AS [Extent1]

我可以使用以下代码从人员存储库中选择电子邮件:

var emailEntity = _personRepository.All.SelectMany(p => p.Emails)
    .SingleOrDefault(e => e.Value.Equals(emailString,StringComparison.OrdinalIgnoreCase))

这在运行时获取了相同的实体,但在SQL事件探查器中显示了不同的命令:

SELECT 
[Extent1].[Id] AS [Id],[Extent1].[FirstName] AS [FirstName],[Extent1].[LastName] AS [LastName],FROM [dbo].[Person] AS [Extent1]

除了从Person中选择的上述查询之外,还有许多“RPC:Completed”事件,一个用于DB中的每个EmailAddress行:

exec sp_executesql N'SELECT 
[Extent1].[Id] AS [Id],FROM [dbo].[EmailAddress] AS [Extent1]
WHERE [Extent1].[PersonId] = 
    @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

exec sp_executesql N'SELECT 
[Extent1].[Id] AS [Id],@EntityKeyValue1=2

我的测试数据库在dbo.EmailAddress中有14行,并且有14种不同的RPC:已完成的调用,每个调用具有不同的@ EntityKeyValue1值.

我认为这对SQL性能有害,因为当dbo.EmailAddress表获得更多行时,将在db上调用更多这些RPC.是否有另一种更好的方法来使用带有EF 4.1 LINQ的DDD聚合根存储库?

更新:解决了

问题是All属性返回了IEnumerable< TEntity>.在此变为IQueryable< TEntity>之后,LINQ开始并一次性选择整个人员电子邮件.但是,在从All返回IQueryable之前,我必须链接.Include(p => p.Emails).

鉴于现代ORM已经为您提供的抽象级别,我个人建议不要在您和您的数据库之间添加额外的抽象层.除了重新发明轮子,你会发现在你的服务层直接使用所选择的ORM将使你对查询,获取和缓存策略有更精细的控制.

Ayende的系列Wages of Sin是一个很好的资源,可以反对使用带有现代ORM的规格/存储库的各种其他论点,特别是考虑到LINQ实际上已经为您提供了几乎所有您可能需要的东西.

我已经在过去的项目中使用了“DDD”的路线(在引号中,因为它与我当时对DDD的理解有关).事后看来,我意识到在公开辩论中DDD经常被简化为应用这些模式,这是一种耻辱.我陷入了陷阱,我希望我可以帮助别人避免它.

存储库和规范是基础结构模式.基础设施是为了一个目的,而不是一个目的.谈到基础设施,我主张严格应用重用抽象原则.为了给出一个快速总结,RAP说你应该引入一个抽象,只要它将由超过2个消费者使用,并且额外的抽象层实际上实现了一些行为.如果你只是引入一个抽象来将你与某些东西(例如ORM)分离,那么要非常小心,你很可能会得到一个漏洞的抽象.

DDD的重点在于将您的域模型与您的基础架构分开,并使您的域模型尽可能具有表现力.没有证据表明如果不使用存储库就无法实现.存储库只是隐藏数据访问的细节,这是ORM已经做过的事情. (另一方面,考虑到DDD书的年龄,我认为ORM的共同使用并不在当时的图片中).现在,存储库可能对强制聚合根等有用.但是,我认为应该通过明确区分“读取”操作(查询)和“写入”操作(命令)来对待.对于后者而言,域模型应该是相关的,通常通过定制(和更灵活)的模型(例如DTO或匿名对象)更好地服务于查询.

规格的情况类似.规范的预期目的是类似的.它们的强大之处在于构建用于查询对象的特定于域的语言的元素.随着LINQ的出现,为了组合这些元素而提供的概括规范模式的大部分“胶水”已经过时了.提示:看一下Predicate Builder(&#50 Lines of C#),你可能只需要实现规范.

总结一下这个冗长的(希望不会太混乱,我稍后会再次访问,希望)帖子:

>不要为基础设施而疯狂,随时随地进行构建.>将您的域模型用于特定于域的行为,而不是用于支持您的视图.>关注DDD更重要的部分:使用聚合根,建立无处不在的语言,确保与业务专家的良好沟通.

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读