code

저장소 패턴을 사용하지 않고 ORM을있는 그대로 사용 (EF)

codestyles 2020. 9. 7. 08:10
반응형

저장소 패턴을 사용하지 않고 ORM을있는 그대로 사용 (EF)


저는 항상 Repository 패턴을 사용했지만 최근 프로젝트에서이 패턴과 "Unit Of Work"구현을 완벽하게 사용할 수 있는지 확인하고 싶었습니다. 땅을 파기 시작할수록 스스로에게 "정말 필요합니까?"라는 질문을하기 시작 했습니다.

이제이 모든 것은 그의 블로그에 Ayende Rahien의 게시물을 추적하여 Stackoverflow에 대한 몇 가지 의견으로 시작됩니다.

이것은 아마도 영원히 이야기 될 수 있으며 다른 응용 프로그램에 따라 다릅니다. 내가 알고 싶은 것,

  1. 이 접근 방식이 Entity Framework 프로젝트에 적합합니까?
  2. 이 접근 방식을 사용하면 비즈니스 로직이 여전히 서비스 계층 또는 확장 방법으로 이동합니까 (아래에서 설명했듯이 확장 방법은 NHib 세션을 사용하고 있음)?

확장 메서드를 사용하면 쉽게 수행 할 수 있습니다. 깨끗하고 간단하며 재사용이 가능합니다.

public static IEnumerable GetAll(
    this ISession instance, Expression<Func<T, bool>> where) where T : class
{
    return instance.QueryOver().Where(where).List();
}

이 접근 방식 Ninject을 DI로 사용 Context하여 인터페이스 를 만들고 컨트롤러에 삽입해야합니까?


나는 많은 길을 갔고 다른 프로젝트에서 많은 리포지토리 구현을 만들었고 ... 나는 수건을 던지고 포기했습니다. 이유가 여기에 있습니다.

예외에 대한 코딩

데이터베이스가 한 기술에서 다른 기술로 변경 될 확률 1 %에 대해 코딩합니까? 비즈니스의 미래 상태에 대해 생각하고 있고 가능하다고 대답하면 a) 다른 DB 기술로 마이그레이션 할 수있는 많은 돈이 있어야합니다. 또는 b) 재미를 위해 DB 기술을 선택하는 것입니다. ) 사용하기로 결정한 첫 번째 기술에 끔찍한 문제가 발생했습니다.

풍부한 LINQ 구문을 버리는 이유는 무엇입니까?

LINQ 및 EF는 개체 그래프를 읽고 순회하기 위해 깔끔한 작업을 수행 할 수 있도록 개발되었습니다. 동일한 유연성을 제공 할 수있는 저장소를 만들고 유지 관리하는 것은 엄청난 작업입니다. 내 경험상 저장소를 만들 때마다 쿼리의 성능을 높이거나 데이터베이스에 대한 적중 수를 줄이기 위해 항상 저장소 계층에 비즈니스 논리가 누출되었습니다.

작성해야하는 쿼리의 모든 순열에 대한 메서드를 만들고 싶지 않습니다. 저장 프로 시저를 작성하는 것이 좋습니다. 나는 싶지 않아 GetOrder, GetOrderWithOrderItem, GetOrderWithOrderItemWithOrderActivity, GetOrderByUserId, 등 ... 난 그냥 주요 기업 트래버스를 얻기 위해 원하는대로 객체 그래프를 포함 나는 그렇게하십시오.

저장소의 대부분의 예는 헛소리입니다.

블로그 나 어떤 것을 개발하지 않는 한, 리포지토리 패턴을 둘러싼 인터넷에서 찾은 예제의 90 %만큼 간단하지 않을 것입니다. 나는 이것을 충분히 강조 할 수 없다! 이것은 알아 내기 위해 진흙 속을 기어 가야하는 것입니다. 완벽하게 생각한 리포지토리 / 솔루션을 깨는 쿼리가 항상있을 것이며, 두 번째로 스스로를 추측하고 기술적 부채 / 침식이 시작될 때까지는 아닙니다.

날 단위 테스트하지 마

하지만 저장소가 없으면 단위 테스트는 어떻습니까? 어떻게 조롱합니까? 간단하지 않습니다. 두 각도에서 살펴 보겠습니다.

