2010-01-05 6 views
36

J'essaie de comprendre l'attribut Constructor Injection du MEF. Je n'ai aucune idée de comment je lui dis de charger les paramètres du constructeur.MEF Constructor Injection

C'est la propriété que je suis en train de charger

[ImportMany(typeof(BUsers))] 
public IEnumerable<BUsers> LoadBUsers { get; set; } 

Voici le code que je utilise pour importer les ensembles.

try 
{ 
    var catalog = new AggregateCatalog(); 
    catalog.Catalogs.Add(new AssemblyCatalog(System.Reflection.Assembly.GetExecutingAssembly())); 
    catalog.Catalogs.Add(new DirectoryCatalog("DI")); 
    var container = new CompositionContainer(catalog); 
    container.ComposeParts(this); 
} 

Voici la classe que je suis en train de charger

[Serializable] 
[Export(typeof(BUsers))] 
public class EditProfile : BUsers 
{ 
    [ImportingConstructor] 
    public EditProfile(string Method, string Version) 
    {    
     Version = "2"; 
     Action = "Edit"; 
     TypeName = "EditProfile"; 
    } 

Répondre

53

Lorsque vous utilisez l'attribut ImportingConstructor, les paramètres au constructeur deviennent les importations. Par défaut, ce que vous importez (le nom du contrat) est basé sur le type du paramètre ou de la propriété que vous importez. Donc, dans ce cas, le type de contrat pour vos deux importations est une chaîne, et il n'y a pas de différence réelle entre le premier et le deuxième paramètre. Il semble que vous essayiez d'utiliser des importations pour fournir des valeurs de configuration, ce qui n'est pas nécessairement ce pour quoi il a été conçu. Pour lui faire faire ce que vous voulez, vous devez remplacer le nom de contrat pour chacun des paramètres, comme ceci:

[ImportingConstructor] 
public EditProfile([Import("Method")] string Method, [Import("Version")] string Version) 
{ } 

Ensuite, vous avez besoin d'exportations pour la méthode et la version dans votre récipient. Une façon de le faire est juste pour les ajouter directement:

var container = new CompositionContainer(catalog); 
container.ComposeExportedValue("Method", "MethodValue"); 
container.ComposeExportedValue("Version", "2.0"); 
container.ComposeParts(this); 

(Notez que ComposeExportedValue est en fait une méthode d'extension définie sur la classe AttributedModelServices statique.)

Si vous voulez lire ces valeurs à partir d'une configuration En quelque sorte, vous pouvez créer votre propre fournisseur d'exportation qui lit la configuration et fournit les valeurs qu'il contient en tant qu'exportations vers le conteneur. Une autre façon de gérer cela serait de simplement importer une interface qui donne accès aux valeurs de configuration par nom, et d'obtenir les valeurs dont vous avez besoin du corps du constructeur.

+0

Je viens de télécharger le nouveau chez CodePlex. La méthode de ComposeExportedValue() n'est pas dans la classe de CompositionContainer. Où est-ce? –

+0

Je pense avoir trouvé la méthode. C'est dans la classe de AttributedModelServices, où la méthode est définie en tant que méthode étendue à la classe CompositionContainer. –

+1

@ David.Chu.ca Oui, ComposeExportedValue est une méthode d'extension de la classe AttributedModelServices. –

23

J'aime la solution de Daniel; cependant, une seule chose que je vois est le couplage serré des noms de paramètres entre l'acteur (qui crée CompopositionContrainer()) et la partie Export avec [ImportingConstructor] pour le CTOR personnalisé. Par exemple, "Méthode" a deux correspondent aux deux endroits. Il est difficile de maintenir la partie Export si l'acteur et la partie Export sont dans des projets différents.

Si c'est possible, j'ajouterais le deuxième CTOR à la classe de pièce Export. Par exemple:

[Export(typeof(BUsers))] 
public class EditProfile : BUsers 
{ 
    [ImportingConstructor] 
    public EditProfile(EditProfileParameters ctorPars) 
    : this(ctorPars.Method, ctorPars.Version) {} 

    public EditProfile(string Method, string Version) 
    { 
     Version = "2"; 
     Action = "Edit"; 
     TypeName = "EditProfile"; 
    } 

La classe de EditProfileParameters devrait être simple: deux propriétés de la méthode et Version:

[Export] 
public class EditProfileParameters{ 
    public string Method { get; set; } 
    public string Version { get; set; } 
} 

Le point clé est d'ajouter l'attribut d'exportation à la classe. Ensuite MEF devrait être capable de mapper cette classe au paramètre de CTOR EditProfile.

Voici exemple d'ajouter la partie Exporter vers conteneur:

var container = new CompositionContainer(catalog); 
var instance1 = new EditProfileParameters(); 
// set property values from config or other resources 
container.ComposeExportedValue(instance1); 
container.ComposeParts(this); 
0

Bien que la fin du jeu, voici une autre approche qui tire parti d'une caractéristique de MEF moins connu: Exportations de propriété

public class ObjectMother 
{ 
    [Export] 
    public static EditProfile DefaultEditProfile 
    { 
     get 
     { 
      var method = ConfigurationManager.AppSettings["method"]; 
      var version = ConfigurationManager.AppSettings["version"]; 

      return new EditProfile(method,version); 
     } 
    } 
} 

Aucune utilisation n'est requise pour ObjectMother pour que cela fonctionne, et aucun attribut n'est requis sur EditProfile.

+0

Belle fonctionnalité mais que faire si 'EditProfile' contient des importations? Dans ce cas, vous devez appeler 'SatisfyImportsOnce' sur le conteneur. Ça fait mal. Actuellement, je n'ai pas vraiment de solution à ce problème. –

+0

Dans ce cas, nous construisons une usine qui instancie manuellement votre EditProfile. Si EditProfile requiert des dépendances supplémentaires dans le constructeur, vous pouvez toujours importer ces dépendances dans Factory via un ImportingConstructor. Je dois préciser que si vous ajoutez des dépendances supplémentaires à EditProfile, c'est la seule référence dans votre application pour ce constructeur et vous obtiendrez des erreurs de compilation. C'est un commerce équitable, à mon humble avis. – bryanbcook