4

ASP.NET 웹 응용 프로그램에서 리포지토리 패턴 구현을 완료하는 방법을 알아 내려고합니다.의존성 주입을 사용하여 작업 단위 컨테이너를 리포지토리 생성자로 전달하는 방법

현재 도메인 클래스별로 저장소 인터페이스가 있는데, 예를 들어. 해당 클래스의 인스턴스로드 및 저장.

각 저장소 인터페이스는 NHibernate 항목을 수행하는 클래스에 의해 구현됩니다. Castle Windsor는 클래스의 DI를 web.config에 따라 인터페이스로 분류합니다. 구현 된 클래스의 예는 아래에 제공된다 :

public class StoredWillRepository : IStoredWillRepository 
    { 
    public StoredWill Load(int id) 
    { 
     StoredWill storedWill; 
     using (ISession session = NHibernateSessionFactory.OpenSession()) 
     { 
     storedWill = session.Load<StoredWill>(id); 
     NHibernateUtil.Initialize(storedWill); 
     } 
     return storedWill; 
    } 

    public void Save(StoredWill storedWill) 
    { 
     using (ISession session = NHibernateSessionFactory.OpenSession()) 
     { 
     using (ITransaction transaction = session.BeginTransaction()) 
     { 
      session.SaveOrUpdate(storedWill); 
      transaction.Commit(); 
     } 
     } 
    } 
    } 

는 이전 스레드에 지적 된 바와 같이, 저장소 클래스는 모든 방법을 인스턴스화보다는 작업 용기의 유닛 (즉 ISession)를 수용 할 필요가있다.

필요할 때마다 (예 : 속성에서) 작업 단위 컨테이너가 각 aspx 페이지에 의해 생성 될 것으로 예상됩니다.

Windsor가 나를 위해이 작업 단위 컨테이너 인스턴스를 생성 할 때이 작업 단위 컨테이너 인스턴스가 StoredWillRepository의 생성자로 전달되도록 지정하려면 어떻게해야합니까?

또는이 패턴이 완전히 잘못 되었습니까?

귀하의 조언에 다시 한 번 감사드립니다.

데이비드

답변

0

기술적으로, 내 질문에 대한 답은 익명 타입과 생성자 인수를 지정할 수 있습니다 container.Resolve의 과부하 사용하는 것입니다

IUnitOfWork unitOfWork = [Code to get unit of work]; 
_storedWillRepository = container.Resolve<IStoredWillRepository>(new { unitOfWork = unitOfWork }); 

을하지만 현실을 직시하자를, 답변 모두에 의해 제공 그렇지 않으면 훨씬 더 유익했습니다.

+0

당신이 말한 것처럼 기술적으로는 건축가 적으로 가장 좋은 해결책이 아니더라도 제 질문에 대한 대답입니다. 나는 너를 똑딱 거리고있다. – David

0

나는 당신과 매우 유사한 구조를 가지고 있고, 여기에 내가 질문 해결 방법은 다음과 같습니다

1) 나는 별도의 클래스를 가지고, 각각의 방법에 내 컨테이너를 지정하려면을 ("SessionManager") 그런 다음 정적 속성을 통해 호출합니다. 이렇게하면 Save 구현을 사용하는 예제가 있습니다.

private static ISession NHibernateSession 
{ 
    get { return SessionManager.Instance.GetSession(); } 
} 

public T Save(T entity) 
{ 
    using (var transaction = NHibernateSession.BeginTransaction()) 
    { 
     ValidateEntityValues(entity); 
     NHibernateSession.Save(entity); 

     transaction.Commit(); 
    } 

    return entity; 
} 

2) 내 컨테이너가 각 ASPX 페이지에서 만들어지지 않습니다. global.asax 페이지에서 모든 NHibernate의 장점을 인스턴스화합니다.

** 몇 가지 **

3) 당신은 부하를 인스턴스화하는 도우미가 없어도 튀어. 로드 대신 가져 오기를 사용할 수도 있습니다. 자세한 내용은 @Difference between Load and Get.

