Entity Framework에서 리포지토리 패턴을 사용하지 않아야하는 이유는 무엇입니까?


203

면접 중에 리포지토리 패턴이 Entity Framework와 같은 ORM에서 작동하기에 적합한 패턴이 아닌 이유를 설명하라는 요청을 받았습니다. 왜 이런 경우입니까?


60
그것은 속임수 질문이었다
Omu

2
인터뷰 대상자에게 Microsoft가 엔티티 프레임 워크를 보여주는 동안 저장소 패턴을 매우 자주 사용한다고 대답했을 것입니다. | .
Laurent Bourgault-Roy

1
면접관이 좋은 생각이 아닌 이유는 무엇입니까?
밥 혼

3
재미있는 사실은 Google에서 "리포지토리 패턴"을 검색하면 주로 Entity Framework와 관련된 결과와 EF에서 패턴을 사용하는 방법을 제공한다는 것입니다.
Arseni Mourzenko

2
ayende의 블로그 ayende.com/blog를 확인하십시오 . 내가 아는 것에 기초하여, 그는 리포지토리 패턴을 사용했지만 결국 쿼리 객체 패턴을 위해 포기했다
Jaime Sangcap

답변:


99

리포지토리 패턴이 Entity Framework에서 작동하지 않는 이유는 없습니다. 리포지토리 패턴은 데이터 액세스 계층에 적용 할 추상화 계층입니다. 데이터 액세스 계층은 순수 ADO.NET 저장 프로 시저부터 Entity Framework 또는 XML 파일에 이르기까지 모든 것이 될 수 있습니다.

다른 소스 (데이터베이스 / XML / 웹 서비스)에서 데이터를 가져 오는 대규모 시스템에서는 추상화 계층을 사용하는 것이 좋습니다. 이 시나리오에서는 리포지토리 패턴이 잘 작동합니다. 나는 Entity Framework가 장면 뒤에서 일어나는 일을 숨길 수있는 추상화라고 생각하지 않습니다.

데이터 액세스 계층 방법으로 Entity Framework와 함께 리포지토리 패턴을 사용했지만 아직 문제가 없습니다.

DbContext리포지토리 로 추상화하는 것의 또 다른 장점 은 단위 테스트 가능성 입니다. 당신은 할 수 있습니다 IRepository인터페이스를 사용하여 2 개 구현, 하나 (실제 저장소)이되는 DbContext데이터베이스와 두 번째 이야기를 FakeRepository메모리 개체 / 조롱 데이터를 반환 할 수 있습니다. 이렇게하면 IRepository단위를 테스트 할 수 있으므로를 사용하는 코드의 다른 부분이 IRepository됩니다.

public interface IRepository
{
  IEnumerable<CustomerDto> GetCustomers();
}
public EFRepository : IRepository
{
  private YourDbContext db;
  private EFRepository()
  {
    db = new YourDbContext();
  }
  public IEnumerable<CustomerDto> GetCustomers()
  {
    return db.Customers.Select(f=>new CustomerDto { Id=f.Id, Name =f.Name}).ToList();
  }
}
public MockRepository : IRepository
{
  public IEnumerable<CustomerDto> GetCustomers()
  {
    // to do : return a mock list of Customers
    // Or you may even use a mocking framework like Moq
  }
}

이제 DI를 사용하여 구현을 얻습니다.

public class SomeService
{
  IRepository repo;
  public SomeService(IRepository repo)
  {
     this.repo = repo;
  }  
  public void SomeMethod()
  {
    //use this.repo as needed
  }    
}

3
나는 내가 또한 EF와 저장소 패턴과 협력하고, 작동하지 않습니다 말했다하지 못했지만, 오늘 나는 IT는 데이터베이스 사용하여 데이터베이스와 패턴, 응용 프로그램을 사용하는 것이 좋지 않은 이유를 질문했다

2
Ok, 이것이 가장 인기있는 답변이기 때문에 정답으로 선택했습니다

65
가장 인기있는 == 정확한 마지막 시간은 언제입니까?
HDave

14
DbContext는 이미 저장소이며 저장소는 낮은 수준의 추상화입니다. 다른 데이터 소스를 추상화하려면이를 나타내는 오브젝트를 작성하십시오.
Daniel Little

7
ColacX. 컨트롤러 레이어에서 바로 DBcontext를 시도해 보았고 repo 패턴으로 되돌 렸습니다. Repo 패턴을 사용하여 단위 테스트는 지속적으로 실패한 대규모 DbContext 조롱에서 나왔습니다. EF는 EF 뉘앙스를 위해 사용하기가 어렵고 취 성적이며 시간이 많이 소요되었습니다. 우리는 이제 레포의 작은 간단한 모형을 가지고 있습니다. 코드가 깨끗합니다. 작업 분리가 더 명확합니다. 나는 더 이상 EF가 이미 레포 패턴이며 단위 테스트가 가능하다는 군중에 동의하지 않습니다.
Rhyous

