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.
Mi chiedevo perché stai testando apparentemente il contenitore nei test dell'unità? –
@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
Più contesto qui - http://caliburnmicro.codeplex.com/discussions/348548 – Gishu