【问题标题】:repositories and querying with raw sql?存储库和使用原始 sql 查询?
【发布时间】:2012-03-01 02:40:45
【问题描述】:

我正在努力了解如何最好地查询存储库。

现在让我陷入困境的三个因素是:

  1. 返回数据类型
  2. 要运行查询的列
  3. 要返回的记录数

第 1 点

关于问题一:

我的存储库包含许多返回实体和标量值组合的方法。这似乎导致了“方法爆炸”。我应该总是返回一个实体对象吗?我应该如何查询只需要一列的对象?

第 2 点 运行查询时,即使我只需要一列或两列,我是否应该包含表中的每一列?如果我为此创建特定查询,则会导致存储库中的更多方法

第 3 点 我应该如何为查询提供条件?我阅读了规范,但我的理解是您遍历返回的记录并过滤掉传递到新集合中的记录。这在性能方面似乎不是一个好主意。现在我只是在 Repo 中创建了一个新方法,例如封装条件的 getNameById()。

请注意我没有使用 ORM,我的存储库中只有原始 sql

更新

第 1 点: 根据答案和更多研究,这将是一个好的实施吗?

现在我有一个大型存储库,它返回标量和实体类型对象的混合(所有相同的实体)。我想如果我只使用 GetUser(userId) 方法而忘记编写只返回单列值的方法,我可以大大减少这种情况。

例如,如果我需要返回一个用户名,我可以调用 GetUser(userId) 方法来混合 User 对象,然后在服务层将其过滤到用户名。

另一种方法是使用某种 QueryBuilder 类,我可以将其传递到存储库中,可以对其进行解析以生成正确的 sql。

第 2 点

回想起来,这与第一点非常相似,我目前的解决方案是获取所有表字段。这是性能和可维护性之间的权衡。

第 3 点

我需要提供某种 where 子句。我不确定这是否有意义通过规范或只是一个 sql 字符串。我目前的解决方案是为这些类型创建新方法,但我希望存储库更通用

总体而言,仍在对此进行研究...我很想听听对此的更多意见,或者将这些联系在一起的书籍或参考资料的链接。

【问题讨论】:

  • 你在课堂上动态创建Sql命令吗?
  • 原始 SQL 并且没有 ORM,哈?就像生活在边缘一样,是吗?说真的,为什么要处理原始 sql?
  • @kerezo - 我有 sql 参数并使用 addwith value

标签: c# domain-driven-design repository repository-pattern ddd-repositories


【解决方案1】:

我的存储库包含许多返回实体和标量值组合的方法。这似乎导致了“方法爆炸”。我应该总是返回一个实体对象吗?我应该如何查询只需要一列的对象?

您可以像对抗其他 SRP 违规一样对抗存储库方法爆炸。您可以为同一实体创建另一个存储库。看到这个answer 来回答类似的问题。

在运行查询时,即使我只需要一列还是两列,我是否应该将表中的每一列都包含在内?如果我为此创建特定查询,则会导致存储库中的更多方法

这不是 DDD 问题。 领域 驱动设计不处理“行和列”。您加载多少数据以“水合”域对象总是存在一些冗余,但您必须衡量这是否真的影响您的性能。如果这真的是一个性能瓶颈,那么它可能是域模型不正确的症状。

我应该如何为查询提供条件?我阅读了规范,但我的理解是您遍历返回的记录并过滤掉传递到新集合中的记录。这在性能方面似乎不是一个好主意。现在我只是在 Repo 中创建了一个新方法,例如封装条件的 getNameById()。

这又是一个数据访问问题。 DDD 中没有任何内容表明您的存储库不能将规范转换为 SQL 查询。是否执行此操作或迭代内存中的记录取决于您自己(只要存储库使用者只看到规范和存储库并且不知道实际实现)。

关于“DDD 中的原始 SQL 与 ORM”,您可能会发现 answer 很有趣。

【讨论】:

  • 您能否指出一些解决此数据访问问题的答案或教程?
  • DDD 书本身有一个关于规范的示例,出于性能考虑,该示例转换为原始 SQL。
  • 那是 Eric Evans 的蓝色吗?不会碰巧知道那个例子的页码吗? :)
【解决方案2】:

我同意 Dmitry 所说的一切,但也许认为您应该阅读一下 CQRS

我曾经在开始使用 DDD 时问过类似的问题(关于“方法爆炸”,而不是您的 SQL 问题),这让我找到了CQRS。就个人而言,我真的不明白没有它的 DDD 是如何实用的,它在查询数据时回答了很多此类问题。我建议使用它的原则:

  1. 仅在提交事务时使用域存储库。也就是说,您不使用存储库在 UI 中显示数据。仅当您想要对它们执行操作时,才从存储库中获取聚合。
  2. 您的存储库仅返回聚合,而不是单独返回单个实体。现在这是有道理的,因为我们仅在事务意义上使用存储库,并且实体只能通过原子操作进行变异并由聚合作为一个整体进行持久化。
  3. 您可以创建单独的存储库(或“查询服务”),为您需要的任何数据提供量身定制的查询和数据类型。这些可以返回没有逻辑的愚蠢 DTO。

这可以保持您的正确域和存储库清洁,同时提供创建提供高性能查询的精简数据访问层的方法。

关于规范模式:您可以在规范上提供表示标准的公共属性,而不是将其转换为代码中的 SQL 查询。然后可以将这些值添加到 SQL 的 where 子句中或作为参数发送到 SPROC。

【讨论】:

    【解决方案3】:

    首先,您还没有真正解释您使用所有这些查询的目的。可能是为了满足用户界面的需求。如果是这样,则无需跳过所有这些环节(服务->存储库->域->dto->客户端),只需尽可能直接查询数据库即可。您知道吗,您是否可以查询标量或仅查询您需要的列的问题已经不复存在。只需使用普通 sql 并返回您需要的内容。不要创建会引起摩擦的抽象。

    【讨论】:

    • 我正在寻找一种查询存储库的“最佳实践”方法,因此我没有一百万种方法,而且它在某种程度上也影响了一些不错的性能。
    • 如果它们不符合域的目的,首先不要将它们添加到存储库中。将它们移到代表用户界面所需查询的另一个外观上。从客户端到立面到数据库到立面到客户端的速度一样快。使用简单的 DTO 来表示需要传输到客户端的状态。
    【解决方案4】:

    乔布,

    我们需要记住关于存储库 [Fowler PoEAA][Evans DDD] 模式的两件事:

    1. 将存储库模式用作简单集合。存储库抽象出这些基础架构细节,因为它们不是来自域。
    2. 如果 Repository pattren 是一个集合,那么它就是一组相同类型的对象。

    另外两种类型可能对您的存储库有所帮助:查询对象 [Fowler PoEAA] 和数据映射器 [Fowler PoEAA] 模式。使用面向对象的方法查询对象模式聚合标准,并知道如何将它们转换为 SQL 语句。 Data Mapper 模式知道从应用程序中映射对象状态和从数据库中映射表列。

    您可以使用延迟加载模式 [Fowler PoEAA] 来缓解内存中大对象的问题。

    你成功了!

    【讨论】:

    • 我认为查询对象对使用不同条件进行查询的问题有很大帮助。我猜 ORM 已经内置了。感谢您提供解决这些问题的模式列表!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-29
    • 2015-11-21
    • 2012-03-16
    • 2014-04-15
    • 1970-01-01
    相关资源
    最近更新 更多