Jas*_*ord 22 asp.net repository repository-design repository-pattern
所以我在应用程序中实现了存储库模式,并且在我对模式的理解中遇到了两个"问题":
查询 - 我已经读过使用存储库时不应该使用IQueryable的响应.但是,显而易见的是,您希望每次调用方法时都不返回完整的对象列表.应该实施吗?如果我有一个名为List的IEnumerable方法,那么IQueryable的一般"最佳实践"是什么?应该/不应该有哪些参数?
标量值 - 最好的方法(使用存储库模式)返回单个标量值而不必返回整个记录是什么?从性能的角度来看,在整行上只返回一个标量值会不会更有效率?
pfr*_*ies 34
严格地说,Repository提供了用于获取/放置域对象的集合语义.它提供了一个围绕实现实现(ORM,手动,模拟)的抽象,以便域对象的使用者与这些细节分离.在实践中,存储库通常抽象对实体的访问,即具有标识的域对象,并且通常是持久的生命周期(在DDD风格中,存储库提供对聚合根的访问).
存储库的最小接口如下:
void Add(T entity);
void Remove(T entity);
T GetById(object id);
IEnumerable<T> Find(Specification spec);
Run Code Online (Sandbox Code Playgroud)
虽然你会看到命名差异和添加Save/SaveOrUpdate语义,但上面是"纯粹"的想法.您将获得ICollection添加/删除成员以及一些查找程序.如果您不使用IQueryable,您还会在存储库中看到finder方法,如:
FindCustomersHavingOrders();
FindCustomersHavingPremiumStatus();
Run Code Online (Sandbox Code Playgroud)
在此上下文中使用IQueryable存在两个相关问题.第一种可能是以域对象的关系形式向客户泄露实现细节,即违反Demeter法则.第二个是存储库获取可能不属于域对象存储库的查找职责,例如,查找与所请求的域对象相比较少于相关数据的投影.
此外,使用IQueryable"中断"模式:具有IQueryable的存储库可能提供也可能不提供对"域对象"的访问.IQueryable为客户提供了许多关于在最终执行查询时将实现的内容的选项.这是关于使用IQueryable的辩论的主旨.
关于标量值,您不应使用存储库来返回标量值.如果你需要一个标量,你通常会从实体本身得到它.如果这听起来效率低,那就是,但您可能不会注意到,这取决于您的负载特性/要求.如果您需要域对象的备用视图,由于性能原因或者您需要合并来自许多域对象的数据,您有两种选择.
1)使用实体的存储库查找指定的实体并将项目/地图映射到展平视图.
2)创建一个finder接口,专门用于返回一个新的域类型,该类型封装了您需要的展平视图.这不是一个存储库,因为没有Collection语义,但它可能会使用现有的存储库.
如果使用"纯"存储库访问持久化实体,则需要考虑的一件事是,您会损害ORM的一些好处.在"纯"实现中,客户端无法提供域对象如何使用的上下文,因此您无法告诉存储库:'嘿,我只是要更改customer.Name属性,所以不要得到那些急切加载的参考资料.另一方面,问题是客户是否应该知道这些东西.这是一把双刃剑.
至于使用IQueryable,大多数人似乎都习惯于"打破"模式以获得动态查询组合的好处,特别是对于诸如分页/排序之类的客户端职责.在这种情况下,您可能有:
Add(T entity);
Remove(T entity);
T GetById(object id);
IQueryable<T> Find();
Run Code Online (Sandbox Code Playgroud)
然后,您可以取消所有这些自定义Finder方法,这些方法会随着查询要求的增长而使存储库变得混乱.
为了回应@lordinateur,我不太喜欢指定存储库接口的事实方法.
因为解决方案中的接口要求每个存储库实现至少需要一个Add,Remove,GetById等.现在考虑一个通过存储库的特定实例保存没有意义的场景,您仍然需要实现其余的方法使用NotImplementedException或类似的东西.
我更喜欢拆分我的存储库接口声明,如下所示:
interface ICanAdd<T>
{
T Add(T entity);
}
interface ICanRemove<T>
{
bool Remove(T entity);
}
interface ICanGetById<T>
{
T Get(int id);
}
Run Code Online (Sandbox Code Playgroud)
因此,SomeClass实体的特定存储库实现可能如下所示:
interface ISomeRepository
: ICanAdd<SomeClass>,
ICanRemove<SomeClass>
{
SomeClass Add(SomeClass entity);
bool Remove(SomeClass entity);
}
Run Code Online (Sandbox Code Playgroud)
让我们退后一步,看看为什么我认为这比在一个通用接口中实现所有CRUD方法更好.
有些对象的要求与其他对象不同.可能不会删除客户对象,无法更新PurchaseOrder,只能创建ShoppingCart对象.当使用通用IRepository接口时,这显然会导致实现中出现问题.
那些实现反模式的人通常会实现他们的完整接口,然后会为他们不支持的方法抛出异常.除了不同意众多OO原则之外,这打破了他们能够有效使用他们的IRepository抽象的希望,除非他们也开始在其上设置方法,以确定是否支持给定对象并进一步实现它们.
此问题的一个常见解决方法是转移到更精细的接口,如ICanDelete,ICanUpdate,ICanCreate等等.这解决了在OO原则方面出现的许多问题,同时也大大减少了代码重用的数量.在大多数情况下,人们将无法再使用Repository具体实例.
我们都不喜欢一遍又一遍地编写相同的代码.然而,作为建筑接缝的存储库合同是扩大合同以使其更通用的错误位置.
从这篇文章中可以看出这些摘录,你可以在评论中阅读更多的讨论.
归档时间: |
|
查看次数: |
7268 次 |
最近记录: |