2010-09-24 8 views
5

J'essaye d'obtenir MEF pour recomposer toutes les parties qu'il sait quand je mets à jour une instance qui est exportée. Essentiellement, je veux que MEF mette à jour toutes mes parties qui importent une valeur de configuration de chaîne de connexion quand elle est changée. Tout semble bon jusqu'au point où je veux changer l'instance. Si j'essaye de ComposeParts avec la valeur mise à jour il semble ajouter une deuxième partie d'instance dans le conteneur, puis mes importations sont mises à jour, mais à null. Est-ce que quelqu'un peut signaler où je vais mal? Ou devrais-je même essayer d'utiliser MEF de cette manière? J'utilise l'aperçu MEF 9 et le ciblage .net framework 3.5, et WPF.Comment faire pour que MEF se recompose quand je change une pièce?

using System; 
using System.Collections.Generic; 
using System.ComponentModel.Composition; 
using System.ComponentModel.Composition.Hosting; 
using System.Linq; 
using System.Text; 
using Shouldly; 

namespace ConsoleApplication4 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      MainClass main = new MainClass(); 
      main.RunTest(); 
     } 
    } 

    public class MainClass 
    { 
     [ImportMany] 
     public IEnumerable<Settings> Settings { get; set; } 

     public void RunTest() 
     { 
      AggregateCatalog catalog = new AggregateCatalog(); 
      catalog.Catalogs.Add(new AssemblyCatalog(typeof(Settings).Assembly)); 

      CompositionContainer container = new CompositionContainer(catalog); 

      container.SatisfyImportsOnce(this); 

      Config cfg = new Config 
      { 
       Settings = new Settings { ConnectionString = "Value1" }, 
      }; 

      // result is returned with a null settings value 
      UsesSettings result = container.GetExportedValue<UsesSettings>(); 

      // this recomposes everything with the new value, result changes to have settings of Value1 
      container.ComposeParts(cfg); 

      // this line results in my import many enumerable returning 2 parts the Value1 setting and null 
      container.SatisfyImportsOnce(this); 

      result.TheSettings.ConnectionString.ShouldBe("Value1"); 

      cfg.Settings = new Settings { ConnectionString = "Value2" }; 

      // how do I tell the container to recompose now I have changed the config object, 
      // or how do I replace the part value with the new value? 

      // this line causes the result.Settings to return null 
      container.ComposeParts(cfg); 

      // this updates the ImportMany to 3 values, Value1, Value2 and null 
      container.SatisfyImportsOnce(this); 
     } 
    } 

    public class Settings 
    { 
     public string ConnectionString = "default value"; 
    } 

    public class Config 
    { 
     [Export(typeof(Settings))] 
     public Settings Settings { get; set; } 
    } 

    [Export(typeof(UsesSettings))] 
    public class UsesSettings 
    { 
     [Import(typeof(Settings), AllowRecomposition = true)] 
     public Settings TheSettings { get; set; } 
    } 
} 

Répondre

6

Vous avez quelques petites choses à faire pour le scénario que vous essayez d'accomplir. Premièrement: Si vous voulez ajouter/supprimer/modifier une exportation particulière, elle ne doit pas être dans le catalogue lui-même. Vous devez donc supprimer votre classe Config qui a la propriété Export settings. Cela est créé par CatalogExportProvider et est la raison pour laquelle vous voyez une valeur nulle dans votre collection.

les opérations suivantes:

public class Program 
{ 
    [ImportMany(AllowRecomposition=true)] 
    public IEnumerable<Settings> Settings { get; set; } 

    public void RunTest() 
    { 
     AggregateCatalog catalog = new AggregateCatalog(); 
     catalog.Catalogs.Add(new AssemblyCatalog(typeof(Settings).Assembly)); 

     CompositionContainer container = new CompositionContainer(catalog); 

     // Settings should have 0 values. 
     container.ComposeParts(this); 
     Contract.Assert(this.Settings.Count() == 0); 

     CompositionBatch batch = new CompositionBatch(); 

     // Store the settingsPart for later removal... 
     ComposablePart settingsPart = 
      batch.AddExportedValue(new Settings { ConnectionString = "Value1" }); 

     container.Compose(batch); 

     // Settings should have "Value1" 
     UsesSettings result = container.GetExportedValue<UsesSettings>(); 
     Contract.Assert(result.TheSettings.ConnectionString == "Value1"); 

     // Settings should have 1 value which is "Value1"; 
     Contract.Assert(this.Settings.Count() == 1); 
     Contract.Assert(this.Settings.First().ConnectionString == "Value1"); 

     // Remove the old settings and replace it with a new one. 
     batch = new CompositionBatch(); 
     batch.RemovePart(settingsPart); 
     batch.AddExportedValue(new Settings { ConnectionString = "Value2" }); 
     container.Compose(batch); 

     // result.Settings should have "Value2" now. 
     Contract.Assert(result.TheSettings.ConnectionString == "Value2"); 

     // Settings should have 1 value which is "Value2" 
     Contract.Assert(this.Settings.Count() == 1); 
     Contract.Assert(this.Settings.First().ConnectionString == "Value2"); 
    } 
} 
public class Settings 
{ 
    public string ConnectionString = "default value"; 
} 

[Export(typeof(UsesSettings))] 
public class UsesSettings 
{ 
    [Import(typeof(Settings), AllowRecomposition = true)] 
    public Settings TheSettings { get; set; } 
} 
+0

Merci Wes! J'ai changé mon code pour conserver la partie composable qui m'a permis de mettre à jour la partie dans le conteneur, plutôt que de simplement ajouter de plus en plus d'instances, fonctionne très bien maintenant. – Matt

Questions connexes