2010-09-12 18 views
4

J'ai une dépendance circulaire qui vient de se produire en raison d'un changement dans mon architecture de l'application.MEF erreur, était la dépendance circulaire et est maintenant quelque chose d'autre

L'application repose sur un gestionnaire de plugins qui charge les plugins via MEF. Tout jusqu'à fonctionnait très bien, car il avait l'air quelque chose comme ceci:

// model.cs 
[Export("Model")] 
public class Model 
{ 
    public PluginManager PM { get; set; } 

    [ImportingConstructor] 
    public Model([Import] PluginManager plugin_manager) 
    { 
    PM = plugin_manager; 
    } 
} 

// pluginmanager.cs 
[Export(typeof(PluginManager))] 
public class PluginManager 
{ 
    [ImportMany(typeof(PluginInterface))] 
    private IEnumerable<PluginInterface> Plugins { get; set; } 
} 

et les plugins ressemblait à ceci:

// myplugin.cs 
[Export(typeof(PluginInterface))] 
public class MyPlugin : PluginInterface 
{ 
} 

Mais maintenant, j'ai une situation où je veux tous les plug-ins d'avoir la possibilité d'interroger le PluginManager (ou éventuellement tout autre objet) via une interface pour connaître les autres plugins du système afin de connaître leurs capacités. J'ai "résolu" cela en ajoutant une autre interface, appelons cela PluginQueryInterface. J'ai ensuite eu le Modèle mettre en œuvre cette interface.

[Export("Model"))] 
[Export(typeof(PluginQueryInterface))] 
public class Model : PluginQueryInterface 
{ 
    // same as before 
} 

et la signature du plugin ressemblerait à ceci:

// 1st possible implementation 
[Export(typeof(PluginInterface))] 
public class MyPlugin : PluginInterface 
{ 
    [Import(typeof(PluginQueryInterface))] 
    public PluginQueryInterface QueryInterface { get; set; } 

    public MyPlugin() {} 
} 

ou cette

// 2nd possible implementation 
[Export(typeof(PluginInterface))] 
public class MyPlugin : PluginInterface 
{ 
    private PluginQueryInterface QueryInterface { get; set; } 

    [ImportingConstructor] 
    public MyPlugin([Import] PluginQueryInterface query_interface) 
    { 
    QueryInterface = query_interface 
    } 
} 

Le 2ème mise en œuvre est assez clairement une référence circulaire, parce que les plug-ins nécessite que le PluginQueryInterface soit créé avant la création du plugin, mais Le PluginQueryInterface est le Model, qui doit importer le PluginManager, qui à son tour a besoin de toutes les PluginInterfaces créées ... et je reçois une erreur de dépendance circulaire MEF lors de mon lancement.

La mise en œuvre 1st ne me semble pas être une référence circulaire. Si le PluginQueryInterface est une propriété, alors je pensais qu'il ne serait pas résolu jusqu'à ce qu'il soit utilisé. Et il n'est pas du tout utilisé par le constructeur. Alors pourquoi le PluginManager ne créerait-il pas joyeusement tous mes MyPlugins? Je reçois la même erreur MEF dans les deux cas.

J'ai essayé de résoudre ce problème en faisant en sorte que PluginManager implémente le PluginQueryInterface, car a) cela a du sens quand même et b) c'est known way of dealing with circular dependencies - faites dépendre les deux classes interdépendantes d'une troisième classe. Maintenant, le problème est que je reçois un erreur différente MEF! C'est ce que cela dit:

GetExportedValue cannot be called before prerequisite import 'Company.App.PluginManager..ctor(Parameter="database_filepath", ContractName="PluginManager.filename")' has been set. 

WTF? J'ai défini des points d'arrêt dans mon code et ma valeur exportée PluginManager.filenamea été définie avant d'appeler GetExportedValue.

Je suis totalement perplexe. Toutes les observations ou suggestions seraient grandement appréciées en ce moment. Je me suis cogné la tête contre le mur vêtu MEF pendant des heures essayant de déboguer ce problème.

(mis à jour)

Je ne pensais pas à ce sujet plus tôt, mais cela aurait pu être des différences entre les plug-ins, alors j'ai supprimé l'une des deux plug-ins, et maintenant mes charges d'application sans erreurs MEF. Je l'ai ajouté, et il a échoué à nouveau. Puis j'ai supprimé l'autre plugin, et cela a fonctionné. Donc, il semble que ce soit une autre erreur MEF.C'est presque comme si elle ne voulait pas que je charge plus d'un plugin avec une interface spécifique ... mais j'utilise ImportMany, et cela ne se serait-il pas manifesté comme un CardinalityException quelconque?

MISE À JOUR

Je ne comprends pas cette partie du MEF, et nous espérons que quelqu'un ici peut expliquer ce qu'il en est. Après être entré dans le code pendant un certain temps, j'ai trouvé que mon erreur provenait de MEF supprimant les définitions d'importation après avoir trouvé la valeur!

private bool TryGetImportValue(ImportDefinition definition, out object value) 
    { 
     lock (this._lock) 
     { 
      if (this._importValues.TryGetValue(definition, out value)) 
      { 
       this._importValues.Remove(definition); // this is the line that got me 
       return true; 
      } 
     } 

     value = null; 
     return false; 
    } 

Je ne l'ai jamais eu ce problème avant, et franchement, je vais avoir du mal à comprendre ce que je fais maintenant avec mes importations et les exportations qui a fait cette surface de problème. Je suppose que je fais quelque chose que les concepteurs du MEF n'avaient pas l'intention de faire. Je pourrait commenter aveuglément le this._importValues.Remove(definition);, mais cela ne pourrait pas être vrai. Je suppose que cela va se résumer aux attributs MEF que j'ai utilisés, mais puisque le plugin qui importe cette valeur a une politique de création de CreationPolicy.Shared, pourquoi aurais-je un problème?

Répondre

3

Eh bien, j'ai une solution possible. Je n'ai aucune expérience avec l'utilisation de ceci, mais en utilisant Lazy instantiation semble aider. Au moins, je peux aller de l'avant sans avoir à modifier le code MEF que je ne comprends pas complètement.