12

리포지토리 기반 솔루션의 토대로 LINQ TO SQL 사용. 다음과 같이 내 구현은 다음과 같습니다리포지토리 패턴의 서브 레코드로드

WhereSomethingEqualsTrue() ... 

내 질문은 다음과 같다 :

IRepository

을 그럼 난 같은 결과를 조회하는 데 사용되는 확장 메서드를
FindAll 
FindByID 
Insert 
Update 
Delete 

My Users 저장소에는 N 개의 역할이 있습니다. 역할을 관리하기위한 역할 리포지토리를 생성합니까? 나는 내가이 길을 가면 수십 개의 저장소 (조인 테이블을 제외하고 테이블 당 거의 1 개)를 만들 것이라고 걱정한다. 테이블 당 저장소가 공통적입니까?

답변

31

각 엔티티에 위에 나열된 IRepository 인터페이스의 메소드 목록이있는 것과 같이 하나의 엔티티 (테이블)에만 해당하도록 리포지토리를 작성하는 경우 실제 수행중인 작업은 Active Record입니다. 무늬.

에는 테이블 당 하나의 저장소가 있어야합니다. 도메인 모델에서 집계 및 수행 할 작업을 식별해야합니다. 사용자와 역할은 일반적으로 밀접한 관련이 있으며 일반적으로 응용 프로그램은 함께 작업을 수행합니다. 이는 사용자 및 관련 밀접한 관련 엔터티를 중심으로 단일 저장소를 요구합니다.

나는 귀하의 게시물에서 당신이 seen this example 인 것으로 추측하고 있습니다. 이 예제의 문제점은 모든 저장소가 기본 수준에서 동일한 CRUD 기능을 공유하지만이 점을 넘어서 도메인 기능을 구현하지 않는다는 것입니다. 이 예제의 모든 리포지토리는 동일하게 보입니다. 그러나 실제로는 실제 리포지토리가 모두 동일하게 보이지는 않지만 (인터페이스가 여전히 있어야하지만) 각 리포지토리와 관련된 특정 도메인 작업이 있습니다.

userRepository.FindRolesByUserId(int userID) 
userRepository.AddUserToRole(int userID) 
userRepository.FindAllUsers() 
userRepository.FindAllRoles() 
userRepository.GetUserSettings(int userID) 

등 ...

이 응용 프로그램은 기본 데이터에 대해 수행하기를 원하는 특정 작업, 그리고 저장소는 그것을 제공해야

저장소 (repository) 도메인 작업은 더 좋아 보일 것입니다. 리포지토리는 도메인에서 수행 할 원자 적 작업 집합을 나타냅니다. 일반 저장소를 통해 일부 기능을 공유하고 확장 방법으로 특정 리포지토리를 확장하는 경우 앱에 적합 할 수있는 방법 중 하나입니다.

작업을 완료하기 위해 응용 프로그램에서 여러 저장소를 인스턴스화해야하는 경우에는 희귀한이어야합니다. 필요는 생기지 만 앱의 모든 이벤트 핸들러가 사용자의 입력을 받아들이고 입력이 나타내는 엔티티를 올바르게 인스턴스화하기 위해 여섯 개의 저장소를 저글링하고 있다면 설계상의 문제가있을 수 있습니다.

+1

자, 거기에 가세요. 매일 무언가를 배우십시오. 나 자신의 접근 방식을 조정해야합니다 :-) – Dylan

+0

그래. 나는 또한이 대답으로 무언가를 배운다. 여러 저장소를 갖는 것이 논리적이지만 각 저장소가 집계 주위에 구축되어야한다는 것을 알지 못했습니다. 총체적인 의미를가집니다. –

+0

우수 게시자입니다. –

4

테이블 당 저장소가 공통적입니까?

아니요,하지만 여전히 여러 개의 저장소가있을 수 있습니다. 집계를 중심으로 저장소를 구축해야합니다.

또한, 모든 저장소에서 추상적 인 몇 가지 기능을 수행 할 수 있습니다 ... 그리고, 당신이 Linq에 - 투 - SQL을 사용하고 있기 때문에, 당신은 아마 수 ...

당신은 기본 저장소를 구현할 수있는 일반적인 방식으로 모든 공통 기능을 구현합니다.

다음 예는이 점을 증명하는 데에만 사용됩니다. 그것은 아마도 많은 개선이 필요합니다 ...

interface IRepository<T> : IDisposable where T : class 
    { 
     IEnumerable<T> FindAll(Func<T, bool> predicate); 
     T FindByID(Func<T, bool> predicate); 
     void Insert(T e); 
     void Update(T e); 
     void Delete(T e); 
    } 

    class MyRepository<T> : IRepository<T> where T : class 
    { 
     public DataContext Context { get; set; } 

     public MyRepository(DataContext context) 
     { 
      Context = Context; 
     } 

     public IEnumerable<T> FindAll(Func<T,bool> predicate) 
     { 
      return Context.GetTable<T>().Where(predicate); 
     } 

     public T FindByID(Func<T,bool> predicate) 
     { 
      return Context.GetTable<T>().SingleOrDefault(predicate); 
     } 

     public void Insert(T e) 
     { 
      Context.GetTable<T>().InsertOnSubmit(e); 
     } 

     public void Update(T e) 
     { 
      throw new NotImplementedException(); 
     } 

     public void Delete(T e) 
     { 
      Context.GetTable<T>().DeleteOnSubmit(e); 
     } 

     public void Dispose() 
     { 
      Context.Dispose(); 
     } 
    } 
