2010-09-24 18 views
5

Estoy intentando que MEF recomponga todas las partes que conoce cuando actualizo una instancia que se exporta. Básicamente, quiero que MEF actualice todas mis piezas que importen un valor de configuración de cadena de conexión cuando se modifique. Todo se ve bien hasta el punto en que quiero cambiar la instancia. Si intento crear ComposeParts con el valor actualizado, parece agregar una segunda instancia parcial en el contenedor, y luego mis importaciones se actualizan, pero se anulan.¿Cómo consigo que MEF se recomponga cuando cambio una pieza?

¿Alguien me puede decir dónde me estoy equivocando? ¿O debería tratar de usar MEF de esta manera?

Estoy usando MEF preview 9 y apuntando .NET framework 3.5 y WPF.

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

Respuesta

6

Tiene algunas cosas un poco mal para el escenario que está tratando de lograr.

Primero: Si desea agregar/eliminar/cambiar una exportación en particular, no debe estar en el catálogo, por lo que debe eliminar su clase Config que tiene la propiedad Exportar configuraciones. Eso está siendo creado por CatalogExportProvider y es la razón por la que está viendo un valor nulo en su colección.

intente lo siguiente:

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

Gracias Wes! Cambié mi código para mantener la parte composable que me permitió actualizar la pieza en el contenedor, en lugar de solo agregar más y más instancias, funciona muy bien ahora. – Matt

Cuestiones relacionadas