리포지토리 없음- 또는 다른 트릭을 DbContext사용하여 모의 할 수 IDbContext있지만 쿼리가 런타임에 결정되기 때문에 LINQ to Entities가 아니라 실제로 LINQ to Objects를 단위 테스트 하는 것입니다 ... 좋아요, 좋지 않습니다! 이제 이것을 다루는 것은 통합 테스트에 달려 있습니다.

리포지토리 사용-이제 리포지토리를 모의하고 그 사이의 계층을 단위 테스트 할 수 있습니다. 좋아요? 글쎄요 ... 위의 경우 쿼리의 성능을 높이고 / 또는 데이터베이스에 대한 적중을 줄이기 위해 리포지토리 계층에 로직을 누출해야하는 경우 단위 테스트가 어떻게이를 처리 할 수 ​​있습니까? 이제 repo 레이어에 있으며 제대로 테스트하고 싶지 IQueryable<T>않습니까? 또한 솔직히 말해서 단위 테스트는 20 줄 .Where()절이 있는 쿼리를 다루지 않을 것입니다..Include()쿼리가 런타임에 생성되기 때문에 어쨌든 다른 모든 작업을 수행하기 위해 데이터베이스에 다시 연결됩니다. 또한 상위 계층 지속성을 무시하기 위해 저장소를 만들었으므로 이제 데이터베이스 기술을 변경하려는 경우 단위 테스트가 런타임에 동일한 결과를 보장하지 못해 통합 테스트로 돌아갑니다. 그래서 저장소 전체가 이상해 보입니다 ..

2 센트

일반 저장 프로 시저 (대량 삽입, 대량 삭제, CTE 등)보다 EF를 사용할 때 이미 많은 기능과 구문이 손실되었지만 C #으로 코딩도하므로 바이너리를 입력 할 필요가 없습니다. 우리는 EF를 사용하므로 다른 공급자를 사용하고 여러 가지 중에서 좋은 관련 방식으로 개체 그래프를 사용할 수 있습니다. 특정 추상화는 유용하지만 일부는 그렇지 않습니다.


저장소 패턴은 추상화 입니다. 그 목적은 복잡성을 줄이고 나머지 코드를 무지하게 만드는 것입니다. 보너스 통합 테스트 대신 단위 테스트 를 작성할 수 있습니다 .