434

Entity Framework에서 리포지토리 패턴을 사용하지 않는 가장 좋은 이유는 무엇입니까? Entity Framework는 이미 리포지토리 패턴을 구현합니다. DbContextUoW (작업 단위)이며 각각 DbSet저장소입니다. 이 위에 다른 계층을 구현하는 것은 중복 될뿐만 아니라 유지 관리를 어렵게 만듭니다.

사람들은 패턴의 목적 을 실현하지 않고 패턴을 따릅니다 . 리포지토리 패턴의 경우 낮은 수준의 데이터베이스 쿼리 논리를 추상화하는 것이 목적입니다. 코드에서 실제로 SQL 문을 작성하던 시절에는 리포지토리 패턴이 해당 코드를 코드 기반에 흩어져있는 개별 메서드에서 해당 SQL로 이동하여 한 곳에서 지역화하는 방법이었습니다. Entity Framework, NHibernate 등과 같은 ORM을 갖는 것이이 코드 추상화를 대체 하므로 패턴의 필요성을 무효화합니다.

그러나 ORM을 기반으로 추상화를 생성하는 것은 좋지 않습니다. UoW / repostitory만큼 복잡한 것은 아닙니다. 서비스 패턴을 사용하여 데이터가 Entity Framework, NHibernate 또는 Web API에서 오는지 여부를 알거나 신경 쓰지 않고 응용 프로그램에서 사용할 수있는 API를 구성합니다. 응용 프로그램에 필요한 데이터를 반환하기 위해 단순히 서비스 클래스에 메서드를 추가하기 때문에 훨씬 간단합니다. 예를 들어, To-do 앱을 작성중인 경우 이번 주에 마감되었지만 아직 완료되지 않은 항목을 리턴하기위한 서비스 요청이있을 수 있습니다. 모든 정보는이 정보를 원하면 해당 메소드를 호출한다는 것입니다. 해당 방법과 일반적으로 서비스 내에서 Entity Framework 또는 사용중인 다른 항목과 상호 작용합니다. 그런 다음 나중에 ORM을 전환하거나 웹 API에서 정보를 가져 오려면,

리포지토리 패턴 사용에 대한 잠재적 인 주장처럼 들릴 수 있지만 여기서 중요한 차이점은 서비스가 더 얇은 계층이고 저장소.


68
이것은 유일한 정답입니다.
Mike Chamberlain

10
EF6 +에서 모의 있습니다DbContext ( msdn.microsoft.com/en-us/data/dn314429.aspx 참조 ). 작은 버전에서도 iterface를 구현하기 때문에 DbContextmocked와 함께 가짜 같은 클래스를 사용할 수 있습니다 . DbSetDbSetIDbSet
Chris Pratt

14
@ TheZenker, 저장소 패턴을 정확하게 따르지 않았을 수 있습니다. 가장 큰 차이점은 반환 값입니다. 리포지토리는 쿼리 가능을 반환하지만 서비스는 열거 가능을 반환해야합니다. 심지어 겹치는 부분이 있기 때문에 실제로는 흑백이 아닙니다. 사용 방법이 더 중요합니다. 리포지토리는 모든 개체 집합을 반환 한 다음 추가 쿼리를 수행하는 반면 서비스는 최종 데이터 집합을 반환해야하며 추가 쿼리를 지원하지 않아야합니다.
Chris Pratt

10
이기적으로 들릴 위험이 있습니다. 이제 공식 자습서에 이르기까지 Microsoft는 EF6 이후에 본 것으로부터 리포지토리를 사용하지 않았습니다. 책과 관련하여 저자가 왜 리포지토리를 사용하기로 선택했는지 말할 수 없습니다. 대규모 응용 프로그램을 구축하는 참호의 누군가로서 내가 말할 수있는 것은 Entity Framework와 함께 저장소 패턴을 사용하는 것이 유지 관리의 악몽이라는 것입니다. 소수의 리포지토리보다 더 복잡한 것으로 이동하면 리포지토리 / 작업 단위를 관리하는 데 많은 시간을 소비하게됩니다.
Chris Pratt

