2009-06-12 7 views
41

私は最終的にC#のIoCとDIの周りを頭で囲んでいますが、いくつかのエッジで苦労しています。私はUnityコンテナを使用していますが、私はこの質問がより広く適用されると思います。IDisposableとIoCをどのように調整しますか?

IoCコンテナを使用してIDisposableを実装するインスタンスをディスペンスしてください!あなたはDispose()をすべきかどうか知っていますか?インスタンスはあなたのためだけに作成されている可能性があります。そのため、Dispose()を実行する必要があります。インスタンスが他の場所で管理されているインスタンスである可能性があります。コードの中にはあなたには何も言われていません。実際、これは設定に基づいて変わるかもしれません!これは私にとって致命的なようです。

この曖昧さを処理する良い方法は、IoCの専門家が記述できますか?

+1

私のソリューションは、適切かつ十分に成文化寿命管理でIOCを使用することです。(彼らは少し異なる動作が)AutoFacと城ウィンザーは、持っています。デフォルトのライフタイムマネージャの下で過渡現象を扱うとき、ユニット2.1は単に失敗します。 – user2864740

答えて

7

AutoFacは、ネストされたコンテナの作成を許可することでこれを処理します。コンテナの処理が完了すると、IDisposableオブジェクトがすべて自動的に破棄されます。 More here

..サービスを解決する際、Autofacは解決された使い捨て可能な(IDisposable)コンポーネントを追跡します。 作業単位の最後に、関連するライフタイムスコープを廃棄し、Autofacは解決されたサービスを自動的にクリーンアップ/廃棄します。

+0

非常に興味深い。私はAutoFacのことを知らなかった。私はUnityのようなことをするのは難しいとは思わない。私はこれについて少し考えなければならないでしょう。ありがとう! –

+1

初期段階から確定的な処理がAutofacのアーキテクチャに組み込まれているので、Unityでそれを行うのは難しいかもしれません。 –

+1

キャッスルウィンザーは、サブコンテナ、スコープライフスタイル、または「container.Release」メソッドを使用してコンポーネントを明示的にリリースすることによって、これを可能にする別のコンテナです。 –

2

私は一般に、最も良いアプローチは、注入されたものを単に廃棄しないことだと思います。インジェクタが割り当てと割り当て解除を行っていると仮定する必要があります。

+3

私はしばしば使い捨てオブジェクトを注入したいので、これは非常に不便です。一般に、IDisposableのセマンティクスは、オブジェクトの作成者/所有者に、適切なタイミングでそのオブジェクトを廃棄するよう要求します。コンテナに責任がある場合、インスタンスのユーザはコンテナの終了時にコンテナに通知する必要があります。これは忘れがちです。 –

+1

あなたの答えの後半部分が悪いので、Downvoted。ほとんどのコンテナには、ネストされたコンテナや、StructureMapsの 'ReleaseAndDisposeAllHttpScopedObjects'メソッドのようなものがあります。あなたは、使い捨て品が適切に処分される方法を把握する必要があります。 – Andy

2

これはDIフレームワークによって異なります。一部のフレームワークでは、注入されたすべての依存関係に対して共有インスタンス(常に同じ参照を使用する)を指定するかどうかを指定できます。この場合、あなたはおそらく処分したくないでしょう。

一意のインスタンスを挿入するように指定できる場合は、(特別に作成されたので)破棄したいと思うでしょう。私はUnityに精通しているわけではありませんが、あなたがこの作業を行う方法については、ドキュメントをチェックする必要があります。それはMEFと私が試したいくつかのものの属性の一部です。

17

あなたは間違いなくあなたのクラスに注入されたオブジェクトに対してDispose()を呼びたくはありません。あなたが唯一の消費者であるという前提を作ることはできません。あなたの最善の策は、いくつかの管理インタフェースで、あなたの非管理オブジェクトをラップすることです:

public class ManagedFileReader : IManagedFileReader 
{ 
    public string Read(string path) 
    { 
     using (StreamReader reader = File.OpenRead(path)) 
     { 
      return reader.ReadToEnd(); 
     } 
    } 
} 

単なる例であることが、私は文字列にテキストファイルを読み込むしようとしていた場合、私はFile.ReadAllText(パス)を使用します。

別のアプローチは、工場を注入し、オブジェクトを自分で管理することである:ユニティフレームワークで