4) 현재 코드를 사용하면 필요한 각 도메인 객체 (StoredWillRepository, PersonRepository, CategoryRepository 등)에 대해 동일한 코드를 반복해야합니다. 이는 드래그처럼 보입니다. 당신은 아주 잘처럼, NHibernate에에서 동작하는 generic class을 사용할 수 있습니다 내 구현에서

public class Dao<T> : IDao<T> 
{ 
    public T SaveOrUpdate(T entity) 
    { 
     using (var transaction = NHibernateSession.BeginTransaction()) 
     { 
      NHibernateSession.SaveOrUpdate(entity); 
      transaction.Commit(); 
     } 

     return entity; 
    } 
} 

, 그때 something like 사용할 수 있습니다

Service<StoredWill>.Instance.SaveOrUpdate(will); 
+0

대단히 감사합니다. 번호가 매겨진 코멘트에서 귀하의 요점에 답하겠습니다 : – David

+0

1) 정적 ISession? 내가 잘못 이해하지 않는 한, 이는 스레드 안전하지 않은 ISession이 응용 프로그램의 모든 스레드간에 공유된다는 것을 의미합니다. 매우 위험합니다. 이게 맞지 않아? – David

+0

2) 무엇을위한 컨테이너? 죄송합니다, 무슨 뜻인지 모르겠군요. – David

1

을 I는 NHibernate에 위에 구축 지속성 프레임 워크를 가지고 몇 가지 웹 응용 프로그램에서 사용됩니다. 그것은 IRepositoryIRepository<T> 인터페이스 뒤에 NH 구현을 숨 깁니다. Unity가 제공 한 구체적인 인스턴스를 가지고 있습니다 (따라서 이론적으로 NHibernate를 Entity Framework로 바꾸는 것이 가능합니다).

Unity는 의존성 삽입 그 자체가 아닌 생성자 매개 변수의 전달을 지원하지 않기 때문에 (또는 적어도 사용하고있는 버전은 지원하지 않기 때문에) 현행 NHession을 전달할 수 없습니다. 하지만 나는 UOW의 모든 객체가 같은 Session을 공유하기를 원한다.

난 스레드 단위에 ISession에 대한 액세스를 관리하는 제어 저장소 클래스함으로써이 문제를 해결이 예에서

public static ISession Session 
    { 
     get 
     { 
      lock (_lockObject) 
      { 
       // if a cached session exists, we'll use it 
       if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY)) 
       { 
        return (ISession)PersistenceFrameworkContext.Current.Items[NHibernateRepository.SESSION_KEY]; 
       } 
       else 
       { 
        // must create a new session - note we're not caching the new session here... that's the job of 
        // BeginUnitOfWork(). 
        return _factory.OpenSession(new NHibernateInterceptor()); 
       } 
      } 
     } 
    } 

PersistenceFrameworkContext.Current.Items는에 있지 않은 경우 어느 ThreadStatic 저장되는 IList<object> 액세스 웹 컨텍스트에 있거나 웹 컨텍스트에있는 경우 HttpContext.Current.Items 내에 있어야합니다 (스레드 풀 문제를 방지하기 위해). 속성에 대한 첫 번째 호출은 저장된 팩시밀리 인스턴스에서 ISession을 인스턴스화합니다. 이후 호출은 저장소에서이를 검색합니다. 잠금 기능을 사용하면 물건의 속도가 약간 느려지지만 appdomain-scoped static ISession 인스턴스를 잠그는 것만 큼은 아닙니다.

그렇다면 BeginUnitOfWorkEndUnitOfWork의 UOW를 처리하는 방법이 있습니다. 특별히 중첩 된 UOW를 허용하지 않았습니다. 솔직하게 관리 할 고통 이었기 때문입니다.

public void BeginUnitOfWork() 
    { 
     lock (_lockObject) 
     { 
      if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY)) 
       EndUnitOfWork(); 

      ISession session = Session; 
      PersistenceFrameworkContext.Current.Items.Add(SESSION_KEY, session); 
     } 
    } 

    public void EndUnitOfWork() 
    { 
     lock (_lockObject) 
     { 
      if (PersistenceFrameworkContext.Current.Items.ContainsKey(SESSION_KEY)) 
      { 
       ISession session = (ISession)PersistenceFrameworkContext.Current.Items[SESSION_KEY]; 
       PersistenceFrameworkContext.Current.Items.Remove(SESSION_KEY); 
       session.Flush(); 
       session.Dispose(); 
      } 
     } 
    } 

