每个实体的通用存储库还是特定存储库? [英] Generic Repository or Specific Repository for each entity?

查看:79
本文介绍了每个实体的通用存储库还是特定存储库?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景



在我工作的公司,我被责令更新旧的MVC应用并为SQL数据库实现存储库模式。我已经使用Entity Framework Database-First创建了数据库上下文,并获得了23个实体。



第一个问题



我需要为每个实体创建一个存储库还是为上下文实现一个通用存储库?我之所以这样问是因为我在搜索互联网时发现了以下内容:


每个域一个存储库



您应该将存储库视为内存中域对象的集合。如果要构建名为Vega的应用程序,则不应有如下存储库:



公共类VegaRepository {}



相反,每个域类都应该有一个单独的存储库,例如OrderRepository,ShippingRepository和ProductRepository。



来源:用Mosh编程:4个常见的错误存储库模式




第二个问题



是否通用实体框架数据库优先的知识库工作?这是因为我在搜索互联网时发现了以下内容:


实体框架



请注意,仅当您具有先使用代码映射的POCO时,存储库模式才有用。否则,您将改为用实体破坏抽象(=则存储库模式不是很有用)。您可以关注本文



来源: CodeProject:正确的存储库模式



解决方案

首先,如果您使用的是完整的ORM(例如Entity Framework或NHibernate),则应避免实现存储库和工作单元的附加层。
这是因为;

如果是EF,则您的 DbContext 是工作单位,而 DbSet 是通用存储库。对于NHibernate,它本身就是 ISession

在相同的现有存储库上构建通用存储库的新包装是重复的工作。为什么重新发明轮子?



但是,有人认为直接在调用代码中使用ORM存在以下问题:


  1. 由于缺乏关注点分离,它使代码复杂一些。

  2. 数据访问代码在业务逻辑中合并。结果,冗余的复杂查询逻辑散布在多个地方。

  3. 由于许多ORM对象在调用代码中被内联使用,因此很难对代码进行单元测试。

  4. ORM仅公开通用存储库,这会导致下面提到的许多问题。

除了上述所有内容外,讨论的另一个问题是如果我们决定将来更改ORM。这不是决策时的重点,因为:




  • 您很少更改ORM,多数情况下从不更改– YAGNI。

  • 如果您更改ORM,则无论如何都必须进行巨大的更改。您可以通过在某物中封装完整的数据访问代码(而不仅仅是ORM)来最大程度地减少工作量。我们将在下面讨论某事



考虑到上述四个问题,可能有必要创建存储库即使您使用的是完整的ORM,也要根据情况决定



即使在这种情况下,也必须避免使用通用存储库。



为什么通用存储库是反模式?


  1. 存储库是正在建模的域的一部分,并且该域不是通用的。


    • 并非每个实体都可以删除。

    • 并非每个实体都可以添加

    • 并非每个实体都有一个存储库。

    • 查询之间存在很大差异;

    • 对于 GetById(),标识符类型可能不同。

    • 不可能更新特定字段(DML)。


  2. 通用查询机制是ORM的职责。


    • 大多数ORM都公开了与通用存储库非常相似的实现。

    • 存储库应实现SPECIFIC查询


  3. 无法使用组合键。

  4. 无论如何,它会泄漏服务中的DAL逻辑。


    • 需要从服务层提供的条件作为接受条件。如果这是ORM特定的类,则会将ORM泄漏到服务中。


您阅读了这些内容( 1 2 3 4 5 )文章解释了为什么通用存储库是反模式。这个其他答案通常讨论存储库模式。



所以,我建议:




  • 根本不使用存储库,直接在调用代码中使用ORM。

  • 如果必须使用存储库,则不要尝试使用通用存储库来实现所有功能。

    相反,可以选择创建非常简单且很小的通用存储库作为抽象基类。或者,如果ORM允许,则可以将ORM公开的通用存储库用作基础存储库。

    根据需要实施具体存储库,并从通用存储库派生所有这些存储库。将具体的存储库暴露给调用代码。

    这样,您可以获得通用存储库的所有优点,而绕过了它的缺点。

    即使非常罕见,这也有助于将来将ORM转换为ORM代码在DAL /存储库中清晰地抽象了。请理解,切换ORM并不是数据访问层或存储库的主要目标。



无论如何,请勿将通用存储库暴露给



此外,请勿从具体存储库返回 IQueryable 。这违反了存储库存在的基本目的-抽象数据访问。通过将 IQueryable 暴露在存储库之外,许多数据访问决策会泄漏到调用代码中,并且存储库失去了对其的控制。


我需要为每个实体创建存储库还是为上下文实现通用存储库


以上建议,为每个实体创建存储库是更好的方法。请注意,存储库最好返回域模型而不是实体。但这是另一个讨论的话题。


