2012-04-03 4 views
5

I가이 저장소 도메인 서비스에 IoC 컨테이너로 주입 등처럼 사용된다는 IDisposable

public class Repository : IRepository, IDisposable 
{ 
    private readonly IUnitOfWork UnitOfWork; 
    private SqlConnection Connection; 

    public Repository(IUnitOfWork unitOfWork, connectionString) 
    { 
     UnitOfWork = unitOfWork; 
     Connection = new SqlConnection(connectionString); 
     Connection.Open(); 
    } 

    public MyObject FindBy(string userName) 
    { 
     //...Ado .Net command.ExecuteReader, etc. 
    } 
} 

다음 ADO 닷넷 저장소 :

public class UserDomainService : IUserDomainService 
{ 
    private readonly IRepository Repository; 

    public UserDomainService(IRepository repository) 
    { 
     Repository = repository; 
    } 

    public User CreateNewUser(User user) 
    { 
     using(Repository) 
     { 
     var user = Repository.FindBy(user.UserName); 
     if(user != null) 
      throw new Exception("User name already exists!"); 

     Repository.Add(user); 
     Repository.Commit(); 
     } 
    } 
} 

아이디어 나는 항상 Repository 객체를 using 문에 집어 넣는다. 그래서 끝날 때 커넥션은 닫히고 처리된다.하지만 Domain Service 클래스가 아직 살아 있고 거기에 두 번째 호출이 있기 때문에 문제가된다. 저장소가 이미 삭제 되었기 때문에 실패합니다.

이제 모든 코드를 완벽하게 제어 할 수 있었고 거친 곡물 서비스 호출 만 디자인하려고했지만 옳지 않은 모든 것에 대해 뭔가가있었습니다.

이렇게하면 도메인 서비스가 저장소의 OpenConnection 및 CloseConnection 메서드를 알고 있다는 것을 피할 수 있습니다.

본 설계가 본질적으로 좋지 않습니까? 아니면 더 좋은 방법이 있습니까?

생각한 후 : 요청이 도착할 때 모든 종속성 트리가 WCF 수준에서 생성되며 물론 저장소 생성자에서 발생하므로 그 순간에 연결이 열리는 것을 볼 수 있습니다. 이 특정 호출의 지속 기간 동안 만 열려 있기 때문에 그렇게 나쁘지 않습니다. 이 가정에 맞지 않습니까? 아니면 프로세스 초기에 DB 연결을 열어서 나쁘게 무언가를하고 있습니까?

+0

'IRepository'는'Repository'와 밀접하게 결합되어 있습니까? 의미, 그것은'Find'와 같은 그것의 메소드를 포함합니까? 그렇다면 그 인터페이스는'IDisposable'을 암시합니까? –

+0

나는 [ServiceContainer, IoC 및 일회용 객체] (http://stackoverflow.com/questions/556580/servicecontainer-ioc-and-disposable-objects)와 관련된 내 자신의 질문이 있습니다. –

+1

왜'저장소 '에'SqlConnection'이 필요한가요? 'IUnitOfWork'에 대해 더 좋아 보입니다. – Steven

답변

8

인스턴스 자체가 아니라 필요한 인스턴스를 생성하는 팩토리를 주입하십시오.

IRepositoryFactory을 가져와 IRepository을 만들고 사용할 때마다 폐기하십시오. 이렇게하면 도메인 서비스 나 공장 모두 처분 할 필요가 없습니다. 또한 중요하게도 코드를 하드 코딩하는 것과 달리 구현을 주입하여 코드를 추상화합니다.

public class UserDomainService : IUserDomainService 
{ 
    private readonly IRepositoryFactory RepositoryFactory; 

    public UserDomainService(IRepositoryFactory factory) 
    { 
     RepositoryFactory = factory; 
    } 

    public User CreateNewUser(User user) 
    { 
     using (IRepository repository = RepositoryFactory.Create()) 
     { 
     var user = repository.FindBy(user.UserName); 
     if(user != null) 
      throw new Exception("User name already exists!"); 

     repository.Add(user); 
     repository.Commit(); 
     } 
    } 
} 

항상 필요한 유형을 주입 할 필요는 없습니다. Castle Windsor (그의 사고 방식은 register-resolve-release)를 읽으면 앱의 수명에서 불확실한 시간에 물건을 해결하기를 원한다면 Type Factory를 사용하는 것이 좋습니다.

리포지토리가 필요하지만 일 때 을 모를 수 있습니다. 저장소를 요청하는 대신 을 생성하는 것을 요청하십시오. 따라서 추상화 수준이 유지되고 구현을 유출하지 않았습니다.

+0

DUH! 왜 내가 이것에 대해 생각하지 않았는지 모르겠다. 감사. –

+0

@SergioRomero 경우에 따라 올바른 결론을 내리기 전에 다른 사람들과 문제를 해결해야 할 때도 있습니다. 언제나 그럴 수 있습니다. 항상 무언가 깊숙이 들어가면 디자인 서클에 덫을 놓는 것은 매우 쉽습니다 .- ( –

+0

캡슐화에 대한 위반입니다. 서비스는 평생에 대해 알 필요가 없습니다. 가장 좋은 해결책은 리포지토리를 다시 작성하여 각 트랜잭션에 대한 연결을 열고 닫을 수있게하는 것입니다. –

1

문제는 소유권 중 하나입니다. UserDomainService 클래스는 IRepository을 만들지 않지만 처리하기 때문에 해당 인스턴스의 소유권을 여전히 사용합니다.

일반적으로 개체를 만드는 사람은 그것을 왜곡해야합니다. 다시 말해, 객체를 생성 한 사람은 소유자이고 소유자는 객체를 파괴해야합니다.

두 가지 해결책이 있습니다.

  1. 아담이 분명히 설명하는대로 IRepositoryFactory을 만듭니다.그러한 공장의 CreateNewRepository() 메소드는 호출자가 소유권을 얻었음을 명확하게 전달하고 생성 된 저장소를 처리해야합니다.

  2. 해당 저장소를 생성 (및 주입)하는 사람이 해당 저장소의 처리를 처리하게하십시오. WCF 서비스에서이 작업을 수동으로 수행하거나 IoC/DI 프레임 워크를 사용합니다. DI 프레임 워크를 사용하는 경우에는 웹 당 요청 시간 또는 이와 유사한 것을 고려해야합니다.

마지막주의 사항 IRepositoryIDisposable을 구현합니다. 솔루션 2를 선택하면 IRepository에서 IDisposable 인터페이스를 제거 할 수 있습니다.이 인터페이스는 응용 프로그램에서 자원이 관련되어 있다는 사실을 숨 깁니다. 응용 프로그램에서 IDisposable을 숨기는 것은 좋은 일입니다. 인터페이스가 새는 추상화이기 때문입니다. 응용 프로그램 내에서 Dispose을 호출하면 전체 응용 프로그램이 손상된 것이므로 이미 발생했습니다.