【问题标题】:Repository / IQueryable / Query Object存储库/IQueryable/查询对象
【发布时间】:2012-08-11 11:15:56
【问题描述】:

我正在构建一个存储库,我在很多地方都看到了不将 IQueryable 暴露在存储库之外的两个原因。

1) 首先是因为不同的 LINQ 提供程序的行为可能不同,而这种差异应该包含在存储库中。

2) 二是防止服务级别开发人员修改数据库查询,以免意外导致性能问题。

我想问题 2 只能通过将所有查询逻辑保留在存储库中并且不允许任何形式的外部查询构建来防止?但这对我来说似乎有点不切实际。

问题 1 似乎可以通过使用数据对象模式来解决。

例如public IEnumerable<T> FindBy(Query query)

我的问题是,为什么我不只是传递一个 lambda 表达式,因为它独立于提供程序,似乎为我提供了与查询对象相同的功能和相同级别的分离?

例如public IEnumerable<T> FindBy(Expression<Func<T,bool>> predicate)

有什么理由不这样做吗?它是否违反了某些规则?最佳实践?我应该知道什么?

【问题讨论】:

  • 我有一个 repo 系统,它主要使用这些 finder lambda 表达式,并且运行良好。我做的另一件事是公开一个允许用户指定finderorderbys 以及skiptake 的QuerySpec 类。
  • 我对你的 cmets 感兴趣,我的新问题 stackoverflow.com/questions/11979140/…

标签: c# repository-pattern iqueryable objectquery


【解决方案1】:

只需返回 IQueryable<T>

在你再写一点“存储库代码”之前,阅读 Ayende 的文章Architecting in the Pit of Doom - The Evils of the Repository Abstraction Layer你会受益匪浅

毫无疑问,您的方法会增加不必要的复杂性。

Generic List of OrderBy Lambda 的另一个问题的所有代码除了用不必要和不熟悉的抽象来掩盖现有的有效 API 外,没有做任何事情。

关于您的两个问题,

  1. LINQ 提供程序的行为确实不同,但只要您传递的谓词可以由 LINQ 提供程序处理,这无关紧要。否则,您仍然会遇到同样的问题,因为您传递了一个Expression,无论如何它最终都会传递给IQueryable。如果IQueryProvider 实现不能处理你的谓词,那么它就不能处理你的谓词。 (如果您需要在无法翻译的进一步过滤之前进行评估,您可以随时致电ToList())。

  2. 修改查询可能会导致性能问题,但更可能会暴露急需的功能。此外,与为避免暴露IQueryable 或通过系统地过滤任何数据访问而提取比您需要的多得多的记录所导致的性能问题相比,次优 LINQ 查询所导致的性能问题的危害性可能要小得多通过臃肿的抽象级别实现逻辑,这些抽象级别实际上并没有做任何事情(第一个威胁更重要)。一般来说,这不是问题,因为大多数领先的 LINQ 提供商都会在翻译过程中优化您的查询逻辑。

如果您想从前端隐藏查询逻辑,请不要尝试创建通用存储库。用实际的业务特定方法封装查询。现在,我可能弄错了,但我假设您对存储库模式的使用受到领域驱动设计的启发。如果是这种情况,那么使用存储库的原因是允许您创建一个主要关注域模型的持久性无知域。但是,使用这种通用存储库只会将语义从Create Read Update Delete 更改为Find Add Remove Save。那里没有嵌入任何真正的商业知识。

考虑一个的意义(和可用性)

interface IPersonRepository 
{ 
     Person GetById(int id);
     IEnumerable<Person> FindByName(string firstName, string lastName);
}

对比

interface IRepository<T> { 
     IEnumerable<T> FindBy(Query<T> query);
}

此外,您能否真正指出使用IRepository&lt;T&gt; 的好处(而不是IQueryable&lt;T&gt;)?

另外,考虑到使用通用方法,您实际上根本没有封装查询逻辑。你最终在外部构建它,这将导致更多额外的不必要的代码。

*关于建议不要使用IQueryable&lt;T&gt; 的资源的另一个注意事项是,值得查看它们的发布日期。曾经有一段时间,LINQ 提供程序的可用性非常有限(对于早期的 EF 和 LINQ-to-SQL)。那时暴露IQueryable&lt;T&gt; 将导致与Microsoft ORM 的一些更流行的替代品不兼容(LINQ-to-NHibernate 早已实现)。目前,LINQ 支持在严肃的 ORM .NET 库中几乎无处不在

【讨论】:

  • 使用存储库模式的一个好处是数据源管理(上下文等)可以封装在存储库中。否则,使用查询的客户端必须将上下文传递给查询本身,从而使客户端“知道太多”。此外,存储库非常适合容纳特定于上下文的详细信息或写入操作。
  • 作为开发人员,我们永远不应忘记数据库,EF 不是灵丹妙药。如果需要在 NoSQL 中存储 UnitOfWork 数据或从 SAP 服务获取数据,IQueryable 将在哪里?或者如何使用存储过程进行优化?文章想一想developersdev.blogspot.ru/2013/12/…
  • +1 指出,至少在 DDD 上下文中,存储库的焦点是域模型。理解这一点应该有助于减少在存储库实现中包含特定于模型的语法(即您的“FindByName”示例)的焦虑。根据我的经验,它会生成更易于理解的代码。
猜你喜欢
  • 2013-01-21
  • 2018-09-09
  • 1970-01-01
  • 2012-08-10
  • 1970-01-01
  • 2011-03-15
  • 1970-01-01
  • 1970-01-01
  • 2010-11-03
相关资源
最近更新 更多