Question

Bien que cette question soit liée à StructureMap, ma question générale est:

  

Lors du câblage de composants avec un IoC   conteneur dans le code (par opposition à   configurer via xml ) voulez-vous   généralement besoin de projet explicite / construction   des références à tous les assemblages?

Pourquoi les assemblées séparées? Parce que:

  

" Classes abstraites résidant dans un   assemblage séparé de leur béton   les implémentations sont un excellent moyen de   réaliser une telle séparation. " - Cadre   Directives de conception p.91

Exemple:

Disons que j'ai PersonBase.dll et Bob.dll

Bob hérite de la classe abstraite PersonBase . Ils sont tous deux dans l'espace de noms Personne . Mais dans différents assemblages .

Je programme pour PersonBase et non pour Bob .

De retour dans mon code principal, j'ai besoin d'une personne. StructureMap peut analyser des assemblages. Génial, je vais en demander un à StructureMap!

Maintenant, dans mon code principal, je ne parle bien sûr que de PersonBase , et non de Bob . En fait, je ne veux pas que mon code connaisse quoi que ce soit à propos de Bob . Aucune référence de projet, pas de nuthin. C'est tout le problème.

Je veux donc dire:

//Reference: PersonBase.dll (only)
using Person;  
...

//this is as much as we'll ever be specific about Bob:
Scan( x=> { x.Assembly("Bob.dll"); }

//Ok, I should now have something that's a PersonBase (Bob). But no ?
ObjectFactory.GetAllInstances<PersonBase>().Count == 0

Pas de chance. Qu'est-ce que le travail est explicite que je veux Bob:

//Reference: PersonBase.dll and Bob.dll
using Person; 
...
Scan( x => {x.Assembly("Bob.dll"); }

//If I'm explicit, it works. But Bob's just a PersonBase, what gives?
ObjectFactory.GetAllInstances<Bob>().Count == 1 //there he is!

Mais maintenant, j'ai dû faire référence à Bob.dll dans mon projet, ce qui est exactement ce que je ne voulais pas.

Je peux éviter cette situation en utilisant la configuration Spring + XML. Mais je reviens à la configuration de Spring + XML ...!

  

Est-ce que je manque quelque chose avec l'utilisation   StructureMap, ou en tant que général   principe, do (fluent) IoC   les configurations nécessitent des références explicites   à tous les assemblées?

Question possiblement liée: StructureMap et ensembles de numérisation

Était-ce utile?

La solution

J'ai finalement résolu ce problème. Cela ressemble à ceci:

IoC Uml http://img396.imageshack.us/img396/1343/iocuml. jpg

avec les assemblées

  • Core.exe
  • PersonBase.dll (référencé moment de la compilation par Core.exe)
  • Bob.dll ( temps d'exécution chargé via StructureMap Scan)
  • Betty.dll ( temps d'exécution chargé via StructureMap Scan)

Pour l'obtenir avec StructureMap, j'avais besoin d'un "ITypeScanner" personnalisé. pour prendre en charge l'analyse des assemblages:

public class MyScanner : ITypeScanner {
  public void Process(Type type, PluginGraph graph) {

    if(type.BaseType == null) return;

    if(type.BaseType.Equals(typeof(PersonBase))) {
      graph.Configure(x => 
        x.ForRequestedType<PersonBase>()
        .TheDefault.Is.OfConcreteType(type));
    }
  }
} 

Donc, mon code principal ressemble à:

ObjectFactory.Configure(x => x.Scan (
  scan =>
  {
    scan.AssembliesFromPath(Environment.CurrentDirectory 
    /*, filter=>filter.You.Could.Filter.Here*/);

    //scan.WithDefaultConventions(); //doesn't do it

    scan.With<MyScanner>();
  }
));

ObjectFactory.GetAllInstances<PersonBase>()
 .ToList()
  .ForEach(p => 
  { Console.WriteLine(p.FirstName); } );

Autres conseils

Vous pouvez également configurer XML avec StructureMap. Vous pouvez même les mélanger si vous le souhaitez.

Il existe également des attributs StructureMap que vous pouvez inclure dans votre classe Bob pour indiquer à StructureMap comment charger l'assembly. DefaultConstructor est celui que j'utilise de temps en temps.

L'option d'analyse automatique ne fonctionne que si vous conservez les conventions de dénomination, d'assemblage et d'espace de nom. Vous pouvez configurer manuellement structemap avec une interface fluide. Exemple:

ObjectFactory.Initialize(initialization => 
   initialization.ForRequestedType<PersonBase>()
    .TheDefault.Is.OfConcreteType<Bob>());

Ce que nous faisons sur mon projet actuel (qui utilise AutoFac, pas StructureMap, mais je pense que cela ne devrait pas faire de différence):

Nous avons les interfaces définissant les services externes que l'application utilise dans un assemblage principal, disons App.Core (comme votre PersonBase).

Nous avons ensuite les implémentations de ces interfaces dans Services.Real (comme Bob.dll).

Dans notre cas, nous avons également Service.Fake , utilisés pour faciliter les tests d'interface utilisateur avec des dépendances sur d'autres services d'entreprise et bases de données, etc.

.

Le "client" frontal; L’application elle-même (dans notre cas, l’application ASP.NET MVC) fait référence à App.Core .

Lorsque l'application démarre, nous utilisons Assembly.Load pour charger les "Services" appropriés. DLL d'implémentation, basée sur un paramètre de configuration.

Chacune de ces DLL a une implémentation d'IServiceRegistry qui renvoie une liste des services qu'elle implémente:

public enum LifestyleType { Singleton, Transient, PerRequest}

public class ServiceInfo {
    public Type InterfaceType {get;set;}
    public Type ImplementationType {get;set;}
    // this might or might not be useful for your app, 
    // depending on the types of services, etc.
    public LifestyleType Lifestyle {get;set;} 
}

public interface IServiceRegistry {
    IEnumerable<ServiceInfo> GetServices();
}

... l'application trouve ce ServiceRegistry via réflexion, énumère ces instances ServiceInfo et les enregistre dans le conteneur. Pour nous, ce registre-tout-services réside dans l'application Web, mais il est possible (et préférable dans de nombreux cas) de l'avoir dans une assemblée séparée.

De cette manière, nous pouvons isoler la logique de domaine du code d'infrastructure et empêcher "juste-ce-une-fois" les solutions de contournement où l'application finit par dépendre d'une référence directe au code d'infrastructure. Nous évitons également d'avoir à faire référence au conteneur dans chaque implémentation de Services.

Une chose vraiment importante si vous faites cela: assurez-vous que vous avez des tests qui vérifient que vous pouvez créer chaque "niveau supérieur". tapez (dans notre cas, les contrôleurs ASP.NET MVC) avec chaque configuration potentielle du conteneur IOC.

Sinon, il est assez facile d'oublier de mettre en œuvre une interface et de casser d'énormes sections de votre application.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top