문제

도메인 모델에 대한 데이터 액세스를 캡슐화하는 리포지토리 (예 : Contactrepository, Userrepository 등)가 있습니다.

내가보고 있었을 때 데이터 검색, 예를 들어

  • 이름이 XYZ로 시작하는 연락처를 찾습니다
  • 1960 년 이후 생일이있는 연락처

    (등),

다음과 같은 저장소 방법을 구현하기 시작했습니다 FirstNamestArtSwith (String Prefix) 그리고 Youngth thanbirthyear (Int Year), 기본적으로 많은 예제를 따릅니다.

그런 다음 문제를 겪었습니다. 여러 검색을 결합해야한다면 어떻게해야합니까? 위와 같은 각 저장소 검색 방법은 실제 도메인 개체의 유한 한 세트 만 반환합니다. 더 나은 방법을 찾기 위해 나는 글을 쓰기 시작했다 확장 방법 iqueryableu003CT> , 예 : 이것 :

public static IQueryable<Contact> FirstNameStartsWith(
               this IQueryable<Contact> contacts, String prefix)
{
    return contacts.Where(
        contact => contact.FirstName.StartsWith(prefix));
}        

이제 나는 다음과 같은 일을 할 수 있습니다

ContactRepository.GetAll().FirstNameStartsWith("tex").YoungerThanBirthYear(1960);

그러나 나는 연장 방법을 쓰고 (그리고 ContactsQueryableExtensions 온통, 나는 적절한 저장소에 모든 것을 갖추어 "좋은 그룹화"를 잃습니다.

이것이 실제로 그렇게하는 방법입니까, 아니면 같은 목표를 달성하는 더 좋은 방법이 있습니까?

도움이 되었습니까?

해결책

@alex- 나는 이것이 오래된 질문이라는 것을 알고 있지만, 내가 할 일은 저장소가 할 수있게하는 것입니다. 정말 간단한 것들 뿐. 즉, 테이블이나보기에 대한 모든 레코드를 얻으십시오.

그런 다음 서비스 계층에서 (N-Tiered 솔루션을 사용하고 있습니까? :)) 모든 '특별한'쿼리를 처리합니다.

좋아, 예제 시간.

저장소 레이어

ContactRepository.cs

public IQueryable<Contact> GetContacts()
{
    return (from q in SqlContext.Contacts
            select q).AsQueryable();
}

멋지고 간단합니다. SqlContext 당신의 인스턴스입니다 EF Context .. Entity 그것에 호출되었습니다 Contacts .. 기본적으로 SQL 연락처 클래스입니다.

이것은 그 방법이 기본적으로 수행한다는 것을 의미합니다. SELECT * FROM CONTACTS ...하지만 해당 쿼리로 데이터베이스를 치는 것은 아닙니다. 지금은 쿼리 일뿐입니다.

OK .. 다음 레이어 .. 발 차기 ... 우리가 간다 (처음 누구나?)

서비스 계층

ContactService.cs

public  ICollection<Contact> FindContacts(string name)
{
    return FindContacts(name, null)
}

public ICollection<Contact> FindContacts(string name, int? year)
{
   IQueryable<Contact> query = _contactRepository.GetContacts();

   if (!string.IsNullOrEmpty(name))
   {
       query = from q in query
               where q.FirstName.StartsWith(name)
               select q;
   }

   if (int.HasValue)
   {
       query = from q in query
               where q.Birthday.Year <= year.Value
               select q);
    }

    return (from q in query
            select q).ToList();
}

완료.

따라서 요약하자. 먼저, 우리는 간단한 것으로 시작합니다.연락처에서 모든 것을 얻으십시오'쿼리. 이제 이름이 제공되면 모든 연락처를 이름별로 필터링하기 위해 필터를 추가하십시오. 다음으로, 우리가 1 년을 제공한다면, 우리는 생일을 해마다 필터링합니다. 마지막으로, 우리는 DB (이 수정 된 쿼리와 함께)를 누르고 어떤 결과를 다시 얻었는지 확인합니다.

메모:-

  • 단순성을 위해 의존성 주입을 생략했습니다. 적극 권장하는 것 이상입니다.
  • 이것은 모든 pseduo 코드입니다. 테스트되지 않은 (컴파일러에 대해) 아이디어를 얻습니다 ....

테이크 아웃 포인트

  • 서비스 계층은 모든 스마트를 처리합니다. 그것이 필요한 데이터를 결정하는 곳입니다.
  • 저장소는 테이블에서 간단한 선택 * 또는 테이블에 간단한 삽입/업데이트입니다.

행운을 빕니다 :)

다른 팁

나는 현재 직장에서 시작한 후 최근에 이것에 대해 많이 생각해 왔습니다. 나는 리포지토리에 익숙합니다. 그들은 당신이 제안한대로 베어 뼈 리포지토리를 사용하여 완전한 iqueryable 경로로갑니다.

Repo 패턴이 사운드라고 생각하고 응용 프로그램 도메인의 데이터를 사용하는 방법을 설명하는 데 반 효과적인 작업을 수행합니다. 그러나 당신이 설명하는 문제는 분명히 발생합니다. 간단한 응용 프로그램을 넘어서 지저분하고 빠릅니다.

아마도 여러 가지 방법으로 데이터를 요구하는 이유를 다시 생각할 수있는 방법이 있습니까? 그렇지 않다면, 나는 하이브리드 접근법이 가장 좋은 방법이라고 생각합니다. 재사용품에 대한 리포 메소드를 만듭니다. 실제로 의미가있는 것들. 건조하고 그 모든 것. 그러나 그 일회성? 왜 IQueryable과 당신이 할 수있는 섹시한 일을 활용하지 않겠습니까? 당신이 말했듯이, 그것에 대한 방법을 만드는 것은 바보 같은 일이지만, 데이터가 필요하지 않다는 것을 의미하지는 않습니다. 드라이가 실제로 적용되지 않습니까?

