2012-03-18 11 views
5

Así es como me gustaría que funcione¿Cuál es el método MEF para obtener un valor/objeto exportado existente (no obtener O Crear)?

container.GetButDoNotCreate<T>(); // should throw (or can return null) if container doesn't contain an instance matching the contract 

var x = container.GetExportedValue<T>(); 

var y = container.GetButDoNotCreate<T>(); // should return the object created in previous step 

Assert.That(x).IsSameAs(y); 

La diferencia es que este método no va a crear una instancia si el contenedor no lo contiene. Es un puro obtener, es decir, obtener este objeto si existe en el contenedor. Lo necesito para mis pruebas donde no quiero que el código de prueba cree objetos en el contenedor de producción (si no se crean) simplemente use los existentes. Solo el código de producción debe agregar/eliminar objetos al contenedor.

Publicado en MEF codeplex forum pero sin respuestas. Así que espero que alguien en SO pueda tener una respuesta. Además, si tuviera que escribir esa función como método de extensión ... también estaría bien como respuesta.

+0

Me pregunto por qué aparentemente está probando el contenedor en las pruebas de su unidad? –

+0

@MatthewAbbott - Estoy intentando escribir pruebas de sistemas en la capa ViewModel (construida con un primer diseño de ViewModel). Estas no son pruebas unitarias ... En lugar de manipular elementos de la interfaz de usuario, quiero meter la mano en el contenedor, agarrar el modelo de vista y lograr el mismo efecto. – Gishu

+0

Más contexto aquí - http://caliburnmicro.codeplex.com/discussions/348548 – Gishu

Respuesta

1

Aquí está mi solución. Al principio intenté crear un método de extensión. Probé varias cosas, leí el documento y exploré las propiedades, eventos y métodos disponibles en el contenedor y el catálogo, pero no pude hacer que funcionara nada.

Después de pensar en el problema, la única forma en que puedo encontrar una solución es crear un contenedor derivado que esté basado en CompositionContainer e implemente el método GetButDoNotCreate.

Actualización: Me di cuenta después de publicar que la solución solo funciona en el ejemplo simple que ha publicado donde solo GetExportedValue se utiliza para recuperar piezas de partes simples. A menos que use el contenedor como un simple localizador de servicio para las partes sin [Import] s que no funcionarán cuando se creen partes con [Import] s.

Aquí es la implementación:

using System; 
using System.Collections.Generic; 
using System.ComponentModel.Composition.Hosting; 
using System.ComponentModel.Composition.Primitives; 

namespace GetButNotCreate 
{ 
    public class CustomContainer : CompositionContainer 
    { 
     private List<Type> composedTypes = new List<Type>(); 

     public CustomContainer(ComposablePartCatalog catalog) 
      : base(catalog) 
     { 
     } 

     public new T GetExportedValue<T>() 
     { 
      if (!composedTypes.Contains(typeof(T))) 
       composedTypes.Add(typeof(T)); 

      return base.GetExportedValue<T>(); 
     } 

     public T GetButDoNotCreate<T>() 
     { 
      if (composedTypes.Contains(typeof(T))) 
      { 
       return base.GetExportedValue<T>(); 
      } 

      throw new Exception("Type has not been composed yet."); 
     } 
    } 
} 

Funciona reemplazando el método GetExportedValue hacer un seguimiento de los tipos que se han compuesto hasta el momento y después de usar este para comprobar si hay Composición Tipo de GetButNotCreate. Lancé una excepción como la que mencionaste en tu pregunta.

Por supuesto, es posible que deba anular las sobrecargas de GetExportedValue (a menos que no las use, pero aun así, las anularía por seguridad) y tal vez agregue otros constructores y cosas si usa la clase. En este ejemplo, hice lo mínimo para que funcione.

Estas son las pruebas unitarias que ponen a prueba el nuevo método:

using System.ComponentModel.Composition; 
using System.ComponentModel.Composition.Hosting; 
using Microsoft.VisualStudio.TestTools.UnitTesting; 
using System; 

namespace GetButNotCreate 
{ 
    public interface IInterface { } 

    [Export(typeof(IInterface))] 
    public class MyClass : IInterface 
    { 
    } 

    [TestClass] 
    public class UnitTest1 
    {  
     [TestMethod] 
     [ExpectedException(typeof(Exception), "Type has not been composed yet.")] 
     public void GetButNotCreate_will_throw_exception_if_type_not_composed_yet() 
     { 
      var catalog = new AssemblyCatalog(typeof(UnitTest1).Assembly); 
      CustomContainer container = new CustomContainer(catalog); 
      container.ComposeParts(this); 

      var test = container.GetButDoNotCreate<IInterface>(); 
     } 

     [TestMethod] 
     public void GetButNotCreate_will_return_type_if_it_as_been_composed() 
     { 
      var catalog = new AssemblyCatalog(typeof(UnitTest1).Assembly); 
      CustomContainer container = new CustomContainer(catalog); 
      container.ComposeParts(this); 

      var x = container.GetExportedValue<IInterface>(); 
      var y = container.GetButDoNotCreate<IInterface>(); 

      Assert.IsNotNull(y); 
      Assert.AreEqual(x, y); 
     } 
    } 
} 

Muestra que la GetButNotCreate lanzará una excepción si el tipo nunca ha sido exportado y que le proporcione este tipo si ya ha sido importado.

No pude encontrar ningún gancho en ningún lugar para comprobar (sin recurrir a la reflexión) para ver si MEF ha compuesto una pieza, por lo que esta solución CustomContainer sería mi mejor opción.

+0

@Giles - este método me obligaría a conocer todas las diversas rutas MEF en las que se pueden construir objetos e interceptarlos. Además, si se construyen árboles objeto, esto solo rastrearía raíces. En segundo lugar, no estoy seguro si este intermediario evitará la recolección de basura de las piezas. – Gishu

+0

Sin embargo +1 para el intento/esfuerzo. Ahora estoy cuestionando la necesidad ... incluso si se creó ViewModel (en getExportedValue) de la prueba (en lugar del código de producción), la prueba fallaría ya que probablemente no se inicializará/cableará a otros colaboradores. – Gishu

+0

@Gishu después de publicar mi publicación inicial, traté de trabajar con eventos, también probé otra solución con métodos de extensión y realmente no pude encontrar nada. Tal vez usando la reflexión podría hacer un método de extensión que vería la colección interna que MEF está usando para su parte y ver si una parte ya está presente en la colección. – Gilles

0

Creo que vale la pena tener su propio contenedor sobre el contenedor. Algo así como IContainerWrapper y utilícelo en cualquier lugar de su código.

Con tener esto en su lugar:

  • Si tiene pruebas unitarias habituales que simplemente puede inyectar otra instancia de la envoltura, que se adapte a sus necesidades.

  • Si desea acceder al contenedor de producción, pero tiene el comportamiento descrito anteriormente, puede usar la directiva de preprocesador en su implementación de envoltura de producción.

Cuestiones relacionadas