Repository模式的最佳实践(Repository Pattern Best Practice

2019-08-03 00:00发布

所以我在一个应用程序中实现存储库模式和跨在我的模式的理解两个“问题”来了:

  1. 查询 - 我读过使用库时的IQueryable不应该被用来响应。 然而,很明显,你会想,让你没有返回的对象每次调用一个方法时,一个完整的列表。 是否应该实施? 如果我有一个名为List IEnumerable的方法,什么是一般的“最佳实践”为一个IQueryable? 应该/不应该它有哪些参数?

  2. 标量值 - 什么是最好的方式(使用存储库模式),而不必返回整个记录返回单,标值? 从性能的角度来看,那岂不是更有效地在整个行只返回一个标量值?

Answer 1:

严格地说,一个库提供集合的语义来获得/把域对象。 它提供了在你的物化执行(ORM,手卷,模拟),使得域对象的消费者从这些细节解耦的抽象。 在实践中,通常存储库访问抽象的实体,即域对象与身份,通常持续的生命周期(在DDD的味道,一个库提供了总根访问)。

对储存的最小界面如下:

void Add(T entity);
void Remove(T entity);
T GetById(object id);
IEnumerable<T> Find(Specification spec);

虽然你会看到不同的命名,并加入保存/ SaveOrUpdate语义的,上面是“纯粹”的想法。 你得到的ICollection的添加/删除成员加上一些发现者。 如果你不使用IQueryable的,你还可以看到像仓库finder方法:

FindCustomersHavingOrders();
FindCustomersHavingPremiumStatus();

有与在这种情况下使用IQueryable的两个相关的问题。 首先是在域对象的关系,即违反迪米特法则的形式泄漏的实施细则,以客户的潜力。 第二是资源库获得可能不属于域对象找到责任仓库正确,如发现少约比相关的数据请求的域对象的预测。

另外,使用的IQueryable“断”的图案:具有IQueryable的阿库可以或可以不提供对“域对象”。 IQueryable的给客户了很多关于当最终执行查询什么将物化的选项。 这是关于使用IQueryable的辩论的主旨。

关于标量值,你不应该使用一个仓库返回标量值。 如果你需要一个标量,你通常会从实体本身得到这个。 如果这听起来很低效,它是,但你可能没有注意到,这取决于你的负载特性/要求。 在你需要的域对象的替代的观点,因为性能的原因还是因为你需要从多个域对象合并数据,你有两个选择的情况。

1)使用实体的存储库中找到指定的实体和项目/地图的平面视图。

2)创建专用于返回封装你所需要的平面视图的一个新的域类型的取景器接口。 这不会是一个仓库,因为就没有收藏的语义,但它可能会使用在幕后现有资源库。

有一点要考虑,如果你使用一个“纯粹”访问资源库中坚持实体是你妥协一些的ORM的好处。 在“纯”的实现,客户端无法提供的域对象将被如何使用的上下文,所以你不能告诉库:“哎,我只是要改变customer.Name属性,所以不要“T打扰让那些急于加载引用。” 在另一面,问题是客户是否应该知道的东西。 这是一把双刃剑。

至于使用IQueryable的,大多数人似乎坦然面对“破”的模式来获得动态查询组合的好处,特别是对客户的责任像分页/排序。 在这种情况下,你可能有:

Add(T entity);
Remove(T entity);
T GetById(object id);
IQueryable<T> Find();

然后你可以与所有这些自定义的搜索方法,这真是混乱的存储库作为你的查询需求的增长做了。



Answer 2:

为了应对@lordinateur我真的不喜欢到指定仓库接口的事实上的方式。

因为在解决方案中的接口要求每个仓库实现至少需要一个添加,删除,GetById等。现在考虑这样一个场景,它没有任何意义通过一个存储库的特定实例保存,你还是要落实其余的方法与NotImplementedException或类似的东西。

我喜欢我的分裂库接口声明,如下所示:

interface ICanAdd<T>
{
    T Add(T entity);
}

interface ICanRemove<T>
{
    bool Remove(T entity);
}

interface ICanGetById<T>
{
    T Get(int id);
}

因此,对于一个SomeClass的实体特定仓库实现可能类似于以下内容:

interface ISomeRepository
    : ICanAdd<SomeClass>, 
      ICanRemove<SomeClass>
{
    SomeClass Add(SomeClass entity);
    bool Remove(SomeClass entity);
}

让我们退后一步,看看为什么我认为这是比实现一个通用接口,所有的CRUD方法一个更好的做法。

某些对象比其他人不同的要求。 一位顾客对象可能不被删除,了一个采购订单无法更新,只能创建一个ShoppingCart对象。 当一个人使用通用IRepository接口这显然会导致执行上的问题。

那些经常实施反模式将实现其全部接口,那么将引发异常的,他们不支持的方法。 除了与众多OO原则不同意这打破他们能够使用他们的IRepository抽象有效,除非他们也开始把它用于与否给出对象的方法,希望支持和​​进一步落实。

一个常见的解决方法这个问题是迁移到更精细的接口,如ICanDelete,ICanUpdate,ICanCreate等等等等,这在工作时周围很多,在OO的原则方面如雨后春笋般出现的问题,也大大减少了代码重用的数量被视为大多数时间一个将无法使用存储库的具体情况了。

我们谁都喜欢一遍又一遍地编写相同的代码。 然而,由于是一个建筑接缝合同库是放错了地方扩大合同,使其更通用。

这些exerpts从被shamelesly采取了这个帖子 ,你也可以在评论阅读更多的讨论。



Answer 3:

对于1:据我可以看到它,它不是IQuerable本身正在从仓库返回的问题。 存储库的一点是,它应该是一个包含所有数据的对象。 所以,你可以要求数据存储库。 如果你有一个以上的对象需要相同的数据,资料库的工作是缓存数据,因此你的资料库的两个客户端会得到相同的情况下 - 所以,如果一个客户端更改属性,对方就会看到, ,监守他们都指向同一个实例。

如果存储库实际上是的LINQ提供程序本身,然后,将适合权。但大多数人只是让LINQ到SQL供应商的IQuerable正好穿过,这实际上绕过库的责任。 这样仓库是不是一个仓库处的全部,至少根据我的理解和图案的使用。

关于2:当然是更高的性能,有效的,只是从数据库中比整个记录返回一个值。 但是,使用一个仓库模式,你就不会在所有返回的记录,你会回来的业务对象。 因此,应用程序逻辑不应领域关注自身,但域对象。

但如何更有效它返回比较完整的域对象的单个值? 你可能无法衡量,如果你的数据库架构合理明确的区别。

这是一个很多更重要的是有干净的,易于理解的代码 - 而不是微观性能优化了前面。



文章来源: Repository Pattern Best Practice