我的应用的数据访问层由实体框架和存储库模式*组成。使用实体框架日志记录,我发现了一些查询,这些查询会返回实体的所有列(当然,在回顾 ef 查询时很明显就是这种情况)。很多低效率和性能问题。
通过修改查询来解决此问题很简单,但我不知道如何将结果从存储库传递回应用程序。
- 通过智商打破了我对分离的一切 信念
- 如果我想从存储库层传递一个强类型对象(只包含我想要的列),它需要我的存储库层"知道"包含 dto 类的其他项目(通过项目引用),这对我来说真的很糟糕。它创建不应存在的程序集依赖项。
- 无法返回匿名类型
我的答案可能是在上述一项上妥协。但我想先问问社区。
*我知道将存储库模式与ORM一起使用是一个非常有争议的话题,但我真的想远离这场辩论。我继承了这个应用程序,所以这艘船在应用程序架构方面航行了。我只需要一个实用的解决方案,我可以与我现有的架构一起实施,无论我必须接受什么妥协。
通过智商打破了我对分离的一切看法
这是正确的答案。 存储库不必知道应用程序可能需要的每个可能的查询。 将所有查询逻辑放在存储库中实际上是打破存储库和应用程序其他部分之间职责分离的原因。
传递强类型对象...从存储库层,它需要我的存储库层"了解"其他项目
DTO 是应用程序组件之间"协定"的一部分。 所有应用程序组件都必须具有对服务协定的共享引用,包括 .NET 接口定义和传入和传出接口方法的数据类型。
通过智商打破了我对分离的一切看法
我相信这是你问题的根源。 :)
我看到的反对使用 IQueryable的典型论点是,人们认为存储库的使用者不需要了解架构,返回 IQueryable 会"泄漏"EF 和模式。我已经看到足够多的尝试来抽象 EF 和架构,这些尝试总是导致复杂的方法接受表达式/Funcs(泄漏 EF-isms,因为调用方仍然必须传递 EF 可以接受的表达式树)或效率极低的存储库散落着数百个方法来迎合每个单独的使用者。
我对这个假设的反驳是,存储库的作用仅仅是抽象数据访问的实现,以便我可以有效地对消费者进行单元测试。通过利用返回 IQueryable(并且仅返回 IQueryable)的存储库,我可以模拟一个存储库以返回适合测试的任何一个或多个实体,以涵盖不属于存储库的业务逻辑。 使用 IQueryable 的优点是你最终会得到非常精简的存储库,我的通常只有一个 Create 方法、适用于该存储库使用者的实体的读取方法,以及在我使用软删除或历史架构的情况下使用 Delete 方法。到目前为止,我的存储库可能会提供一个"ById"类型的方法,因为它通常用于更新等,但这种方法也返回 IQueryable。如果我正在执行更新,则使用者会在包含可能也更新的任何相关实体后调用.Single()
。其他代码可能只是执行.Any
检查,或者只是出于某种目的从该实体中选择几个值。该模式具有适应性。
通过利用 IQueryable,您可以毫不犹豫地希望有效地从实体或其层次结构中选择几个字段。它易于使用,易于理解,并导致高效,快速的查询。受信任的内部调用方可以利用 Linq 方法使用.Any
、.Count
、分页、排序、过滤等,同时只Select
满足其特定要求所需的数据。存储库处理核心级筛选规则,例如授权、软删除处于活动状态检查等。 它可能被滥用并导致对数据库的丑陋、昂贵的打击,但您编写的任何代码也可以。复杂的解决方案更难理解,当它们似乎不符合未来的需求时,它们会导致黑客攻击或修改,从而破坏某些东西。具有数十种专用方法的存储库会导致大量重复,因为开发人员在需要额外 1 列或少一列时不会费心筛选不断增长的 # 方法。在我看来,更糟糕的是,这些方法将实体作为仅部分填充的容器返回。实体应始终表示真实、完整的数据状态,因为任何接受实体的方法都不必怀疑该实体的实际完整性。
当面对编写会被其他人触及的代码时,我专注于使其易于理解,易于发现滥用并纠正它们。我相信好的代码和架构应该让错误容易发现和修复,而不是试图犯很难犯的错误。