通用存储库是否适用于EF Database First?



Background

At the company I work for I have been ordered to update an old MVC app and implement a repository pattern for a SQL database. I have created the context of the database using Entity Framework Database-First and got 23 entities.

The first question

Do I need to create a repository for each entity or implement a generic repository for the context? I'm asking this because I have found following while searching internet:

One repository per domain

You should think of a repository as a collection of domain objects in memory. If you’re building an application called Vega, you shouldn’t have a repository like the following:

public class VegaRepository {}

Instead, you should have a separate repository per domain class, like OrderRepository, ShippingRepository and ProductRepository.

Source: Programming with Mosh: 4 Common Mistakes with the Repository Pattern

The second question

Does a generic repository work for Entity Framework Database-First? This is because I have found following while searching internet:

Entity framework

Do note that the repository pattern is only useful if you have POCOs which are mapped using code first. Otherwise you’ll just break the abstraction with the entities instead (= the repository pattern isn’t very useful then). You can follow this article if you want to get a foundation generated for you.

Source: CodeProject: Repository pattern, done right

解决方案

To begin with, if you are using full ORM like Entity Framework or NHibernate, you should avoid implementing additional layer of Repository and Unit Of Work. This is because; the ORM itself exposes both Generic Repository and Unit Of Work.
In case of EF, your DbContext is Unit Of Work and DbSet is Generic Repository. In case of NHibernate, it is ISession itself.
Building new wrapper of Generic Repository over same existing one is repeat work. Why reinvent the wheel?

But, some argue that using ORM directly in calling code has following issues:

  1. It makes code little more complicated due to lack of separation of concerns.
  2. Data access code is merged in business logic. As a result, redundant complex query logic spread at multiple places; hard to manage.
  3. As many ORM objects are used in-line in calling code, it is very hard to unit test the code.
  4. As ORM only exposes Generic Repository, it causes many issues mentioned below.

Apart from all above, one other issue generally discussed is "What if we decide to change ORM in future". This should not be key point while taking decision because:

  • You rarely change ORM, mostly NEVER – YAGNI.
  • If you change ORM, you have to do huge changes anyway. You may minimize efforts by encapsulating complete data access code (NOT just ORM) inside something. We will discuss that something below.

Considering four issues mentioned above, it may be necessary to create Repositories even though you are using full ORM - This is per case decision though.

Even in that case, Generic Repository must be avoided. It is considered an anti-pattern.

Why generic repository is anti-pattern?

  1. A repository is a part of the domain being modeled, and that domain is not generic.
    • Not every entity can be deleted.
    • Not every entity can be added
    • Not every entity has a repository.
    • Queries vary wildly; the repository API becomes as unique as the entity itself.
    • For GetById(), identifier types may be different.
    • Updating specific fields (DML) not possible.
  2. Generic query mechanism is the responsibility of an ORM.
    • Most of the ORMs expose an implementation that closely resemble with Generic Repository.
    • Repositories should be implementing the SPECIFIC queries for entities by using the generic query mechanism exposed by ORM.
  3. Working with composite keys is not possible.
  4. It leaks DAL logic in Services anyway.
    • Predicate criteria if you accept as parameter needs to be provided from Service layer. If this is ORM specific class, it leaks ORM into Services.

I suggest you read these (1, 2, 3, 4, 5) articles explaining why generic repository is an anti-pattern. This other answer discusses about Repository Pattern in general.

So, I will suggest:

  • Do NOT use repository at all, directly use ORM in your calling code.
  • If you have to use repository, then do not try to implement everything with Generic Repository.
    Instead, optionally create very simple and small Generic Repository as abstract base class. OR you can use Generic Repository exposed by your ORM as base repository if ORM allows it.
    Implement Concrete Repositories as per your need and derive all them from Generic Repository. Expose concrete repositories to calling code.
    This way you get all the good of generic repository still bypassing its drawbacks.
    Even though very rare, this also helps switching ORM in future as ORM code is cleanly abstracted in DAL/Repositories. Please understand that switching ORM is not a primary objective of Data Access Layer or Repository.

In any case, do not expose Generic Repository to calling code.

Also, do not return IQueryable from concrete repositories. This violates basic purpose of existence of Repositories - To abstract data access. With exposing IQueryable outside the repository, many data access decisions leak into calling code and Repository lose the control over it.

do I need to create a repository for each entity or implement a generic repository for the context

As suggested above, creating repository for each entity is better approach. Note that, Repository should ideally return Domain Model instead of Entity. But this is different topic for discussion.

does a generic repository works for EF Database First?

As suggested above, EF itself exposes Generic Repository. Building one more layer on it is useless. Your image is saying the same thing.

这篇关于每个实体的通用存储库还是特定存储库?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