10

MEF を使用して、DI の方法としてインターフェイスを実装クラスにマップします。たとえば、インターフェイスには Import 属性を使用し、実装クラスには Export 属性を使用します。私の理解では、MEF フレームワークは実装クラスのインスタンスを作成し、使用または自動注入のために MEF のコンテナーに保持します。

私の実装クラスのいくつかは IDispose インターフェイスを実装しています。インスタンスは MEF によって作成されるため、MEF が終了したときにコンポーネントが破棄される場合は、MEF がコンポーネントの Dispose メソッドを呼び出せるようにする必要があると思います。たとえば、私のアプリケーションでは、MEF のコンテナーへの参照を保持しています。アプリケーションが終了したら、コンテナーの Dispose メソッドを呼び出します。問題は、コンポーネントの Dispose が呼び出されないことです。

インポートとエクスポートのマッピングに関するコードの例を次に示します。

[Import]
private IMyInterface IComponent1 { get; set; }
....

[Export]
private IMyInterface Component {
  get {
     var instance = new MyImplemetation();
     ....
     return instance;
 }
}
....

同様の方法で、他のマッピングのインポートおよびエクスポート定義が他にも多数あります。この方法でマッピングを作成し、MEF が関係と、マップされたインスタンスを作成する方法を認識できるようにします。AssemblyCatalog を使用してマッピングを読み込むアプリケーションのコードを次に示します。

var catalog = new AggregateCatalog();
catalog.Add (new AssemblyCatalog(Assembly.GetExecutingAssembly());
var batch = new CompositionBatch();
batch.AddPart(catalog);
// MEF container has all the mappings
var container = new CompositionContainer(catalog);
....
// Get instance from container
var instance = container.GetExportedValue<IMyInterface>();
// my instance CTOR has a contructor with several other 
// implementation instances injected by interface
// instance starts to do its job and coordinates others ...
instance.Start();
....
// Finally the job is done.
// Dispose the container explicitly there.
container.Dispose();
// But my components are never disposed
// this results some connections not being closed
// file streams not being closed...

ここで、インスタンスには、MEF によって CTOR を介して注入された他の多くのコンポーネントがあります。これらのコンポーネントには、MEF によって注入される他のコンポーネントも含まれています。問題は、一部のインスタンスが共有されているため、いつコンポーネントを破棄するかを決定するのが非常に難しいことです。1 つで Dispose を呼び出すと、他のユーザーはそれを使用できなくなります。この図でわかるように、インスタンスは MEF によって作成され、アプリケーション クラスに挿入されます。各コンポーネントは他のコンポーネントを認識してはならず、注入されたコンポーネントを使用してジョブを実行する必要があります。

アプリケーションが終了したとき、またはコンテナーが破棄されたときに、コンポーネントで Dispose を呼び出すように MEF に指示する場所/方法がわかりません。コンポーネントで Dispose を呼び出す必要がありますか? MEF がそれらを作成し、必要に応じてクライアントに挿入するため、それは正しくないと思います。クライアントは、ジョブを終了するときに Dispose を呼び出すべきではありません。

4

2 に答える 2

7

MEF は、作成したコンポーネントの有効期間を管理します。あなたの例の問題は、破棄したいオブジェクトが実際には MEF によって作成されていないことです。おそらく、次のようなことをしたいと思うでしょう:

public class ComponentExporter : IDisposable
{
    private IMyInterface _component;

    [Export]
    public IMyInterface Component
    {
        get
        {
            if (_component != null)
            {
                _component = new MyImplementation();

                // ...
            }
            return _component;
        }
    }

    public void Dispose()
    {
        if (_component != null)
        {
            _component.Dispose();
        }
    }
}

ComponentExporter は、MEF によって実際に作成されるクラスであり、IDisposable を実装する場合、MEF はそれをコンテナーと共に破棄します。この例では、ComponentExporter は、作成されたコンポーネントを破棄するときに破棄します。これはおそらく必要なことです。

もちろん、エクスポートを MyImplementation クラスに直接配置するだけの方が簡単です。そうしない理由があると思いますが、これは次のようになります。

[Export(typeof(IMyInterface))]
public class MyImplementation : IMyInterface, IDisposable
{
    // ...
}

コードに関するその他の注意事項: カタログをどこかにインポートしてコンテナー内のパーツから変更する場合を除き、カタログをバッチ経由でコンテナーに追加する必要はおそらくありません。たまたま多くのリクエストを処理していて、パフォーマンスが心配な場合は、AssemblyCatalog を 1 回だけ作成してから、すべてのリクエストに同じものを使用する必要があります。

于 2010-02-24T21:21:51.107 に答える
1

ダニエルは正しいです。Import と Export の関係をマッピング クラスのプロパティとして定義しました。これらを ComposablePartCatalog として MEF のコンテナーにロードし、MEF が対応するインスタンスをその場で魔法のように取得できるようにしました。新しいインスタンスへのコードがいくつかあるのは、マッピング クラス内です。したがって、MEF がプロセスから外れているときに、MEF がこれらのマッピング クラスにコールバックして、作成されたリソースを破棄できるようにする方法を見つける必要があります。

エクスポート部分にクラスを導入するというダニエルの提案が気に入っています。すべての DI マッピングはプロパティ (ゲッターとセッター) の方法で定義されているため、次のような基本クラスを作成しました。

public class ComponentExporterBase: IDisposable {
  private List<IDisposable> _list;

  public ComponentExporterBase()  {
    _list = new List<IDisposable>();
  }

  protect void Add(IDisposable obj) {
    _list.Add(obj);
  }

  protected virtual void Dispose(bool disposing) {
    if (disposing) {
      foreach(var obj in _list) {
        obj.Dispose();
      }
      _list.Clear();
    }
  }  

  public void Dispose()  {
    Dispose(true);
  }
}

この基本クラスを使用すると、マッピング クラスで MEF に廃棄作業を任せることができます。たとえば、ここに 1 つの例があります。

internal class MyDIMappingClass : ComponentExporterBase {
  [Import]
  private IDataReader _dataReader { get; set; }

  [Export]
  private IController {
      get {
         var reader = _dataReader;
         var instance = new MyMainController(reader);
         base.Add(instance);
         return instance;
  }
  ...
}

私のすべてのマッピング クラスは同様の方法で定義されており、より明確になっています。基本的な原則は、クラス内で作成されたインスタンスまたはリソースはクラス内で破棄する必要がありますが、インスタンスを注入することはできません。このようにして、次の例のように、MEF によって挿入されたインスタンスをクリーンアップする必要がなくなります。

public class MyMainController : IController {
   private IDataReader _dataReader;

   // dataReader is injected through CTOR
   public MyMainControler(IDataReader dataReader) {
     _dataReader = dataReader; 
     ...
   }
   ...
   public void Dispose() {
     // dispose only resources created in this class
     // _dataReader is not disposed here or within the class!
     ...}
}

ところで、属性はクラスのビジネス ロジックとは関係がないため、プロパティをインポートおよびエクスポートとして使用するのが好きです。他の多くの場合、一部のクラスはサードパーティからのものであり、それらのソースコードにアクセスしてエクスポートとしてマークすることはできません.

于 2010-02-25T20:30:18.923 に答える