2011-12-15 5 views
8

私のクラスにIDisposableというメンバ変数がある場合はIDisposableを実装する必要があります。私のクラスはIDisposableを自身でデータベースオブジェクト(以下dbクラスのメンバー)が含まれているので、まあ、私は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メンバーのインスタンス化は、いくつかの状況下で失敗する可能性があります。 例外がコンストラクタでスローされ、データベースオブジェクトが作成されない場合はどうすればよいですか?例外を元に戻すか、InsertRecordメソッドでdb != nullかどうかを確認する必要がありますか?

+0

IMO例外を元に戻す必要があります。元のexepctionをより高いレベルのものにラップして、レイヤーの分離を改善することができます。何かが間違っていることを知らせる方が良いです。 – Krzysztof

+0

[SOLID](https://en.wikipedia.org/wiki/Solid_%28object-oriented_design%29)、具体的には[SRP](https://en.wikipedia.org/wiki/Single_responsibility_principle)をご覧ください。と[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を指している最高の答えです。また、私はSOLIDの原則について言及し、 'PetaPoco.Database'の代わりに' IDatabase'を使用します。 – oleksii

+0

@oleksii、私はあなたに同意しなければなりません。 'CommissionModel'の実装の背後にある理由は、すべての低レベルのデータ操作とデータベース処理機能をコードの他の部分から分離するためです。さらに、ある日、私はPetaPocoの代わりに別のORMフレームワークに切り替えることに決めました。 – ezpresso

+0

@ezpresso * "[...]いつか私はPetaPocoの代わりに別のORMフレームワークに切り替えることができます" *なぜ、あなたは今、PetaPocoで実装されるインターフェースを渡す必要があるかもしれない他のORMに変更することができます。私はある意味で、あなたがそれに同意しないことをあなたに追いかけるのかどうかはわかりません。あなたは私にそれを説明することができますか? – oleksii

3

あなたのコンストラクタが例外をスローした場合は、あなたのコードの消費者は、自分のクラスのインスタンスへの参照を受け取ることはありません。部分的に初期化されたクラスメモリは最終的に収集され、Disposeは呼び出されません。

Iは、一般的に「接続」のように、別の方法(無効なパラメータ値と同様に、誤用によるず)により外部状況に失敗することが移動操作をお勧めします。

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); 
     } 
    } 

(*):例外は、そのような状況で発生する可能性があるよりも、それが可能だ場合

は、私のようなパターンをお勧めします。あなたのコードを打ち切りますが、実際にはそれについて何もする必要はありません。

関連する問題