public void DoSomething() 
{ 
    using (var resourceThatShouldBeDisposed = injectedFactory.CreateResource()) 
    { 
     // do something 
    } 
} 
+2

しかし、私はIDisposableオブジェクトを注入したいと思います。構成に応じて注射部位にそれらを処分する必要はない。 最初の例は、基底のリソースに一時的にアクセスするときに機能しますが、リソースがより永続的な場合は役に立ちません。 2番目の例では、この方法で工場を投入するのは奇妙に思えます。これは最初のIoCモデルの主な機能の1つです。私がこのコンセプトにIoCを適用すると、私は元の質問に戻ります。私はコンテナ自体がこれに参加する必要があると思います。 –

+8

@パテ依存性注入は工場の必要性を排除するものではなく、それらの1つ(ab)使用を不要にするだけです。たとえば、実行時までの具体的な依存関係のタイプや高価な条件付き依存関係(作成するには多くのリソースを必要とするオブジェクトではないオブジェクト)が分からない場合は、オブジェクトそのもの。あなたのDIフレームワークがIDisposableをサポートしているかどうかによって、ファクトリ・インジェクションが最良の方法かもしれません。 (+1) –

+0

これは良いアドバイスだと思います。 IDisposablesを直接注入しないでください。管理されたラッパーを使用するか、UnityのStructureMapまたはPerRequestLifetimeManagerのHttpContextScopedのような特定のケースで明示的に廃棄することを可能にする生涯を使用します。 –

1

、注入されたクラスを登録するには2つの方法があります:シングルトンは(あなたはいつもの同じインスタンスを取得するとあなたがそれを解決するときにクラス)、またはそれぞれの解決でクラスの新しいインスタンスを取得するなどです。

解決済みのインスタンスを不要にしたら、後でそのインスタンスを破棄する必要があります(これはかなり合理的なアプローチです)。一方、コンテナ(オブジェクト解決を扱うクラス)を配置すると、すべてのシングルトンオブジェクトも自動的に破棄されます。

したがって、Unityフレームワークで注入された使い捨てオブジェクトには問題はありません。他のフレームワークについてはわかりませんが、依存性注入フレームワークが十分に実用的である限り、この問題を確実に処理する方法はあります。

+1

これに関する偉大な記事:http://www.ladislavmrnka.com/2011/03/unity-build-in-lifetime-managers/ – TrueWill

+2

これは、ひどい*アプローチです。 - 処分する必要のある対象物。ライフタイムが未知であるためシングルトン、トランジェントなどの理由で、コンポーネントが注入された依存関係を手動で廃棄することがさらに不可能であるため、これは複雑です。標準ライフタイムの過渡状態がコンテナによって決して廃棄されないという点でユニティ(2.x) 。 – user2864740

+0

「後者の場合...」クライアントは最初のケースか後者のケースか分かりません。注射された対象の選択された寿命を知らないままにすべきである。私はUnityが失敗するuser2864740の発言に同意しません。残念ながら、それは私たちが対処しなければならないフレームワークの特徴だと私は思います。 –

2

コンテナの前にファサードを置くと、これも解決できます。さらに、サービスのシャットダウンやスタートアップ、ServiceHostの状態遷移などのより豊富なライフサイクルを追跡するために、これを拡張することもできます。

コンテナは、IServiceLocatorインターフェイスを実装するIExtensionに存在する傾向があります。これは統合のファサードであり、WCfサービスに簡単にアクセスできるようにします。私はServiceHostBaseからサービスイベントにアクセスできます。

最後に登録されたシングルトンが登録されているか、作成されたタイプがファサードが追跡しているインターフェイスを実装しているかどうかを確認します。

あなたはこれらのイベントに縛られていますが、タイムリーに処分することはできませんが、それはちょっと役立ちます。

タイムリーに処分したい場合(サービスシャットダウン時にv.sという)あなたが得るアイテムは使い捨てであること、それを処分するビジネスロジックの一部なので、IDisposableはオブジェクトのインターフェースの一部でなければならないことを知る必要があります。そしておそらく、disposeメソッドが呼び出されることに関連する期待値の検証が必要です。

+0

このようにコンストラクタインジェクションの利点を失っていませんか? –

4

これも私によく戸惑っています。それについて幸せではありませんが、私は常にIDisposableオブジェクトを一時的に返すことは決して最善ではないという結論に達しました。