6
일반적으로 데이터베이스 또는 액세스 방법 당 하나의 서비스 만 있습니다. 동일한 메소드 세트에서 여러 엔티티 유형을 조회하기 위해 일반 메소드를 사용합니다. 나는 Ninject를 사용하여 내 컨텍스트를 서비스에 주입 한 다음 서비스를 컨트롤러에 주입하여 모든 것이 깔끔하고 깔끔합니다.
크리스 프랫

45

다음은 Ayende Rahien에서 가져온 것입니다. 파멸의 구덩이에서 설계 : 저장소 추상화 계층의 악

그의 결론에 동의하는지는 아직 확실하지 않습니다. 한편으로는 catch-22입니다-쿼리 별 데이터 검색 방법을 사용하여 유형별 리포지토리에서 EF 컨텍스트를 래핑하면 실제로 코드 (정렬)를 단위 테스트 할 수 있으며 Entity에서는 거의 불가능합니다. 프레임 워크 만. 반면에, 나는 관계에 대한 풍부한 쿼리 및 의미 적 유지 관리 기능을 잃어 버립니다 (그러나 그러한 기능에 완전히 액세스 할 때조차도 항상 EF 또는 다른 ORM 주위에서 달걀 껍질을 걷고 있다고 생각합니다 , IQueryable 구현이 지원하거나 지원하지 않는 방법을 알지 못하기 때문에 탐색 속성 컬렉션에 추가하는 것을 생성 또는 단순한 연결로 해석할지 여부, 지연 또는 열망에 의한로드 여부에 관계없이 기본값 등 아마 이것은 더 나은 것입니다. 제로 임피던스 객체 관계형 "매핑 (mapping)"은 신화적인 생물의 일종입니다. 아마도 Entity Framework의 최신 릴리스가 코드 명 "Magic Unicorn"인 이유 일 것입니다.

그러나 쿼리 별 데이터 검색 방법을 통해 엔티티를 검색한다는 것은 이제 단위 테스트가 본질적으로 화이트 박스 테스트이며이 문제에서 선택의 여지가 없음을 의미합니다. 그것을 조롱하기 위해 전화. 통합 테스트를 작성하지 않는 한 실제로 쿼리 자체를 테스트하지는 않습니다.

복잡한 솔루션이 필요한 복잡한 문제입니다. 모든 엔티티가 서로 관계가없는 별도의 유형 인 것처럼 가장하여 각각의 저장소로 원자화하면 문제를 해결할 수 없습니다. 글쎄, 당신 있지만 짜증나.

업데이트 : Entity Framework에 Effort 공급자를 사용하여 약간의 성공을 거두었습니다 . Effort는 실제 데이터베이스에 대해 EF를 사용하는 것과 정확히 동일한 방식으로 테스트에서 EF를 사용할 수있게하는 인 메모리 제공자 (오픈 소스)입니다. 나는이 공급자를 사용하기 위해 노력하고있는이 프로젝트의 모든 테스트를 전환하는 것을 고려하고 있습니다. 지금까지 내가 찾은 유일한 솔루션으로, 이전에 발생했던 모든 문제를 해결했습니다. 테스트 내 메모리 데이터베이스를 생성하기 때문에 테스트를 시작할 때 약간의 지연이 있습니다 (NMemory라는 다른 패키지를 사용 하여이 작업을 수행함). 그러나 이것을 실제 문제로 보지는 않습니다. 테스트를 위해 Effort (SQL CE와 비교)를 사용 하는 방법에 대한 코드 프로젝트 기사가 있습니다.


3
단위 테스트를 언급하지 않은 건축 기사는 자동으로 휴지통으로 보내집니다. 리포지토리 패턴의 요점 중 하나는 테스트 가능성을 얻는 것입니다.
슬리퍼 스미스

3
EF 컨텍스트 (이미 리포지토리)를 래핑하지 않고도 유닛 테스트를 계속할 수 있습니다. 데이터베이스 쿼리가 아닌 도메인 / 서비스를 단위 테스트해야합니다 (통합 테스트).
Daniel Little

2
EF의 테스트 기능은 버전 6에서 크게 향상되었습니다 DbContext. 이제 완전히 조롱 할 수 있습니다 . 어쨌든 항상 조롱 할 수 DbSet있으며 그것이 Entity Framework의 고기입니다. DbContextDbSet모든 데이터베이스 초기화 및 연결 항목을 원치 않거나 필요하지 않은 단위 테스트 컨텍스트에서 한 위치 (작업 단위)에 속성 (저장소)을 저장 하는 클래스에 지나지 않습니다.
Chris Pratt

관련 엔터티 탐색을 잃는 것은 좋지 않으며 반대 OOP이지만 쿼리되는 내용을 더 많이 제어 할 수 있습니다.
Alireza

테스트 시점에서 EF Core는 단위 테스트를 가능하게하기 위해 즉시 In-Memory 및 Sqlite 공급자와 함께 In-Memory를 사용하여 먼 길을 왔습니다. 컨테이너화 된 데이터베이스에서 테스트를 실행하기 위해 통합 테스트가 필요할 때 도커를 가져옵니다.
Sudhanshu Mishra

16

아마도 그렇게하는 이유는 약간 중복되기 때문입니다. Entity Framework는 풍부한 코딩 및 기능적 이점을 제공하므로이를 사용하고이를 활용하여 이러한 이점을 버리는 리포지토리 패턴으로 랩핑하면 다른 데이터 액세스 계층을 사용할 수도 있습니다.


"Entity Framework가 풍부한 코딩 및 기능적 이점을 제공합니다"에 대한 몇 가지 장점을 알려주시겠습니까?
ManirajSS

2
이것이 그가 의미 한 바입니다. var id = Entity.Where (i => i.Id == 1337) .Single ()이 캡슐화되어 저장소에 래핑하면 기본적으로 외부에서 이와 같은 쿼리 논리를 수행 할 수 없으므로 A에 더 많은 코드를 추가해야합니다. ID를 가져 오기위한 저장소 및 인터페이스 B는 저장소에서 엔터티 컨텍스트를 반환하므로 쿼리 논리를 작성할 수 있습니다 (
비논리적 임

14

이론적으로는 데이터베이스 연결 논리를 캡슐화하여보다 쉽게 ​​재사용 할 수 있도록하는 것이 합리적이라고 생각하지만 아래 링크에서 알 수 있듯이 현대 프레임 워크는 이제이를 기본적으로 처리합니다.

리포지토리 패턴 재검토


필자는이 기사를 좋아했지만 엔터프라이즈 애플리케이션 용 IMHO, DAL과 Bl 간의 추상화 계층 기능이 있어야한다. 내일 정확히 어떤 용도로 사용 될지 알 수 없기 때문이다. 링크를 공유해 주셔서 감사합니다

1
개인적으로 나는 그것이 예를 들어 NHibernate ( ISessionFactory그리고 ISession쉽게 조롱 가능)에 대해 맞다고 생각하지만 DbContext, 불행히도 그렇게 쉽지는 않다 .
Patryk Ćwiek

6

리포지토리 패턴 을 사용 하는 매우 좋은 이유 는 비즈니스 로직 및 / 또는 UI를 System.Data.Entity에서 분리 할 수 있기 때문 입니다. Fakes 또는 Mocks를 사용할 수있게함으로써 단위 테스트의 실질적인 이점을 포함하여 여기에는 많은 장점이 있습니다.


이 답변에 동의합니다. 내 리포지토리는 기본적으로 확장 메서드이며 표현식 트리를 만드는 것 외에는 아무것도하지 않습니다. dbcontext의 맨 위에 직접 일반 기능을 제공하는 매우 간단한 추상화를 통해. 추상화의 유일한 실제 목적은 IoC를 조금 더 쉽게 만드는 것입니다. 사람들은 리포지토리에서하지 말아야 할 일을하려고한다고 생각합니다. 이들은 엔티티별로 리포지토리를 만들거나 서비스 계층에 있어야하는 비즈니스 로직을 배치합니다. 실제로 하나의 간단한 일반 저장소 만 필요합니다. 필요하지 않고 일관성있는 인터페이스 만 제공합니다.
Brandon

방금 추가하고 싶은 것이 하나 더 있습니다. 예 CQRS는 대부분의 경우에 매우 뛰어난 방법입니다. 데이터베이스 사용자가 개발자와 잘 작동하지 않을 때 (특히 은행에서 생각하는 것보다 더 자주 발생하는) 일부 클라이언트의 경우 EF over SQL이 최선의 선택입니다. 이 특정 시나리오에서 데이터베이스를 전혀 제어하지 않으면 리포지토리 패턴이 적합합니다. 데이터 구조와 매우 유사하고 데이터베이스로 진행되는 내용을 쉽게 번역 할 수 있기 때문에 그 반대도 마찬가지입니다. 제 생각에 그것은 정말로 정치적이고 물류적인 결정입니다. DB 신을 달래기 위해.
Brandon

1
나는 실제로 이것에 대한 나의 초기 의견에 의문을 가지기 시작했다. EF는 작업 단위 및 리포지토리 패턴이 결합 된 것입니다. Chris Pratt가 EF6으로 위에서 언급했듯이 Context 및 DbSet 객체를 쉽게 조롱 할 수 있습니다. 나는 여전히 비즈니스 로직 클래스를 실제 데이터 액세스 메커니즘으로부터 보호하기 위해 데이터 액세스를 클래스로 래핑해야한다고 생각하지만 전체 저장소로 이동하고 EF를 다른 저장소와 랩으로 묶는 것은 과도하게 보입니다.
James Culshaw

지지하는 진술은 단지 하나만 나열하는 동안 수많은 이점이 있다는 것이므로 이것이 좋은 대답이라고 생각하지 않습니다. 목록에있는 것은 메모리 내 데이터베이스를 사용하여 엔티티를 수행 할 수 있기 때문에 좋은 이유가 아닙니다. 단위 테스트.
Joel McBeth

@jcmcbeth 당신이 당신의 바로 위에 내 의견을 보면 저장소 패턴과 EF와 관련하여 나의 원래 의견이 바뀌 었음을 알 수 있습니다.
James Culshaw

0

유형별로 new () 리포지토리 (예 : 각각 DBContext에서 고유 한 IDbSet를 호출하는 UserRepository 및 GroupRepository 인스턴스)를 갖는 IoC 컨테이너가 요청 당 여러 컨텍스트를 유발할 수있는 중복하지만 다른 Entity Framework DbContext 인스턴스에 문제가있었습니다. (MVC / 웹 컨텍스트에서).

대부분의 경우 여전히 작동하지만 그 위에 서비스 계층을 추가하고 해당 서비스가 한 컨텍스트로 작성된 오브젝트가 다른 컨텍스트의 새 오브젝트에 하위 콜렉션으로 올바르게 첨부된다고 가정하면 때때로 실패하고 때로는 실패합니다. 커밋 속도에 따라 t.


여러 다른 프로젝트 에서이 문제가 발생했습니다.
ColacX

0

작은 프로젝트에서 저장소 패턴을 시도한 후에는 사용하지 않는 것이 좋습니다. 데이터를 조롱하는 것이 악몽이 아니라 테스트가 쓸모 없게되기 때문에 시스템을 복잡하게하기 때문이 아닙니다.

데이터를 모의하면 헤더없이 세부 정보를 추가하고 데이터베이스 제약 조건을 위반하는 레코드를 추가하며 데이터베이스에서 제거를 거부 할 엔티티를 제거 할 수 있습니다. 실제로 단일 업데이트는 여러 테이블, 로그, 기록, 요약 등에 영향을 줄 수 있으며 마지막 수정 날짜 필드, 자동 생성 키, 계산 필드와 같은 열에도 영향을 줄 수 있습니다.

실제 데이터베이스에서 테스트를 실행하면 실제 결과를 얻을 수 있으며 서비스 및 인터페이스뿐만 아니라 데이터베이스 동작도 테스트 할 수 있습니다. 저장 프로 시저가 데이터로 올바른 작업을 수행하는지, 예상 결과를 반환하는지, 또는 삭제하기 위해 보낸 레코드가 실제로 삭제되었는지 확인할 수 있습니다! 이러한 테스트는 저장 프로 시저에서 오류 발생을 잊어 버리는 것과 같은 수천 가지 시나리오와 같은 문제를 노출시킬 수도 있습니다.

엔터티 프레임 워크는 지금까지 읽은 기사보다 저장소 패턴을 더 잘 구현한다고 생각합니다.

리포지토리는 XBase, AdoX 및 Ado.Net을 사용했지만 엔티티와 함께 ​​사용하던 시절에 모범 사례였습니다! (리포지토리를 통한 리포지토리)

마지막으로 저는 너무 많은 사람들이 저장소 패턴을 배우고 구현하는 데 많은 시간을 투자한다고 생각합니다. 대부분 자신이 시간을 낭비하지 않았다는 것을 증명합니다.


1
완전히 테스트 수준이 아니기 때문에 단위 테스트에서 데이터베이스 동작을 테스트하지 않으려는 경우를 제외하고.
Mariusz Jamro

그렇습니다. 여기서 말하는 것은 통합 테스트이며, 실제로는 가치가 있지만 단위 테스트는 완전히 다릅니다. 단위 테스트는 실제 데이터베이스에 도달해서는 안되지만 통합 테스트를 추가하는 것이 좋습니다.
Chris Pratt

-3

연결 문자열이 web.config에 있으므로 마이그레이션이 작동하지 않습니다. 그러나 DbContext는 Repository 레이어에 있습니다. IDbContextFactory에는 데이터베이스에 대한 구성 문자열이 필요하지만 마이그레이션이 web.config에서 연결 문자열을 가져 오는 방법은 없습니다.

해결 방법이 있지만 아직 깨끗한 해결책을 찾지 못했습니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.