我正在建立一个存储库,我在很多地方看到过2个不在存储库外暴露IQueryable的原因。
1)首先是因为不同的LINQ提供者可能表现不同,这种差异应该包含在存储库中。
2)第二个是阻止服务级别开发人员修改数据库查询,以免意外导致性能问题。
我想问题2只能通过将所有查询逻辑保留在存储库中并且不允许任何形式的外部查询构建来防止?但这对我来说似乎有点不切实际。
问题1似乎可以通过使用数据对象模式来解决。
e.g。 public IEnumerable<T> FindBy(Query query)
我的问题是,为什么我不仅仅传递一个lambda表达式,因为它与提供者无关,似乎为我提供了与查询对象相同的功能,以及相同的分离级别?
e.g。 public IEnumerable<T> FindBy(Expression<Func<T,bool>> predicate)
有什么理由不这样做吗?它违反了一些规则吗?最佳做法?我应该知道什么?
答案 0 :(得分:21)
IQueryable<T>
。在您编写另一部分“存储库代码”之前,您将从阅读Ayende的文章Architecting in the Pit of Doom - The Evils of the Repository Abstraction Layer
中获益匪浅来自Generic List of OrderBy Lambda的另一个问题的所有代码除了使用不必要且不熟悉的抽象掩盖现有的有效API之外,都无法做任何其他事情。
关于您的两个问题,
LINQ提供程序的行为方式不同,但只要您传递的谓词可以由LINQ提供程序处理,这就无关紧要了。否则,您仍会遇到同样的问题,因为您传递的是Expression
,最终无论如何都会传递给IQueryable
。如果IQueryProvider
实现无法处理您的谓词,则它无法处理您的谓词。 (如果您需要在进一步过滤之前进行评估而无法翻译,则可以随时调用ToList()
。
修改查询可能会导致性能问题,但更有可能暴露出急需的功能。此外,次优LINQ查询引起的性能问题可能比通过提取比您需要的更多记录而产生的性能问题明显更不利,以避免暴露IQueryable
或系统地过滤任何数据访问逻辑通过膨胀的抽象级别实际上没有做任何事情(第一个威胁更重要)。一般来说,这不会成为问题,因为大多数领先的LINQ提供商都会在翻译过程中优化您的查询逻辑。
如果要从前端隐藏查询逻辑,请不要尝试创建通用存储库。使用实际的业务特定方法封装查询。现在,我可能会弄错,但我假设您使用存储库模式的灵感来自Domain Driven Design。如果是这种情况,那么使用存储库的原因是允许您创建一个主要关注域模型的持久性无知域。但是,使用这种通用存储库除了将语义从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<T>
的好处(而不是IQueryable<T>
)吗?
另外,考虑到使用通用方法,实际上并不是封装查询逻辑。您最终会在外部构建它,这将导致更多不必要的代码。
*关于建议不要使用IQueryable<T>
的资源的另一个注意事项是,值得查看其发布日期。曾经有一段时间LINQ提供程序的可用性非常有限(早期的EF和LINQ-to-SQL)。那时暴露IQueryable<T>
将导致与一些Microsoft ORM更受欢迎的替代品不兼容(LINQ-to-NHibernate早已实现)。目前,LINQ支持在严重的ORM .NET库中几乎无处不在
答案 1 :(得分:0)
与上面的回答相反,单元测试是一个明显的反对点。返回一个简单的泛型IQueryable
对于任何有权调用它的客户端都是抽象的泄漏。因此,存储库方法没有任何限制,因此在单元测试中无需声明。
IQueryable
的使用几乎可以肯定取决于应用程序本身的上下文以及体系结构。例如,如果您一个人在从事个人项目,则可以放心地承担使用这种不受限制的权力的责任,而不会对您本人造成任何伤害。但是,如果您正在为将代码投入生产的公司开发应用程序,那么肯定有更多的利益相关者。在这种情况下,您可能正在数据访问层中工作,并尝试针对其他开发人员对您代码的使用进行编码。现在,您需要控制进出的内容,以使它们不会破坏您的代码,也不会破坏它们的代码。这是激励,而单元测试则是执行。
话虽如此,不要简单地使用IQueryable<T>
。 使用对您的应用程序有意义的内容。