문제는 많은 개발자가 패턴 목적을 이해하지 못하고 호출자에게 지속성 특정 정보를 유출하는 저장소를 생성하지 못한다는 것입니다 (일반적으로 IQueryable<T>. 그렇게함으로써 그들은 OR / M을 직접 사용하는 것보다 혜택을받지 못합니다.

다른 답변을 해결하기 위해 업데이트

예외에 대한 코딩

저장소를 사용하는 것은 지속성 기술을 전환 할 수있는 것이 아닙니다 (예 : 데이터베이스 변경 또는 웹 서비스 사용 등). 복잡성과 결합을 줄이기 위해 비즈니스 로직을 지속성과 분리하는 것입니다.

단위 테스트와 통합 테스트

리포지토리에 대한 단위 테스트를 작성하지 않습니다. 기간.

그러나 리포지토리 (또는 지속성과 비즈니스 사이의 다른 추상화 계층)를 도입하여 비즈니스 논리에 대한 단위 테스트를 작성할 수 있습니다. 즉, 잘못 구성된 데이터베이스로 인해 테스트 실패에 대해 걱정할 필요가 없습니다.

쿼리에 관해서. LINQ를 사용하는 경우 리포지토리와 마찬가지로 쿼리가 작동하는지 확인해야합니다. 통합 테스트를 사용하여 수행됩니다.

차이점은 비즈니스를 LINQ 문과 혼합하지 않은 경우 실패한 것이 지속성 코드이고 다른 것이 아니라는 것을 100 % 확신 할 수 있다는 것입니다.

테스트를 분석하면 문제가 혼합되지 않은 경우 (예 : LINQ + 비즈니스 로직) 훨씬 더 깔끔하다는 것을 알 수 있습니다.

저장소 예

대부분의 예는 헛소리입니다. 그것은 매우 사실입니다. 그러나 디자인 패턴을 검색하면 많은 엉뚱한 예를 찾을 수 있습니다. 그것은 패턴 사용을 피할 이유가 아닙니다.

올바른 저장소 구현을 구축하는 것은 매우 쉽습니다. 사실, 당신은 단 하나의 규칙 만 따라야합니다 :

필요할 때까지 저장소 클래스에 아무것도 추가하지 마십시오.

코더의 많은 일반적인 저장소를 만들 게으른 시도하고 그들이 그 방법의 많은 기본 클래스를 사용할 있어야합니다. 야 그니. 리포지토리 클래스를 한 번 작성하고 응용 프로그램이 유지되는 동안 보관합니다 (년이 될 수 있음). 왜 게 으르면서 망쳐 버려. 기본 클래스 상속없이 깨끗하게 유지하십시오. 읽기 및 유지 관리가 훨씬 쉬워집니다.

(위의 설명은 지침이며 법률이 아닙니다. 기본 클래스는 동기를 부여 할 수 있습니다. 추가하기 전에 생각하여 올바른 이유에 추가하십시오)

오래된 물건

결론:

비즈니스 코드에 LINQ 문이 있거나 단위 테스트에 신경 쓰지 않는다면 Entity Framework를 직접 사용하지 않을 이유가 없습니다.

최신 정보

리포지토리 패턴과 "추상화"가 실제로 의미하는 바에 대해 블로그를 작성했습니다. http://blog.gauffin.org/2013/01/repository-pattern-done-right/

업데이트 2

필드가 20 개 이상인 단일 항목 유형의 경우 모든 순열 조합을 지원하는 쿼리 방법을 어떻게 디자인할까요? 이름으로 만 검색을 제한하고 싶지는 않습니다. 탐색 속성으로 검색하는 것은 어떻습니까? 특정 가격 코드가있는 모든 주문 목록, 3 단계 탐색 속성 검색. IQueryable발명 된 모든 이유 는 데이터베이스에 대한 검색 조합을 구성 할 수 있기 때문입니다. 이론상 모든 것이 훌륭해 보이지만 사용자의 요구가 이론보다 우위에 있습니다.

다시 : 필드가 20 개 이상인 엔터티가 잘못 모델링되었습니다. 그것은 신의 존재입니다. 그것을 파괴.

나는 그것이 IQueryablequering을 위해 만들어진 것이 아니라고 주장하는 것이 아닙니다. 리포지토리 패턴과 같은 추상화 레이어 에는 누수가 있기 때문에 적합하지 않다는 것입니다 . 100 % 완전한 LINQ To Sql 공급자 (예 : EF)는 없습니다.

이들은 모두 eager / lazy 로딩을 사용하는 방법이나 SQL "IN"문을 수행하는 방법과 같은 구현 특정 사항을 가지고 있습니다. IQueryable저장소에 노출 되면 사용자는 이러한 모든 것을 알 수 있습니다. 따라서 데이터 소스를 추상화하려는 전체 시도는 완전한 실패입니다. OR / M을 직접 사용하는 것보다 이점을 얻지 않고 복잡성 만 추가하면됩니다.

리포지토리 패턴을 올바르게 구현하거나 아예 사용하지 마십시오.

(만약 큰 엔터티를 처리하고 싶다면 Repository 패턴을 Specification 패턴 과 결합 할 수 있습니다 . 이는 또한 테스트 가능한 완전한 추상화를 제공합니다.)


IMO는 Repository추상화와 추상화 모두 UnitOfWork의미있는 개발에서 매우 가치있는 위치를 차지합니다. 사람들은 구현 세부 사항에 대해 논쟁을 벌일 것이지만 고양이를 피부로 만드는 방법이 많 듯이 추상화를 구현하는 방법도 많습니다.

귀하의 질문은 구체적으로 사용 여부와 그 이유입니다.

당신은 의심의 여지가 실현되지 것처럼 당신은 이미, 엔티티 프레임 워크에 내장 된 두 가지 패턴이 DbContext입니다 UnitOfWorkDbSet입니다 Repository. 당신은 일반적으로 단위 테스트에 필요하지 않습니다 UnitOfWork또는 Repository그들은 단순히 수업 및 기본 데이터 액세스 구현에 용이하게되어 스스로. 서비스의 논리를 단위 테스트 할 때이 두 가지 추상화를 모방하는 것이 반복해서 수행해야하는 작업입니다.

테스트를 수행하는 로직과 테스트중인 로직 사이 에 코드 종속성 레이어 (제어하지 않는)추가하는 외부 라이브러리를 사용하여 모의, 가짜 또는 무엇이든 할 수 있습니다 .

작은 포인트 그래서 있다는 것입니다 에 대한 자신의 추상화를 가진 UnitOfWork하고하는 Repository단위 테스트를 조롱 할 때 최대의 제어와 유연성을 제공합니다.

모두 매우 훌륭하지만, 이러한 추상화의 진정한 힘은 Aspect Oriented Programming 기술 을 적용 하고 SOLID 원칙을 준수 하는 간단한 방법을 제공한다는 입니다.

그래서 당신은 당신의 IRepository:

public interface IRepository<T>
    where T : class
{
    T Add(T entity);
    void Delete(T entity);
    IQueryable<T> AsQueryable();
}

그리고 그 구현 :

public class Repository<T> : IRepository<T>
    where T : class
{
    private readonly IDbSet<T> _dbSet;
    public Repository(PPContext context) 
    {
        _dbSet = context.Set<T>();
    }

    public T Add(T entity)
    { 
        return _dbSet.Add(entity); 
    }

    public void Delete(T entity)
    {
        _dbSet.Remove(entity); 
    }

    public IQueryable<T> AsQueryable() 
    {
        return _dbSet.AsQueryable();
    }
}

지금까지는 평범하지 않지만 이제 로깅 데코레이터를 사용하여 로깅을 쉽게 추가하고 싶습니다 .

public class RepositoryLoggerDecorator<T> : IRepository<T>
    where T : class
{
    Logger logger = LogManager.GetCurrentClassLogger();
    private readonly IRepository<T> _decorated;
    public RepositoryLoggerDecorator(IRepository<T> decorated)
    {
        _decorated = decorated;
    }

    public T Add(T entity)
    {
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString() );
        T added = _decorated.Add(entity);
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
        return added;
    }

    public void Delete(T entity)
    {
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
        _decorated.Delete(entity);
        logger.Log(LogLevel.Debug, () => DateTime.Now.ToLongTimeString());
    }

    public IQueryable<T> AsQueryable()
    {
        return _decorated.AsQueryable();
    }
}