이 작업을 잘 수행하려면 징계가 필요하지만 실제로 적절한 길이라고 생각합니다.

나는 이것이 오래되었다는 것을 알고 있지만, 나는 최근에 같은 문제를 다루고 있었고, Chad와 같은 결론에 도달했습니다. 약간의 훈련으로 확장 방법과 저장소 방법의 하이브리드가 가장 잘 작동하는 것 같습니다.

(Entity Framework) 응용 프로그램에서 내가 따르던 몇 가지 일반적인 규칙 :

주문 쿼리

이 방법이 순서에만 사용되는 경우 작동하는 확장 방법을 작성하는 것이 좋습니다. IQueryable<T> 또는 IOrderedQueryable<T> (기본 제공 업체를 활용하기 위해.) 예를 들어

public static IOrderedQueryable<TermRegistration> ThenByStudentName(
    this IOrderedQueryable<TermRegistration> query)
{
    return query
        .ThenBy(reg => reg.Student.FamilyName)
        .ThenBy(reg => reg.Student.GivenName);
}

이제 사용할 수 있습니다 ThenByStudentName() 내 리포지토리 클래스 내에서 필요에 따라.

단일 인스턴스를 반환하는 쿼리

메소드에 원시 매개 변수 별 쿼리가 포함되면 일반적으로 ObjectContext 쉽게 만들 수 없습니다 static. 이 방법은 저장소에 남겨 둡니다. 예를 들어

public Student GetById(int id)
{
    // Calls context.ObjectSet<T>().SingleOrDefault(predicate) 
    // on my generic EntityRepository<T> class 
    return SingleOrDefault(student => student.Active && student.Id == id);
}

그러나 메소드가 대신 쿼리를 포함하는 경우 EntityObject 그것의 사용 내비게이션 속성, 일반적으로 만들 수 있습니다 static 매우 쉽게, 확장 방법으로 구현되었습니다. 예를 들어

public static TermRegistration GetLatestRegistration(this Student student)
{
    return student.TermRegistrations.AsQueryable()
        .OrderByTerm()
        .FirstOrDefault();
}

이제 편리하게 쓸 수 있습니다 someStudent.GetLatestRegistration() 현재 범위에서 저장소 인스턴스가 필요하지 않습니다.

쿼리 컬렉션을 쿼리합니다

메소드가 일부를 반환하는 경우 IEnumerable, ICollection 또는 IList, 나는 그것을 만들고 싶다 static 가능하면 저장소에 두십시오 내비게이션 속성을 사용하더라도 예를 들어

public static IList<TermRegistration> GetByTerm(Term term, bool ordered)
{
    var termReg = term.TermRegistrations;
    return (ordered)
        ? termReg.AsQueryable().OrderByStudentName().ToList()
        : termReg.ToList();
}

이것은 내 때문입니다 GetAll() 방법은 이미 저장소에 살고 있으며 확장 방법의 혼란을 피하는 데 도움이됩니다.

확장 방법으로 이러한 "컬렉션 getters"를 구현하지 않는 또 다른 이유는 반환 유형이 암시되지 않기 때문에 더 많은 장황한 이름을 의미하기 때문입니다. 예를 들어, 마지막 예제는됩니다 GetTermRegistrationsByTerm(this Term term).

이게 도움이 되길 바란다!

6 년 후, @alex가 그의 문제를 해결했다고 확신하지만, 받아 들여진 대답을 읽은 후 나는 2 센트를 추가하고 싶었습니다.

확장의 범용 IQueryable a 저장소 유연성을 제공하고 소비자가 데이터 검색을 사용자 정의 할 수 있도록 권한을 부여합니다. Alex가 이미 한 것은 좋은 일입니다.

a의 주요 역할 서비스 계층 is to adhere to the 우려의 분리 원칙과 주소 명령 로직 비즈니스 기능과 관련이 있습니다.

실제 응용 프로그램에서 쿼리 로직 종종 저장소 자체가 제공하는 검색 역학 이외의 확장이 필요하지 않습니다 (예 : 값 변경, 유형 변환).

다음 두 시나리오를 고려하십시오.

IQueryable<Vehicle> Vehicles { get; }

// raw data
public static IQueryable<Vehicle> OwnedBy(this IQueryable<Vehicle> query, int ownerId)
{
    return query.Where(v => v.OwnerId == ownerId);
}

// business purpose
public static IQueryable<Vehicle> UsedThisYear(this IQueryable<Vehicle> query)
{
    return query.Where(v => v.LastUsed.Year == DateTime.Now.Year);
}

두 방법 모두 간단한 쿼리 확장이지만 미묘하지만 역할이 다릅니다. 첫 번째는 간단한 필터이며, 두 번째는 비즈니스 요구 (예 : 유지 보수 또는 청구)를 의미합니다. 간단한 응용 프로그램에서는 저장소에서 둘 다 구현할 수 있습니다. 보다 이상적인 시스템에서 UsedThisYear 서비스 계층에 가장 적합하며 (일반 인스턴스 방법으로 구현 될 수 있음) 더 잘 촉진 할 수 있습니다. CQRS 분리 전략 명령 그리고 쿼리.

주요 고려 사항은 (a) 저장소의 주요 목적 및 (b) 얼마나 많은 것을 준수하고 싶습니까? CQRS 그리고 DDD 철학.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top