Comment les infrastructures DI résolvent-elles la dépendance pour une même interface avec plusieurs configurations?

Considérez ci-dessous un exemple de code:

public interface IMyInterface { void SetName(ssortingng name); ssortingng GetName(); } public class MyInterfaceImplementor1 : IMyInterface { protected ssortingng Name { set; get; } public void SetName(ssortingng name) { this.Name = name; } public virtual ssortingng GetName() { return this.Name; } } public class MyInterfaceImplementor2 : MyInterfaceImplementor1 { public override ssortingng GetName() { return Ssortingng.Format("Hello! {0}", base.Name); } } 

Et la configuration DI pour cela: (extrait de code StructureMap fourni)

 ObjectFactory.Configure(x => { x.For() .Use(); }); ObjectFactory.Configure(x => { x.For() .Use(); }); 

Supposons que, dans mon code, j’utilise MyInterfaceImplementor1 à un moment donné et que j’utilise MyInterfaceImplementor2 à un autre moment. Ma question est, comment le framework DI (StructureMap ou tout autre) va résoudre la configuration ci-dessus? En outre, comment déterminera-t-il où renvoyer une instance de MyInterfaceImplementor1 et quand identique de MyInterfaceImplementor2 ? OU est-ce que je fais quelque chose de mal ici?

Dans votre cas, StructureMap résoudra MyInterfaceImplementor2 . StructureMap stocke tous les enregistrements mais résout le dernier enregistré avec la méthode Use () .

Nomenclature

Type de plugin – type à injecter, dans la plupart des cas, il s’agit d’une interface, mais SM résout également des types concrets (même si vous ne les avez pas enregistrés).

Type bouché / concret – implémentation du type plugin

 c.For().Use(); 

En règle générale, il existe 2 types d’enregistrement manuel dans SM. Vous pouvez enregistrer des dépendances avec les méthodes Add () et Use () .

Quelle est la différence?

  • Add () enregistre juste le type concret dans la famille de plugin
  • Use () enregistre et le définit comme enregistrement par défaut à utiliser lors de la résolution du type de plugin

Pour Add (), il existe une convention selon laquelle, lorsque vous n’avez qu’un seul enregistrement, celui-ci sera résolu. Dans le cas où il y aurait plus d’un conteneur d’enregistrement, une exception StructureMapException serait lancée lors de la tentative de résolution du type de plug-in.

La réponse de csprabala fournit une bonne explication sur la manière dont plusieurs implémentations doivent être enregistrées dans StructureMap.

Quand j’ai plusieurs implémentations de type plugin, j’enregistre toujours la méthode avec Add () et s’il y a une implémentation par défaut, je l’enregistre avec Use ()

Comment mapper la même interface à différentes ConcreteClasses avec StructureMap?

Regardez ci-dessus. La plupart des frameworks DI utilisent des astuces présentées sous la forme de noms / atsortingbuts pour injecter les classes concrètes appropriées au moment de l’exécution.

Regardez ci-dessous pour

StructureMap: Choisissez un type concret de dépendance nestede

Dans certains cas, il est judicieux d’avoir plusieurs implémentations de la même interface, mais pas dans tous les cas. Assurez-vous toujours de ne pas enfreindre le principe de substitution de Liskov (LSP). Voici deux cas, l’un dans lequel l’utilisation de plusieurs implémentations est acceptable et l’autre dans lequel ce n’est pas le cas.

Disons par exemple que vous avez une abstraction FileLogger avec les FileLogger et SqlLogger . Dans la plupart des cas, vous souhaitez consigner des éléments dans la firebase database, mais dans certaines parties du système, vous souhaitez explicitement vous connecter à un fichier, car les erreurs de journalisation dans cette partie du système sont relevées par un autre access à la firebase database. Bien que vous deviez toujours vous demander si vous vous connectez trop , dans la mesure où l’échange des implémentations n’affecte pas les consommateurs de l’abstraction d’ ILogger , du sharepoint vue du LSP, tout va bien.

Cependant, supposons que votre application communique avec deux bases de données, chacune ayant son propre schéma. En plus de cela, vous avez une abstraction IUnitOfWork , utilisée pour communiquer avec la firebase database. Sur la base de cette abstraction, vous avez implémenté OrdersUnitOfWork pour communiquer avec la firebase database Orders et CrmUnitOfWork pour communiquer avec la firebase database CRM. Il est évident que certaines parties du système doivent faire fonctionner OrdersUnitOfWork , tandis que d’autres ont besoin de CrmUnitOfWork . Si tel est le cas, vous rompez le LSP, car vous ne pouvez pas simplement échanger les implémentations sans que les consommateurs le remarquent. Non, lorsque vous fournissez à un consommateur la mauvaise implémentation, cela casserait complètement, car les schémas de firebase database sont complètement différents. Ceci est une violation du LSP.

Donc, dans ce dernier cas, le problème réside dans la conception de l’application et peut être résolu en atsortingbuant à chaque firebase database sa propre abstraction. Par exemple: IOrdersUnitOfWork et ICrmUnitOfWork .

Si vous effectuez ce type d’enregistrement, tout dépend de la bibliothèque DI que vous utilisez, qu’elle retourne la première ou la dernière. Ceci est souvent très peu intuitif, ce qui facilite grandement les erreurs de configuration.

Pour cette raison, la bibliothèque DI Simple Injector n’autorise pas ce type d’enregistrement. Les concepteurs de Simple Injector considèrent cette API comme un défaut de conception, comme nous l’expliquons ici . Au lieu de cela, Simple Injector vous oblige à faire un seul enregistrement pour une clé donnée ( IMyInterface dans votre cas) ou à enregistrer une collection de choses. Cela évite la confusion dans son ensemble quant à ce que la bibliothèque retournera pour vous.

Si vous devez déterminer l’implémentation à injecter, Simple Injector vous permet d’effectuer une injection en fonction du contexte . Par exemple:

 container.Register(); container.Register(); container.RegisterWithContext(context => context.ImplementationType.Namespace.Contains("Administrator") ? container.GetInstance() : container.GetInstance()); 

Ici, l’implémentation à injecter est déterminée en fonction de l’espace de noms du consommateur. Vous pouvez bien sûr penser à toutes sortes de règles pour déterminer quoi injecter.

StructureMap explose avec une exception, car il n’essayera PAS de deviner lequel vous voulez s’il y a plus d’un enregistrement – et je maintiens cette décision à ce jour. Certains des autres conteneurs IoC utiliseront le premier ou le dernier enregistré.