모두 완료되었으며 기존 코드는 변경되지 않았습니다 . 예외 처리, 데이터 캐싱, 데이터 유효성 검사 등과 같이 추가 할 수있는 다른 수많은 교차 절단 문제가 있으며, 설계 및 빌드 프로세스 전반에 걸쳐 기존 코드를 변경하지 않고 간단한 기능을 추가 할 수있는 가장 가치있는 것입니다. 우리의 IRepository추상화 입니다.

이제 StackOverflow에서이 질문을 여러 번 보았습니다. "멀티 테넌트 환경에서 Entity Framework가 작동하도록 만드는 방법은 무엇입니까?"

https://stackoverflow.com/search?q=%5Bentity-framework%5D+multi+tenant

Repository추상화 가 있다면 대답은 "데코레이터를 추가하는 것은 쉽습니다"입니다.

public class RepositoryTennantFilterDecorator<T> : IRepository<T>
    where T : class
{
    //public for Unit Test example
    public readonly IRepository<T> _decorated;
    public RepositoryTennantFilterDecorator(IRepository<T> decorated)
    {
        _decorated = decorated;
    }

    public T Add(T entity)
    {
        return _decorated.Add(entity);
    }

    public void Delete(T entity)
    {
        _decorated.Delete(entity);
    }

    public IQueryable<T> AsQueryable()
    {
        return _decorated.AsQueryable().Where(o => true);
    }
}

IMO는 항상 몇 군데 이상에서 참조되는 타사 구성 요소에 대해 간단한 추상화를 배치해야합니다. 이러한 관점에서 ORM은 우리 코드의 많은 부분에서 참조되는 완벽한 후보입니다.

