gpt4 book ai didi

linq - 我应该将 DDD 聚合根存储库与 EF 4.1 + LINQ 一起使用吗?

转载 作者:行者123 更新时间:2023-12-04 17:04:33 25 4
gpt4 key购买 nike

我已经阅读了 DDD Evans,并且正在尝试使用 C# 和 Entity Framework 4.1 + LINQ 进行聚合根存储库设计。

但是,我担心发送到数据库的实际查询。我正在使用 SQL 2008 R2,并运行 SQL Profiler 来检查数据库响应 LINQ 代码所做的工作。

考虑一个包含 Person 和 EmailAddress 的简单 2 实体设计。一个人可以有零到多个电子邮件地址,一个电子邮件地址必须只有一个人。 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 Profiler 中显示了不同的命令:
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],
[Extent1].[PersonId] AS [PersonId],
[Extent1].[Value] AS [Value],
[Extent1].[IsDefault] AS [IsDefault],
[Extent1].[IsConfirmed] AS [IsConfirmed],
FROM [dbo].[EmailAddress] AS [Extent1]
WHERE [Extent1].[PersonId] =
@EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1

exec sp_executesql N'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]
WHERE [Extent1].[PersonId] =
@EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=2

我的测试数据库在 dbo.EmailAddress 中有 14 行,并且有 14 个不同的 RPC:Completed 调用,每个调用都有不同的 @EntityKeyValue1 值。

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

更新:已解决

问题是 All 属性返回了 IEnumerable<TEntity> .之后改为 IQueryable<TEntity> , LINQ 启动并一次性选择了整个 Person + Emails。但是,在从 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#),它可能是您实现规范所需的全部内容。

总结这篇冗长的(希望不会太杂乱无章,我希望稍后会重新访问)帖子:

  • 不要为基础设施而疯狂,随心所欲地建立它。
  • 将您的域模型用于特定于域的行为,而不是用于支持您的 View 。
  • 专注于 DDD 更重要的部分:使用聚合根,建立你的通用语言,确保与业务专家的良好沟通。
  • 关于linq - 我应该将 DDD 聚合根存储库与 EF 4.1 + LINQ 一起使用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6047163/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com