2012-03-18 6 views
5

Ecco come vorrei farlo funzionareQual è il metodo MEF per ottenere un valore/oggetto esportato esistente (non ottenere o creare)?

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 differenza è che questo metodo non creare un'istanza se il contenitore non contiene esso. È un puro get ottenerlo se esiste nel contenitore. Ne ho bisogno per i miei test in cui non voglio che il codice di prova crei oggetti nel contenitore di produzione (se non vengono creati) basta usare quelli esistenti. Solo il codice di produzione dovrebbe aggiungere/rimuovere oggetti al contenitore.

, inviato a MEF codeplex forum ma nessuna risposta. Quindi sperando che qualcuno su SO possa avere una risposta. Anche se avrei bisogno di scrivere quella funzione come metodo di estensione ... andrebbe bene anche come risposta.

+0

Mi chiedevo perché stai testando apparentemente il contenitore nei test dell'unità? –

+0

@MatthewAbbott - Sto provando a scrivere test di sistema sul livello ViewModel (costruito con un primo progetto ViewModel). Questi non sono test unitari ... Invece di manipolare gli elementi dell'interfaccia utente, voglio raggiungere il contenitore, afferrare il modello viewmodel e ottenere lo stesso effetto. – Gishu

+0

Più contesto qui - http://caliburnmicro.codeplex.com/discussions/348548 – Gishu

risposta

1

Ecco la mia soluzione. Inizialmente ho provato a creare un metodo di estensione. Ho provato diverse cose, ho letto il documento e ho esplorato le proprietà, gli eventi e i metodi disponibili sul container e sul catalogo, ma non ho potuto far funzionare nulla.

Dopo aver considerato il problema, l'unico modo in cui riesco a trovare una soluzione è creare un contenitore derivato basato su CompositionContainer e implementare il metodo GetButDoNotCreate.

Aggiornamento: Mi sono reso conto dopo aver postato che la soluzione funziona solo nell'esempio semplice che hai pubblicato dove viene utilizzato solo GetExportedValue per recuperare parti semplici di parti. A meno che non si utilizzi il contenitore come semplice localizzatore di servizi per le parti senza [Import] s che non verranno eseguite quando vengono create parti con [Importa] s.

Ecco l'attuazione:

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

Agisce override del metodo GetExportedValue per tenere traccia dei tipi che sono stati composti finora e quindi utilizzare questo per verificare il tipo di composizione in GetButNotCreate. Ho lanciato un'eccezione come hai detto nella tua domanda.

Ovviamente, potrebbe essere necessario sovrascrivere i sovraccarichi di GetExportedValue (a meno che non li si usi, ma anche così, li sovrascriverei comunque per sicurezza) e magari aggiungere altri costruttori e cose se si utilizza la classe. In questo esempio, ho fatto il minimo per farlo funzionare.

Qui ci sono i test di unità che mettono alla prova il nuovo metodo:

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

Essa mostra che la GetButNotCreate un'eccezione se il tipo non è mai stato esportato e che tornerà questo tipo se è già stato importato.

Non sono riuscito a trovare nessun hook da controllare (senza ricorrere alla riflessione) per vedere se MEF ha composto una parte, quindi questa soluzione CustomContainer sarebbe la soluzione migliore.

+0

@Giles - proprio questo metodo mi richiederebbe di conoscere tutti i vari percorsi MEF in cui gli oggetti possono essere costruiti e intercettarli. Anche se vengono costruiti alberi di oggetti, questo traccia solo le radici. In secondo luogo, non sono sicuro che questo intermediario possa impedire la raccolta dei rifiuti delle parti. – Gishu

+0

Tuttavia +1 per il tentativo/sforzo. Ora sto mettendo in dubbio la necessità .. anche se il ViewModel è stato creato (su getExportedValue) dal test (invece del codice di produzione), il test fallirebbe poiché probabilmente non sarà inizializzato/cablato ad altri collaboratori. – Gishu

+0

@Gishu dopo aver postato il mio post iniziale, ho provato a lavorare con gli eventi, ho anche provato un'altra soluzione con i metodi di estensione e non riuscivo davvero a trovare nulla. Forse usando la riflessione potresti creare un metodo di estensione che guarderebbe alla raccolta interna che MEF sta usando per la sua parte e verifica se una parte è già presente nella collezione. – Gilles

0

Penso valga la pena avere il proprio wrapper su Container. Qualcosa come IContainerWrapper e usalo ovunque nel tuo codice.

Con avere questo posto:

  • Se si dispone di solito unit test è semplicemente possibile iniettare un'altra istanza di involucro, che si adatta alle vostre esigenze.

  • Se si desidera accedere al contenitore di produzione, ma il comportamento è descritto sopra, è possibile utilizzare la direttiva del preprocessore nell'implementazione del wrapper di produzione.

Problemi correlati