2010-02-24 6 views
10

Я использую MEF для сопоставления интерфейса с классом реализации как способ DI. Например, я использую атрибут «Импорт» для интерфейса и «Экспорт для класса реализации». Я понимаю, что структура MEF создаст экземпляры класса реализации и удерживает их в контейнере MEF для использования или автоматической инъекции.Утилизировать компоненты контейнером MEF?

Некоторые из моих классов реализации реализуют интерфейс IDispose. Поскольку экземпляры создаются MEF, я думаю, что я должен позволить MEF вызывать метод Dispose, если они являются одноразовыми, когда MEF отключен. Например, в моем приложении я держу ссылку на контейнер 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... 

Здесь экземпляр имеет много других компонентов, впрыскивают через CTOR в MEF. Эти компоненты также содержат другие компоненты, которые вводятся MEF. Проблема в том, что очень сложно принять решение о том, когда выставлять компоненты, поскольку некоторые экземпляры разделяются. Если я вызову Dispose на одном, это может привести к тому, что другие не смогут его использовать. Как вы можете видеть на этом рисунке, экземпляры создаются MEF и вводятся в мои классы приложений. Каждый компонент не должен иметь никаких знаний о других, и он должен использовать инъецированные компоненты для выполнения этой работы.

Я не уверен, где/как я должен указывать MEF на вызов Dispose на компонентах, когда приложение завершается или контейнер установлен? Должен ли я вызвать Dispose на компонентах? Я не думаю, что это правильно, так как MEF создает их и при необходимости вводит их в клиентов. Клиенты не должны называть их Dispose при завершении работы.

ответ

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, а затем использовать один и тот же для всех запросов.

+0

Я думаю, что Даниэль объяснил это красиво. Я создал экземпляр в моем агенте Get property. Имеет смысл удерживать экземпляр и очищать оттуда. Я предпочитаю переносить Export на getter вместо класса. Я проверю его и дам вам знать, разрешит ли это проблему. –

1

Даниэль прав. Я определил отношения импорта и экспорта как свойства в моих классах сопоставления. Я загрузил их как 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 сделать увольнение. Например, вот один пример:

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! 
    ...} 
} 

Кстати, мне нравится использовать свойство, как мой импорт и экспорт, так как атрибуты имеют нечего делать бизнес-логику класса. В других случаях некоторые классы принадлежат третьим сторонам, и у меня нет доступа к их исходным кодам, чтобы отметить их как экспорт.

Смежные вопросы