2017-03-14 1 views
2

Problème

Je suis avec la mise en œuvre des plugins pour une application MEF WinForms et je remarque un comportement avec la composition que je ne comprends pas.MEF - aide pour comprendre ce qui se passe avec le récipient et la composition

Juste pour le sortir du chemin, j'utilise avec .NET 4. MEF

Comme une sorte de tl; dr, voici ce que je ne suis pas clair en ce qui concerne les conteneurs et la composition :

  1. Si je construis un récipient à l'intérieur d'une classe qui est, elle-même, une partie exportée et appelle ComposeParts(this) est l'instance existante de cette catégorie a été ajoutée au conteneur ou est une autre instance créée?
  2. Lorsque j'utilise la méthode GetExportedValue<T>(), la pièce retournée doit-elle déjà être composée (ou a-t-elle rempli ses importations) ou dois-je explicitement composer la pièce?

Lire la suite pour plus de détails sur chaque question ...

Contexte

En ce qui concerne la première question, j'ai un formulaire de demande principale qui a un panneau qui application hôte « normale » usercontrols comme ainsi que les contrôles userc des plugins. Ce formulaire d'application principal implémente une interface appelée IHostingForm qui lui permet d'exposer certaines de ses propres fonctionnalités aux plugins. Ce formulaire d'application principal s'exporte à travers l'interface IHostingForm et a une importation qui doit être remplie. Voici un extrait de code:

[Export(typeof(IHostingForm))] 
public partial class frmMain : RibbonForm, IHostingForm 
{ 
    //public/private form methods here... 
    private CompositionContainer _container; 

    //An import that needs to be filled. 
    [ImportMany] 
    public IEnumerable<Lazy<IAddInController, IAddInControllerMetadata>> AddInControllers; 

    public frmMain() 
    { 
     //Init code here omitted to save space... 

     //Setup the container 
     var catalog = new AggregateCatalog(); 
     catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly)); 
     //Temporary location while testing... 
     catalog.Catalogs.Add(new DirectoryCatalog(Application.StartupPath)); 

     _container = new CompositionContainer(catalog); 
     try 
     { 
      //Is the current instance of the class (this) added to the 
      //container or does the container construct its own instance? 
      _container.ComposeParts(this); 
     } 
     catch (CompositionException ex) 
     { 
      //Error catching code omitted... 
     } 
    } 

    //IHostingForm implementation 
    public void AddTabToTabBar(string tabText) 
    { 
     RibbonTab rt = new RibbonTab(tabText); 
     //Some more init code here which I'm leaving out to save space... 
     this.ribbonBar1.CommandTabs.Add(rt); 
    } 
    //etc... 
} 

Avis dans l'extrait ci-dessus comment il est lui-même exportateur, mais il appelle également ComposeParts(this) dans le constructeur? Est-ce que cela crée une instance distincte du formulaire ou est ComposeParts(this) suffisamment intelligent pour savoir utiliser l'instance déjà existante et l'ajouter simplement au conteneur?

Ceci conduit à ma deuxième question. Comme je ne savais pas si la méthode de composition que j'utilisais était correcte ou non, j'ai essayé de créer le conteneur dans la classe Program de l'application et j'ai trouvé un autre comportement que je ne comprends pas. Je me suis déplacé le code d'initialisation du conteneur de la classe de la forme et dans le Programme comme celui-ci:

static class Program 
{ 
    internal static CompositionContainer _container; 

    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    [STAThread] 
    static void Main() 
    { 
     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 

     //Setup the container 
     var catalog = New AggregateCatalog(); 
     catalog.Catalogs.Add(new AssemblyCatalog(typeof(Program).Assembly)); 
     catalog.Catalogs.Add(new DirectoryCatalog(Application.StartupPath)); 

     _container = new CompositionContainer(catalog); 

     IHostingForm frm = _container.GetExportedValue<IHostingForm>(); 
     //If I don't call this, the form's imports don't get satisfied 
     _container.CompostParts(frm); 
     Application.Run((Form)frm); 
    } 
} 

Sur la base de la documentation sur MSDN et aussi the answer to this question, j'avais l'impression que la méthode GetExportedValue<T>() instancierez l'exportation des données taper et aussi le composer (ou satisfaire ses dépendances?) parce qu'il sortait du conteneur. Mais grâce à des tests, il semble que, après avoir obtenu la pièce du conteneur, je dois explicitement dire au conteneur de composer la pièce afin de remplir ses importations. Cela semble contredire ce que j'ai lu jusqu'ici.

Je ne savais pas si c'était plusieurs questions ou non. Si elles doivent être divisées, faites le moi savoir et je serai heureux de modifier cette question et d'en créer une autre. Mais, pour moi, ils semblent liés en ce sens qu'ils traitent tous deux de mon manque de compréhension de ce qui se passe avec le conteneur et la composition.

Répondre

1

Vous remarquez dans l'extrait ci-dessus comment il s'exporte mais appelle également ComposeParts (this) dans le constructeur?Est-ce que cela crée une instance distincte du formulaire ou est-ce que ComposeParts (this) est suffisamment intelligent pour savoir utiliser l'instance déjà existante et l'ajouter au conteneur?

Il est «assez intelligent» comme dans, il fera ce qu'il dit. ComposeParts(this) regardera d'abord à laquelle Import s doivent être satisfaits pour this. Il essaiera alors de trouver le type/types avec un Export correspondant et appellera leur constructeur sans paramètre, après qu'une instance ait été créée, il regardera les Import s de ce type et recommencera tout à nouveau.

Si, à un moment donné, il rencontre une directive Import qui demande un type déjà créé, il alimentera l'instance déjà existante dans cette directive Import - au lieu de créer une seconde instance.

Vous pouvez facilement vérifier cela en ajoutant un constructeur sans paramètre à quelques types et y placer un point d'arrêt.

Quant à votre deuxième question, je vous suggère de poser une question séparée - mais pour autant que je sache, ComposeParts trouvera ou instancier précédemment parties inutilisées/inconnues et annoncent l'attribut Export du type/objet auquel il a été appelé à partir de - dans votre cas [Export(typeof(IHostingForm))]. D'autre part GetExportedValue ne recherche les déjà parties et existantes ne instancier de nouvelles pièces - ce qui signifie que si une Import demande pour une partie X, mais pas une telle partie a été précédemment créée par ComposeParts ou SatisfyImports - la propriété/champ derrière cette Import restera insatisfait/null.