最近、私は自分自身のために質問を言い換えました:これは本当にIoCの問題か.netフレームワークの問題ですか?とにかく、処分は厄介です。意味のある機能的な目的はなく、技術的なものだけです。 IoCの問題よりも、私たちが対処しなければならないフレームワークの問題です。

私がDIについて気に入っているのは、技術的な詳細を気にすることなく、私に機能を提供する契約を求めることができるということです。私は所有者ではない。それがどの層に入っているかについての知識はなく、契約を履行するためにどの技術が必要かについての知識はなく、生涯心配はありません。私のコードはきれいに見え、非常にテスト可能です。私は彼らが所属する層に責任を実装することができます。

このルールに例外があり、生涯を整理する必要がある場合は、その例外を作りましょう。私が好きかどうか。インターフェイスを実装しているオブジェクトが私にそれを処分する必要がある場合は、そのオブジェクトをできるだけ短いものとして使用するようトリガーされて以来、私はそれについて知りたいと思っています。あとで処理される子コンテナを使用して解決することによって、トリックは、私が必要以上にオブジェクトを長く生かし続ける原因になる可能性があります。オブジェクトの登録時に、オブジェクトの許容寿命が決定されます。子コンテナを作成し、それを一定期間保持する機能ではありません。

私たちは開発者が処分を心配する必要がある限り、これまでどんな一時的な使い捨てオブジェクトもできるだけ少量注入しようとします。 1.私はオブジェクトをIDisposableではないようにしようとします。たとえば、ディスポーザブルオブジェクトをクラスレベルではなく、より小さなスコープに保つことによって。 2.オブジェクトを再利用可能にして、別の生涯マネージャを適用できるようにしようとしています。

これが実現できない場合は、工場を使用して、注入された契約のユーザーが所有者であり、その責任を負うべきであることを示します。

1つの注意点があります。契約者を非処分から使い捨てに変更することは大きな変更になります。その時、インターフェースはもはや登録されなくなり、工場へのインターフェースになります。しかし、私はこれが他のシナリオにも当てはまると思います。子供のコンテナを使用することを忘れると、その瞬間から記憶上の問題が生じます。工場アプローチはIoC解決例外を引き起こすでしょう。

いくつかのサンプルコード:

using System; 
using Microsoft.Practices.Unity; 

namespace Test 
{ 
    // Unity configuration 
    public class ConfigurationExtension : UnityContainerExtension 
    { 
     protected override void Initialize() 
     { 
      // Container.RegisterType<IDataService, DataService>(); Use factory instead 
      Container.RegisterType<IInjectionFactory<IDataService>, InjectionFactory<IDataService, DataService>>(); 
     } 
    } 

    #region General utility layer 

    public interface IInjectionFactory<out T> 
     where T : class 
    { 
     T Create(); 
    } 

    public class InjectionFactory<T2, T1> : IInjectionFactory<T2> 
     where T1 : T2 
     where T2 : class 

    { 
     private readonly IUnityContainer _iocContainer; 

     public InjectionFactory(IUnityContainer iocContainer) 
     { 
      _iocContainer = iocContainer; 
     } 

     public T2 Create() 
     { 
      return _iocContainer.Resolve<T1>(); 
     } 
    } 

    #endregion 

    #region data layer 

    public class DataService : IDataService, IDisposable 
    { 
     public object LoadData() 
     { 
      return "Test data"; 
     } 

     protected virtual void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       /* Dispose stuff */ 
      } 
     } 

     public void Dispose() 
     { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
     } 
    } 

    #endregion 

    #region domain layer 

    public interface IDataService 
    { 
     object LoadData(); 
    } 

    public class DomainService 
    { 
     private readonly IInjectionFactory<IDataService> _dataServiceFactory; 

     public DomainService(IInjectionFactory<IDataService> dataServiceFactory) 
     { 
      _dataServiceFactory = dataServiceFactory; 
     } 

     public object GetData() 
     { 
      var dataService = _dataServiceFactory.Create(); 
      try 
      { 
       return dataService.LoadData(); 
      } 
      finally 
      { 
       var disposableDataService = dataService as IDisposable; 
       if (disposableDataService != null) 
       { 
        disposableDataService.Dispose(); 
       } 
      } 
     } 
    } 

    #endregion 
} 
関連する問題