2010-02-24 9 views
10

Uso MEF para asignar la interfaz a la clase de implementación como una forma de DI. Por ejemplo, uso el atributo Importar para una interfaz y Exportar para la clase de implementación. Tengo entendido que el marco de MEF creará las instancias de la clase de implementación y las mantendrá en el contenedor de MEF para su uso o autoinyección.¿Desechar los componentes por contenedor MEF?

Algunas de mis clases de implementación implementan la interfaz IDispose. Dado que las instancias son creadas por MEF, creo que debería dejar que el MEF llame al método Dispose de los componentes si son desechables cuando el MEF está desactivado. Por ejemplo, en mi solicitud, tengo una referencia al contenedor del MEF. Cuando la aplicación termina, llamo al método Dispose del contenedor. El problema es que nunca se llama a los componentes 'Dispose'.

Éstos son algunos códigos de ejemplo acerca de la asignación de importación y exportación:

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

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

Hay muchas otras definiciones de importación y exportación para otras asignaciones de la misma manera. Construyo mapeos de esta manera para que el MEF tenga el conocimiento de las relaciones y la forma de crear las instancias mapeadas. Aquí hay algunos códigos en mi solicitud para cargar asignaciones mediante 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... 

aquí la instancia tiene muchos otros componentes inyectados a través CTOR por el MEF. Esos componentes también contienen otros componentes que son inyectados por el MEF. El problema es que es realmente difícil decidir cuándo deshacerse de los componentes, ya que algunas instancias se comparten. Si llamé a Dispose en uno, esto podría causar que otros no puedan usarlo. Como puede ver en esta imagen, las instancias son creadas por el MEF e inyectadas a mis clases de aplicaciones. Cada componente no debe tener ningún conocimiento de los demás, y debe usar componentes inyectados para hacer el trabajo.

No estoy seguro de dónde y cómo debo instruir al MEF para que llame a Dispose en los componentes cuando la aplicación finaliza o cuando se elimina el contenedor. ¿Debo llamar a Dispose en los componentes? No creo que sea correcto, ya que el MEF los crea e inyecta en los clientes según sea necesario. Los clientes no deben llamar a su Dispose al terminar sus trabajos.

Respuesta

7

MEF administra la vida útil de los componentes que crea. Parece que el problema en su ejemplo es que el objeto que desea eliminar no es realmente creado por MEF. Tal vez usted quiere hacer algo como esto:

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 es la clase de hecho creada por el MEF, y si se implementa IDisposable continuación MEF se deshará con el contenedor. En este ejemplo, ComponentExporter dispone el componente creado cuando se desecha, que es lo que usted desea.

Por supuesto, sería más fácil si solo pone la exportación en la clase MyImplementation directamente. Supongo que habrá alguna razón para no hacerlo, pero esto es cómo se vería:

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

Algunas otras notas en su código: Es probable que no necesita agregar el catálogo al contenedor a través del lote , a menos que lo esté importando en alguna parte y modificándolo desde partes dentro del contenedor. Y si está procesando muchas solicitudes y le preocupa el rendimiento, solo debe crear el catálogo de ensamblados una vez y luego usar el mismo para todas las solicitudes.

+0

Creo que Daniel lo explicó muy bien. Creé la instancia en mi get de propiedades Export. Tiene sentido mantener la instancia y limpiar desde allí. Prefiero poner Exportar en getter en lugar de class. Lo probaré y le diré si esto resolverá el problema. –

1

Daniel tiene razón. Definí una relación de Importar y Exportar como propiedades en mis clases de mapeo. Los cargué como ComposablePartCatalog en el contenedor de MEF para que MEF pueda obtener mágicamente las instancias correspondientes en vuelo.Es dentro de las clases de mapeo que tengo algunos códigos para nuevas instancias. Por lo tanto, tengo que encontrar la forma de que MEF vuelva a llamar a esas clases de asignación para disponer de los recursos creados cuando MEF está fuera de proceso.

Me gusta la sugerencia de Daniel de introducir una clase para mi parte de exportación. Puesto que todas mis asignaciones DI se definen en el camino de propiedades (getter y setters), he creado una clase base como esto:

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

Con esta clase base, mis clases de mapeo serán capaces de dejar que el MEF para hacer el trabajo de eliminación. Por ejemplo, aquí hay un ejemplo:

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

Todas mis clases de asignación se definen de la misma manera, y son mucho más claras. El principio básico es que las instancias o los recursos que se crean dentro de una clase deben eliminarse dentro de la clase, pero no las instancias inyectadas. De esta manera, no necesito para limpiar cualquier instancia inyectados por el MEF más, como en este ejemplo:

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

Por cierto, me gusta usar mis propiedades como la importación y exportación desde los atributos tienen nada que hacer una lógica de negocios de clase. En otros muchos casos, algunas clases son de terceros y no tengo acceso a sus códigos fuente para marcarlos como exportación.

Cuestiones relacionadas