누군가 "내가이 Repository라이브러리 또는 타사 라이브러리에 대한 추상화 (예 :)를 가져야하는 이유"라고 말할 때 일반적으로 떠오르는 대답 은 "왜 그렇지 않겠습니까?"입니다.

PS 데코레이터는 SimpleInjector 와 같은 IoC 컨테이너를 사용하여 적용하는 것이 매우 간단합니다 .

[TestFixture]
public class IRepositoryTesting
{
    [Test]
    public void IRepository_ContainerRegisteredWithTwoDecorators_ReturnsDecoratedRepository()
    {
        Container container = new Container();
        container.RegisterLifetimeScope<PPContext>();
        container.RegisterOpenGeneric(
            typeof(IRepository<>), 
            typeof(Repository<>));
        container.RegisterDecorator(
            typeof(IRepository<>), 
            typeof(RepositoryLoggerDecorator<>));
        container.RegisterDecorator(
            typeof(IRepository<>), 
            typeof(RepositoryTennantFilterDecorator<>));
        container.Verify();

        using (container.BeginLifetimeScope())
        {
            var result = container.GetInstance<IRepository<Image>>();

            Assert.That(
                result, 
                Is.InstanceOf(typeof(RepositoryTennantFilterDecorator<Image>)));
            Assert.That(
                (result as RepositoryTennantFilterDecorator<Image>)._decorated,
                Is.InstanceOf(typeof(RepositoryLoggerDecorator<Image>)));
        }
    }
}

우선, 일부 답변에서 제안했듯이 EF 자체는 저장소 패턴이므로 저장소 이름을 지정하기 위해 추가 추상화를 만들 필요가 없습니다.

단위 테스트를위한 Mockable Repository, 정말 필요합니까?

EF는 단위 테스트에서 테스트 DB와 통신하여 SQL 테스트 DB에 대해 비즈니스 논리를 직접 테스트 할 수 있습니다. 리포지토리 패턴을 모의하는 것의 이점은 전혀 없습니다. 테스트 데이터베이스에 대해 단위 테스트를 수행하는 것이 실제로 잘못된 것은 무엇입니까? 대량 작업이 불가능하므로 원시 SQL을 작성하게됩니다. 메모리의 SQLite는 실제 데이터베이스에 대한 단위 테스트를 수행하기에 완벽한 후보입니다.

불필요한 추상화

나중에 EF를 NHbibernate 등으로 쉽게 대체 할 수 있도록 저장소를 만들고 싶습니까? 훌륭한 계획처럼 들리지만 정말 비용 효율적입니까?

Linq가 단위 테스트를 죽입니까?

나는 그것이 어떻게 죽일 수 있는지에 대한 예를보고 싶습니다.

의존성 주입, IoC

와우, 이것은 훌륭한 단어입니다. 이론적으로는 훌륭해 보이지만 때로는 훌륭한 디자인과 훌륭한 솔루션 사이의 균형을 선택해야합니다. 우리는 그 모든 것을 사용했고 결국 모든 것을 쓰레기통에 버리고 다른 접근 방식을 선택했습니다. 크기 대 속도 (코드 크기 및 개발 속도)는 실제 생활에서 매우 중요합니다. 사용자는 유연성이 필요합니다. DI 또는 IoC 측면에서 코드가 훌륭한 디자인인지 상관하지 않습니다.

Visual Studio를 빌드하지 않는 한

많은 사람들이 개발할 Visual Studio 또는 Eclipse와 같은 복잡한 프로그램을 빌드하고 고도로 사용자 정의 할 수 있어야하는 경우 이러한 모든 훌륭한 디자인이 필요합니다. 모든 훌륭한 개발 패턴은 수년간의 개발 끝에 등장했으며 이러한 IDE는이 모든 훌륭한 디자인 패턴이 중요한 곳에서 진화했습니다. 그러나 간단한 웹 기반 급여 또는 간단한 비즈니스 앱을 수행하는 경우 100 만 명의 사용자에게만 배포되는 수백만 사용자를 위해 빌드하는 데 시간을 소비하는 대신 시간에 따라 개발을 진행하는 것이 좋습니다.

필터링 된보기로서의 저장소-ISecureRepository

