2011-12-15 3 views
8

읽습니다. 내 클래스에 IDisposable 멤버 변수가있는 경우 IDisposable을 구현해야합니다.IDisposable을 구현할 때 생성자의 예외 처리

public class CommissionModel : IDisposable 
    { 
     protected PetaPoco.Database db; 

     public CommissionModel() 
     { 
      string connectionString = "Server=localhost;..."; 

      // The line below may throw an exception (!!!) 
      db = new PetaPoco.Database(connectionString, "mysql");    
     } 

     // Automatically close database connection 
     public void Dispose() 
     { 
      if (db != null) 
       db.Dispose(); 

      db = null; 
     } 

     public void InsertRecord(Record somerecord) 
     { 
      // ... 
      db.Insert(somerecord); 
     } 

문제는 db 멤버의 인스턴스가 어떤 상황에서 실패 할 수 있습니다 : 내 클래스는 데이터베이스 개체 (아래 db 클래스 멤버)가 포함되어 있기 때문에 글쎄, 난으로 IDisposable 자체 인 IDisposable 인터페이스를 구현하고있다. 예외가 생성자에서 throw되고 데이터베이스 객체가 만들어지지 않을 때 어떻게해야합니까? 예외를 다시 던지거나 InsertRecord 메서드에 db != null이 있는지 확인해야합니까?

+0

IMO 예외를 제해야합니다. 더 나은 레이어 분리를 위해 원본 exepction을 더 높은 레벨 1로 래핑 할 수 있습니다. 무엇인가 잘못되었다는 것을 알리는 것이 낫습니다. – Krzysztof

+0

[SOLID] (https://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29), 특히 [SRP] (https://en.wikipedia.org/wiki/Single_responsibility_principle)를 참조하십시오. and [DI] (https://en.wikipedia.org/wiki/Dependency_inversion_principle) – oleksii

답변

1

처리 할 수있는 예외 만 잡아야합니다. 코드에서 변수 db를 초기화하면 데이터베이스 서버와 통신하는 데 문제가 있음을 나타낼 수 있습니다.

제 제안은 코드를 그대로두고 Global.asax의 Application_Error 이벤트 나 CommissionModel을 초기화하는 이벤트와 같은 중앙 위치에서 예외를 처리하는 것입니다.

2

이상적으로 생성자에서 "작업"을 수행하면 안됩니다. 생성자의 작업은 실제로 객체 참조를 연결해야합니다. 이렇게하면 모의 수업을 대신 연결하여이 수업을 단위 테스트 할 수 있습니다.

대신을 시도해보십시오

public CommissionModel(PetaPoco.Database db) { 
    this.db = db; 
} 
+1

+1 더 나은 디자인을 향한 OP를 지적하는 가장 좋은 대답입니다. 솔리드 원칙을 언급하고'PetaPoco.Database' 대신'IDatabase'를 사용할 수도 있습니다 – oleksii

+0

@oleksii, 나는 그것에 대해 동의하지 않으면 안됩니다. 'CommissionModel' 구현의 배경은 모든 저수준 데이터 조작과 데이터베이스 처리 기능을 코드의 다른 부분과 분리하는 것입니다. 게다가 어느 날 PetaPoco 대신 다른 ORM 프레임 워크로 전환하기로 결정할 수도 있습니다. – ezpresso

+0

@ezpresso * "[...] 언젠가 PetaPoco 대신 다른 ORM 프레임 워크로 전환하기로 결정할 수 있습니다."* 바로 PetaPoco로 구현할 인터페이스를 전달해야하는 이유입니다. 원하는 경우 다른 ORM으로 변경할 수 있습니다. 나는 당신이 어떤 특정한 점에 동의하지 않는다는 의미에서 당신을 따라 오는지 확신하지 못합니다. pls 나에게 설명해 주시겠습니까? – oleksii

3

생성자가 예외를 throw했을 경우는, 코드의 소비자가 클래스의 인스턴스에 대한 참조를 수신하지 않습니다. 부분적으로 초기화 된 클래스 메모리는 결국 수집되고 Dispose는 호출되지 않습니다.

외부 조건 (잘못된 매개 변수 값과 같은 오용이 아닌)으로 인해 실패 할 수있는 작업을 "연결"과 같은 별도의 메서드로 이동하는 것이 좋습니다.

2

아무 것도 할 필요가없는 것처럼 보입니다.

건설 공정이 중단되면 자원이 생성되지 않습니다. 대부분의 사용 시나리오 (클래스 외부에 위치)에서는 Dispose()가 호출되지 않습니다. 하지만 그럴 때도 코드 if (db != null) 만 있으면 충분합니다.

몇 가지 사소한 점 :

  • 폐기 내부 db = null;()는 무의미하다. 생성자가 자원하고 응용 프로그램에 리턴됩니다 시간을 획득 시간 사이 예외 (*)가 발생할 수 있다는 방법이없는 경우
  • InsertRecord()은 걱정할 아무것도, if (db != null)
1

을 선택하여 시작합니다 약. 일부 불쾌한 사람 잡아 먹는 귀신은 트레드를 호출 의미하는 경우 ThreadAbortException이 발생하는 것이 거의 항상 가능

 
public class CommissionModel : IDisposable 
    { 
     protected PetaPoco.Database db; 
     protected OtherResourceType resource2; 

     public CommissionModel() 
     { 
      Boolean ok; 
      string connectionString = "Server=localhost;..."; 

      ok = false; 
      try 
      { 
       // Either of the next two lines may throw an exception 
       db = new PetaPoco.Database(connectionString, "mysql"); 
       resource2 = new OtherResourceType(); 
       // Once we make it this far, we should be successful 
       ok = true; 
      } 
      finally 
      { 
       if (!ok) 
        Dispose(); 
      } 
     } 

     // Automatically close database connection 
     public void Dispose() 
     { 
      Zap(ref db); // Method to Dispose and null out, only if not null 
      Zap(ref resource2); 
     } 
    } 

(*) : 예외가 같은 상황에서 발생할 수있는 것보다 가능하다면

, 나는 같은 패턴을 건의 할 것입니다. 코드를 중단하십시오. 그러나 어떤 경우에도 그렇게 할 필요가 없습니다.