+2

아니요, 각 저장소는 하나의 집합 루트에 대한 지속성 논리를 캡슐화해야합니다. –

+0

@Johannes 예, 그렇습니다. –

+0

클라이언트 클래스에서 "T FindByID (Func 술어)"함수를 사용하는 방법을 알려주십시오. 코드 데모를 보려고합니다. – Lijo

1

나에게 저장소 패턴은 데이터 액세스 방법론에 대해 얇은 래퍼를 두는 것에 관한 것입니다. 귀하의 경우에 LINQ SQL하지만, NHibernate, 다른 사람 손으로 압연. 내가 뭘 찾았는지에 대한 테이블 당 저장소를 만드는 것은 매우 간단합니다 (bruno 목록과 이미 있습니다). 그것은 물건을 찾고 CRUD 작업을 수행하는 책임이 있습니다.

그러나 Johannes가 언급 한 것처럼 집계 된 루츠를 더 많이 처리하는 서비스 수준이 있습니다. getExistingUser (int id)와 같은 메서드를 사용하여 UserService를 갖게됩니다. 내부적으로 UserRepository.GetById() 메서드를 호출하여 사용자를 검색합니다. 비즈니스 프로세스에서 GetExistingUser()에 의해 반환 된 사용자 클래스가 거의 항상 User.IsInRoles() 속성을 채워야 할 필요가있는 경우 UserRepository RoleRepository에 따라 UserService를 설정하면됩니다. 의사 코드에서는이 같은 것을 볼 수 있었다 :

public class UserRep : IUserRepository 
{ 
    public UserRep(string connectionStringName) 
    { 
     // user the conn when building your datacontext 
    } 

    public User GetById(int id) 
    { 
     var context = new DataContext(_conString); 
     // obviously typing this freeform but you get the idea... 
     var user = // linq stuff 
     return user; 
    } 

    public IQueryable<User> FindAll() 
    { 
     var context = // ... same pattern, delayed execution 
    } 
} 

가 개인적으로 나는이 내부적으로 범위 저장소 클래스를 만들 것 다음 userRep 및 roleRep는 SQL로 LINQ로 구성된다

public class UserService 
{ 
    public UserService(IUserRepository userRep, IRoleRepository roleRep) {...} 
    public User GetById(int id) 
    { 
     User user = _userService.GetById(id); 
     user.Roles = _roleService.FindByUser(id); 
     return user; 
} 

는 다음과 같이 비트 UserService 및 기타 XXXXXService 클래스를 공용으로 유지하여 서비스 API의 소비자를 정직하게 유지하십시오. 다시 말해서 저장소가 데이터 저장소와 대화하는 방식과 더 밀접하게 관련되어 있음을 알지만 서비스 계층은 비즈니스 프로세스의 요구 사항에 더욱 밀접하게 관련되어 있습니다.

나는 종종 실제로 필요한 것을 뱉어내는 서비스 방법을 구축하는 대신에 Linq가 Objects와 그 모든 것들에 대한 유연성을 과장하고 실제로 IQuerable 등을 사용하여을 사용하고있다.사용자 LINQ는 적절하지만 리포지토리를 모든 작업을 수행하려고 시도하지는 않습니다.

public IList<User> ActiveUsersInRole(Role role) 
{ 
    var users = _userRep.FindAll(); // IQueryable<User>() - delayed execution; 
    var activeUsersInRole = from users u where u.IsActive = true && u.Role.Contains(role); 
    // I can't remember any linq and i'm type pseudocode, but 
    // again the point is that the service is presenting a simple 
    // interface and delegating responsibility to 
    // the repository with it's simple methods. 
    return activeUsersInRole; 
} 

그래서 약간 번쩍였다. 내가 정말로 도움이되었는지 확신 할 수는 없지만, 확장 방법으로 너무 멋지게하는 것을 피하고 각 움직이는 부분을 매우 단순하게 유지하는 또 다른 레이어를 추가하는 것이 좋습니다. 나를 위해 일합니다.

1

우리가 Womp가 제안한 것과 같이 상세하게 저장소 계층을 작성한다면 우리는 서비스 계층에 무엇을 넣을 것인가? 우리는 동일한 메소드 호출을 반복해야만 하는가? 대부분은 해당 저장소 메소드에 대한 호출로 구성되며, 컨트롤러 또는 코드 숨김에 사용된다. 여기에는 유효성 검사, 캐싱, 워크 플로, 인증/권한 부여 코드 등을 작성하는 서비스 계층이 있다고 가정합니다. 아니면 기지에서 벗어나나요?

관련 문제