마지막으로, 방법의 한 쌍의 도메인 형 특정 저장소에 대한 액세스 제공. (여기서, PersistentObject<T> 기본 클래스 제공하는 ID이고, 지지체와 동일)

public IRepository<T> For<T>() 
     where T : PersistentObject<T> 
    { 
     return Container.Resolve<IRepository<T>>(); 
    } 

    public TRepository For<T, TRepository>() 
     where T : PersistentObject<T> 
     where TRepository : IRepository<T> 
    { 
     return Container.Resolve<TRepository>(); 
    } 

따라서 주어진 저장소에 대한 액세스는 패턴입니다.

NHibernateRepository.For<MyDomainType>().Save(); 

특정 유형의 전문 저장소를 가지고 어디

MyDomainType.Repository.Save(); 

을 사용할 수 있도록 이상 facaded 그때 IRepository<T>, 연장 구현 내 IRepository<T> 구현 상속에서 파생 인터페이스를 생성 (즉, 그것이 IRepository<T>에서 얻을 수있는 것보다 더 많은 필요) 및 도메인 유형 자체에 내가 사용하는 정적 Repository 속성을 재정의 supplyi을 담당 외관 클래스는 [실제 제품에 뭔가 덜 검은 제비 갈매기라고] new

new public static IUserRepository Repository 
    { 
     get 
     { 
      return MyApplication.Repository.For<User, IUserRepository>(); 
     } 
    } 

(MyApplication 유니티를 통해 Repository 인스턴스를 도메인 클래스 내에서 특정 NHibernate 저장소 구현에 의존하지 않아야합니다.)

이것은 리포지토리 구현을 위해 Unity를 통해 완전히 플러그 가능한 기능을 제공하며, 코드를 통해 저장소로 쉽게 이동할 수 있습니다. 투명하고 스레드 당 ISession 관리.

위의 코드보다 코드가 훨씬 많습니다 (예제 코드를 간단하게 단순화했습니다).하지만 일반적인 생각을 알 수 있습니다. 내 웹 응용 프로그램에서

MyApplication.Repository.BeginUnitOfWork(); 
User user = User.Repository.FindByEmail("[email protected]"); 
user.FirstName = "Joe"; // change something 
user.LastName = "Bloggs"; 
// you *can* call User.Repository.Save(user), but you don't need to, because... 
MyApplication.Repository.EndUnitOfWork(); 
// ...causes session flush which saves the changes automatically 

, 정말 BeginUnitOfWorkEndUnitOfWork 각각 BeginRequestEndRequest에서 호출되는 세션 당 요청 수 있습니다.

+0

정말 대단합니다. 내 코드가 당신의 장점을 사용할 수 있는지 확실히 확인할 것입니다. xD – rebelliard

+0

스택 오버 플로우에는 '아마도 옳은 대답이지만'솔직히 이해할 수없는 버튼이 필요합니다. ' 나는 그것을 여기에서 바로 클릭 할 것이다. 나는 주말 내내 그것을 소화하려고 노력할 것이다. 그러나 술에 취하는 것이 차선책일지도 모른다. 적어도 나는 당신의 이메일 주소를 고맙게 생각했다! – David

+0

술 취하는 것은 오늘 저녁 늦게 복사 할 훌륭한 아이디어입니다 .--) 코드에 관해서는 모든 간접적 인 지시와 메타 프로그래밍은 더 읽기가 더 어렵습니다. 퍼시스턴스 프레임 워크의 실제적이고 완벽한 구현은 여기에있는 가장 복잡한 코드에 관한 것입니다.하지만 한 번 해본 적이 있다면 다시 할 필요가 없습니다. 그것은 여러 번에 걸쳐 스스로 지불되었습니다. –

관련 문제