반면에 저장소는 현재 사용자 / 역할을 기반으로 필요한 필러를 적용하여 데이터에 대한 액세스를 보호하는 EF의 필터링 된보기 여야합니다.

그러나 이렇게하면 유지 관리해야 할 거대한 코드 기반으로 끝나기 때문에 저장소가 훨씬 더 복잡해집니다. 사람들은 서로 다른 사용자 유형 또는 엔티티 유형 조합에 대해 서로 다른 저장소를 생성하게됩니다. 뿐만 아니라 우리는 많은 DTO로 끝납니다.

다음 답변은 전체 클래스 및 메서드 집합을 생성하지 않고 Filtered Repository를 구현 한 예입니다. 질문에 직접 답할 수는 없지만 도출하는 데 유용 할 수 있습니다.

면책 조항 : 저는 Entity REST SDK의 작성자입니다.

http://entityrestsdk.codeplex.com

위의 사항을 염두에두고 CRUD 작업을위한 필터를 보유하는 SecurityContext를 기반으로 필터링 된 뷰의 저장소를 생성하는 SDK를 개발했습니다. 그리고 두 종류의 규칙 만이 복잡한 작업을 단순화합니다. 첫 번째는 엔티티에 대한 액세스이고 다른 하나는 속성에 대한 읽기 / 쓰기 규칙입니다.

장점은 다른 사용자 유형에 대해 비즈니스 로직 또는 리포지토리를 다시 작성하지 않고 단순히 액세스를 차단하거나 부여 할 수 있다는 것입니다.

public class DefaultSecurityContext : BaseSecurityContext {

  public static DefaultSecurityContext Instance = new DefaultSecurityContext();

  // UserID for currently logged in User
  public static long UserID{
       get{
             return long.Parse( HttpContext.Current.User.Identity.Name );
       }
  }

  public DefaultSecurityContext(){
  }

  protected override void OnCreate(){

        // User can access his own Account only
        var acc = CreateRules<Account>();

        acc.SetRead( y => x=> x.AccountID == UserID ) ;
        acc.SetWrite( y => x=> x.AccountID == UserID );

        // User can only modify AccountName and EmailAddress fields
        acc.SetProperties( SecurityRules.ReadWrite, 
              x => x.AccountName,
              x => x.EmailAddress);

        // User can read AccountType field
        acc.SetProperties<Account>( SecurityRules.Read, 
              x => x.AccountType);

        // User can access his own Orders only
        var order = CreateRules<Order>();
        order.SetRead( y => x => x.CustomerID == UserID );

        // User can modify Order only if OrderStatus is not complete
        order.SetWrite( y => x => x.CustomerID == UserID 
            && x.OrderStatus != "Complete" );

        // User can only modify OrderNotes and OrderStatus
        order.SetProperties( SecurityRules.ReadWrite, 
              x => x.OrderNotes,
              x => x.OrderStatus );

        // User can not delete orders
        order.SetDelete(order.NotSupportedRule);
  }
}

이러한 LINQ 규칙은 모든 작업에 대해 SaveChanges 메서드의 데이터베이스에 대해 평가되며 이러한 규칙은 데이터베이스 앞에서 방화벽 역할을합니다.


어떤 방법이 옳은지에 대한 많은 논쟁이 있기 때문에 둘 다 수용 가능하다고 생각하므로 내가 가장 좋아하는 것을 사용합니다 (저장소가 아닌 UoW).

EF에서 UoW는 DbContext를 통해 구현되고 DbSet은 리포지토리입니다.

데이터 레이어로 작업하는 방법은 DbContext 개체에서 직접 작업하고 복잡한 쿼리의 경우 재사용 할 수있는 쿼리에 대한 확장 메서드를 만들 것입니다.

Ayende는 CUD 작업을 추상화하는 것이 얼마나 나쁜지에 대한 게시물도 가지고 있다고 생각합니다.

I always make an interface and have my context inherit from it so I can use an IoC container for DI.


Linq is a nowadays 'Repository'.

ISession+Linq already is the repository, and you need neither GetXByY methods nor QueryData(Query q) generalization. Being a little paranoid to DAL usage, I still prefer repository interface. (From maintainability point of view we also still have to have some facade over specific data access interfaces).

