2009-06-22 4 views
5

J'ai besoin d'ajouter des points d'extension à notre code existant, et j'ai envisagé MEF comme une solution possible. Nous avons une interface IRandomNumberGenerator, avec une implémentation par défaut (ConcreteRNG) que nous aimerions pouvoir échanger. Cela ressemble à un scénario idéal pour MEF, mais j'ai eu des problèmes avec la façon dont nous instancions les générateurs de nombres aléatoires. Notre code actuel ressemble à:Puis-je contrôler la création d'objets en utilisant MEF?

public class Consumer 
{ 
    private List<IRandomNumberGenerator> generators; 
    private List<double> seeds; 

    public Consumer() 
    { 
     generators = new List<IRandomNumberGenerator>(); 
     seeds = new List<double>(new[] {1.0, 2.0, 3.0}); 

     foreach(var seed in seeds) 
     { 
      generators.Add(new ConcreteRNG(seed)); 
     } 
    } 
} 

En d'autres termes, le consommateur est responsable de l'instanciation les RNG dont elle a besoin, notamment en fournissant la semence que chaque instance exige.

Ce que je voudrais faire, c'est que l'implémentation concrète de RNG soit découverte et instanciée par MEF (en utilisant DirectoryCatalog). Je ne suis pas sûr de savoir comment y parvenir. Je pourrais exposer une propriété Generators et la marquer comme [Import], mais comment puis-je fournir les graines requises?

Y at-il une autre approche qui me manque?

Répondre

5

Actuellement, il n'y a pas de moyen direct de le faire dans MEF mais l'équipe MEF envisage de le soutenir dans v.Next. Vous voulez essentiellement créer plusieurs instances de la même implémentation qui est traditionnellement effectuée en utilisant un modèle Factory. Donc, une approche que vous pouvez utiliser est quelque chose comme:

public interface IRandomNumberGeneratorFactory 
{ 
    IRandomNumberGenerator CreateGenerator(int seed); 
} 

[Export(typeof(IRandomNumberGeneratorFactory))] 
public class ConcreateRNGFactory : IRandomNumberGeneratorFactory 
{ 
    public IRandomNumberGenerator CreateGenerator(int seed) 
    { 
    return new ConcreateRNG(seed); 
    } 
} 

public class Consumer 
{ 
    [Import(typeof(IRandomNumberGeneratorFactory))] 
    private IRandomNumberGeneratorFactory generatorFactory; 
    private List<IRandomNumberGenerator> generators;  
    private List<double> seeds;  

    public Consumer()  
    { 
    generators = new List<IRandomNumberGenerator>(); 
    seeds = new List<double>(new[] {1.0, 2.0, 3.0}); 

    foreach(var seed in seeds) 
    {    
     generators.Add(generatorFactory.CreateGenerator(seed)); 
    } 
    } 
} 
+0

Merci Wes. J'avais envisagé une approche d'usine, mais je me suis retrouvé coincé parce que je voulais une usine générique qui créerait une instance de n'importe quel type IRandomNumberGenerator a été découvert par MEF. En y repensant, votre approche ne semble pas beaucoup de travail supplémentaire - merci encore. – Akash

+1

Je travaille maintenant. Je l'ai simplifié un peu en fournissant une méthode d'usine statique sur ConcreteRNG: [Export (typeof (Func ))] public static readonly Func Create = graine => new ConcreteRNG (graine) ; – Akash

+0

Oui exporter une fonction elle-même est également un autre moyen simplifié d'obtenir ce que vous voulez. De plus, je viens de réaliser que si vous voulez utiliser cette importation dans le constructeur, vous devrez en faire une importation de constructeur, parce que cette importation, comme je l'ai démontré, ne sera pas définie avant la construction de l'objet. –

0

Je crois que c'est ce que la fonction Lazy Exports est pour. A partir de cette page:

[Import] 
public Export<IMessageSender> Sender { get; set; } 

Dans ce cas, vous êtes opt-in pour retarder ce instanciation jusqu'à ce que vous avez réellement besoin de l'instance de mise en œuvre. Pour demander l'instance, utilisez la méthode [Export.GetExportedObject()]. Veuillez noter que cette méthode n'agira jamais comme une fabrique d'implémentations de T, donc l'appeler plusieurs fois retournera la même instance d'objet retournée lors du premier appel.

+0

Scott, j'ai besoin de plusieurs instances de IRandomNumberGenerator. Votre commentaire suggère que j'obtiendrai la même instance à chaque fois. Est-ce que je manque quelque chose? – Akash

+0

Désolé, j'ai raté cette partie. Dans ce cas, je pense que vous avez besoin du modèle d'usine. –

4

L'aperçu MEF 8 a un support expérimental pour cela, bien qu'il ne soit pas encore inclus dans System.ComponentModel.Composition.dll. Voir this blog post pour plus d'informations.

Vous devrez télécharger les sources MEF et créer la solution. Dans le dossier Samples\DynamicInstantiation, vous trouverez l'assembly Microsoft.ComponentModel.Composition.DynamicInstantiation.dll. Ajoutez une référence à cette assemblée et ajouter un fournisseur de instanciation dynamique à votre récipient comme celui-ci:

var catalog = new DirectoryCatalog("."); 
var dynamicInstantiationProvider = new DynamicInstantiationProvider(); 
var container = new CompositionContainer(catalog, dynamicInstantiationProvider); 
dynamicInstantiationProvider.SourceProvider = container; 

Maintenant, vos pièces seront en mesure d'importer un PartCreator<Foo> si elles ont besoin pour créer dynamiquement Foo parties. L'avantage par rapport à l'écriture de votre propre classe d'usine est que cela va prendre en charge de façon transparente les importations de Foo, et les importations d'importations, etcetera.

modifier:

  • dans MEF Preview 9PartCreator a été renommé ExportFactory mais il est seulement inclus dans l'édition silverlight.
  • dans MEF 2 Preview 2, ExportFactory est devenu inclus pour l'édition de bureau. Donc, ExportFactory fera probablement partie de la prochaine version du framework .NET après .NET 4.0.
+0

Merci, je vais suivre sur ce lien. – Akash

Questions connexes