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の.Netリポジトリ:

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

アイデア私はリポジトリオブジェクトを常にusingステートメントに入れているので、接続が閉じられて処分されますが、ドメインサービスクラスがまだ生存していて、それに2度目の呼び出しがあるので問題として認識しますリポジトリがすでに破棄されているため失敗します。

私はすべてのコードを完全にコントロールしています。粗い穀物サービスの呼び出しのみを設計したいのですが、すべてが正しいとは思わないものがあります。

私はこのようにしているので、ドメインサービスはリポジトリの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です)を読んで、アプリの生涯で不確定な時間に物事を解決したい場合は、型ファクトリの使用をお勧めします。

あなたはリポジトリが必要ですが、のときはを知らないことがわかります。リポジトリを要求する代わりに、を作成するものを要求してください。このように抽象化のレベルは維持され、実装を漏らしていません。

+0

DUH!なぜ私はこれについて考えなかったのか分かりません。ありがとう。 –

+0

@SergioRomero場合によっては、あなた自身が正しい結論に達する前に、一歩後退して他の人との間で問題を解決する必要があることもあります。私はいつもそうしていますが、あなたはいつも膝の中にいれば、デザインサークルに閉じ込められるのはとても簡単です:-( –

+0

カプセル化の違反があります。あなたはその秘密を明らかにしました。リポジトリを書き換えて、各トランザクションの接続を開いたり閉じたりできるようにするのが最善の解決策です。 –

1

あなたが持っている問題は所有権の1つです。 UserDomainServiceクラスはIRepositoryを作成しませんが、それは処分されているので、そのインスタンスの所有権を引き継ぎます。

一般的なルールは、オブジェクトを作成する人がそれを分散することです。つまり、オブジェクトを作成する者は所有者であり、所有者はそのオブジェクトを破棄しなければなりません。

問題の解決策は2つあります。

  1. アダムが明確に説明しているように、IRepositoryFactoryを作成します。そのような工場での方法は、呼び出し元が所有権を取得し、作成したリポジトリを処分する必要があることを明確に伝えます。

  2. そのリポジトリを作成(および注入)する人がそのリポジトリの処分を処理するようにします。 WCFサービスでこれを手動で行うか、IoC/DIフレームワークを使用します。 DIフレームワークを使用する場合、おそらくPer Web Requestの生涯などを調べるべきです。

最後に、IRepositoryIDisposableを実装しています。ソリューション2を選択するときは、IRepositoryからIDisposableインターフェイスを削除することができます。これにより、リソースがアプリケーションに関連していることが隠されます。アプリケーションからのIDisposableの隠蔽は良いことです。そのインタフェースは漏れた抽象であるからです。アプリケーション内からDisposeを呼び出すと、アプリケーション全体が中断されているので、すでにこの問題が発生しています。

関連する問題