Here is repository we use - it de-couples us from direct usage of nhibernate, but provides linq interface (as ISession access in exceptional cases, which are subject to refactor eventually).

class Repo
{
    ISession _session; //via ioc
    IQueryable<T> Query()
    {
        return _session.Query<T>();
    }
}

The Repository (or however one chooses to call it) at this time for me is mostly about abstracting away the persistence layer.

I use it coupled with query objects so I do not have a coupling to any particular technology in my applications. And also it eases testing a lot.

So, I tend to have

public interface IRepository : IDisposable
{
    void Save<TEntity>(TEntity entity);
    void SaveList<TEntity>(IEnumerable<TEntity> entities);

    void Delete<TEntity>(TEntity entity);
    void DeleteList<TEntity>(IEnumerable<TEntity> entities);

    IList<TEntity> GetAll<TEntity>() where TEntity : class;
    int GetCount<TEntity>() where TEntity : class;

    void StartConversation();
    void EndConversation();

    //if query objects can be self sustaining (i.e. not need additional configuration - think session), there is no need to include this method in the repository.
    TResult ExecuteQuery<TResult>(IQueryObject<TResult> query);
}

Possibly add async methods with callbacks as delegates. The repo is easy to implement generically, so I am able not to touch a line of the implementation from app to app. Well, this is true at least when using NH, I did it also with EF, but made me hate EF. 4. The conversation is the start of a transaction. Very cool if a few classes share the repository instance. Also, for NH, one repo in my implementation equals one session which is opened at the first request.

Then the Query Objects

public interface IQueryObject<TResult>
{
    /// <summary>Provides configuration options.</summary>
    /// <remarks>
    /// If the query object is used through a repository this method might or might not be called depending on the particular implementation of a repository.
    /// If not used through a repository, it can be useful as a configuration option.
    /// </remarks>
    void Configure(object parameter);

    /// <summary>Implementation of the query.</summary>
    TResult GetResult();
}

For the configure I use in NH only to pass in the ISession. In EF makes no sense more or less.

An example query would be.. (NH)

public class GetAll<TEntity> : AbstractQueryObject<IList<TEntity>>
    where TEntity : class
{
    public override IList<TEntity> GetResult()
    {
        return this.Session.CreateCriteria<TEntity>().List<TEntity>();
    }
}

To do an EF query you would have to have the context in the Abstract base, not the session. But of course the ifc would be the same.

In this way the queries are themselves encapsulated, and easily testable. Best of all, my code relies only on interfaces. Everything is very clean. Domain (business) objects are just that, e.g. there is no mixing of responsibilities like when using the active record pattern which is hardly testable and mixes data access (query) code in the domain object and in doing so is mixing concerns (object which fetches itself??). Everybody is still free to create POCOs for data transfer.

All in all, much code reuse and simplicity is provided with this approach at the loss of not anything I can imagine. Any ideas?

And thanks a lot to Ayende for his great posts and continued dedication. Its his ideas here (query object), not mine.


What most apply over EF is not a Repository Pattern. It is a Facade pattern (abstracting the calls to EF methods into simpler, easier to use versions).

EF is the one applying the Repository Pattern (and the Unit of Work pattern as well). That is, EF is the one abstracting the data access layer so that the user has no idea they are dealing with SQLServer.

And at that, most "repositories" over EF are not even good Facades as they merely map, quite straightforwardly, to single methods in EF, even to the point of having the same signatures.

The two reasons, then, for applying this so-called "Repository" pattern over EF is to allow easier testing and to establish a subset of "canned" calls to it. Not bad in themselves, but clearly not a Repository.


For me, it's a simple decision, with relatively few factors. The factors are:

  1. Repositories are for domain classes.
  2. In some of my apps, domain classes are the same as my persistence (DAL) classes, in others they are not.
  3. When they are the same, EF is providing me with Repositories already.
  4. EF provides lazy loading and IQueryable. I like these.
  5. Abstracting/'facading'/re-implementing repository over EF usually means loss of lazy and IQueryable

So, if my app can't justify #2, separate domain and data models, then I usually won't bother with #5.

참고URL : https://stackoverflow.com/questions/14110890/not-using-repository-pattern-use-the-orm-as-is-ef

반응형