2

Nous avons expérimenté StructureMap et je n'arrive pas à comprendre comment gérer des situations où une seule interface a plusieurs implémentations. Le code ci-dessous montre un exemple où deux bases de données sont accessibles à partir d'un même service.IOC avec plusieurs bases de données qui utilisent la même interface (StructureMap ou tout autre cadre DI)

public class SomeController : Controller 
{ 
    private ISomeService _service; 
    private IClientRepository _repository; 
    protected IContext _masterContext; 
    protected IContext _clientContext; 

    public SomeController(ISomeService service, ISomeRepository repository 
     , IContext masterCon, IContext clientCon) 
    { 
     _service = service; 
     _repository = repository; 
     _masterContext = masterCon; 
     _clientContext = clientCon; 
    } 
} 

public class SomeService : ISomeService 
{ 
    private IContext _masterContext; 
    private IContext _clientContext; 

    public SomeService(IContext masterContext, IContext clientContext) 
    { 
     masterContext = _masterContext; 
     clientContext = _clientContext; 
    } 
} 

public class ClientRepository : IClientRepository 
{ 
    private IContext _clientContext; 

    public ClientRepository(IContext clientContext) 
    { 
     _clientContext = clientContext; 
    } 
} 

public class MasterContext : IContext 
{ 
    public MasterContext(String connString) 
    //<snip, snip> implement 3rd party data context 
} 

public class ClientContext : IContext 
{ 
    public ClientContext(String connString) 
    //<snip, snip> implement 3rd party data context 
} 

StructureMap fonctionnait très bien quand nous avions un contexte unique (base de données), mais comment puis-je lui dire comment résoudre le 2e? Remarque: dans la plupart des cas, nous n'aurions pas de service gérant 2 bases de données (mais un contrôleur gérant 2 connexions, c'est-à-dire 2 référentiels accédant à 2 bases de données différentes), mais cela ne semble toujours pas simplifier.

Je suis à moitié prêt à abandonner l'utilisation d'un framework IoC et à revenir à DI de pauvre.

+0

Je suis dans une situation similaire. J'ai donc déjà 3 injections différentes dans mon contrôleur et je suis sur le point d'en ajouter une 4ème et comme vous - je pense qu'il doit y avoir une solution à ce problème. L'ironie est que tous ont les méthodes de base: Add, Update, FindSingle, FindMany, Delete et SaveChanges sauf qu'ils appellent un contexte différent. Quelqu'un a des exemples? – ozzy432836

Répondre

2

N'est-il pas possible d'avoir un IClientContext et un IMasterContext, possiblement héritant de IContext. Mon sentiment est que le code ferait l'une des deux choses très différentes selon que vous parliez à la base de données «Maître» ou «Client».

1

En Unity vous pouvez avoir des enregistrements nommés, ce qui vous permet d'enregistrer efficacement plus d'une classe pour une interface donnée. Ainsi, vous pouvez faire (en tapant par cœur, consultez la documentation de l'unité réelle si vous êtes intéressé):

container.RegisterType<IContext, MasterContext>("Master"); 
container.RegisterType<IContext, ClientContext>("Client"); 

et le constructeur de SOMESERVICE être:

public SomeService(
    [Dependency("Master")]IContext masterContext, 
    [Dependency("Client")]IContext clientContext) 
{ 
    //... 
} 

L'inconvénient est que de cette manière votre service La classe n'est plus indépendante du cadre DI utilisé, mais en fonction du projet, cela peut convenir.

1

Cela peut être un peu difficile si vous comptez sur StructureMap pour résoudre les dépendances automatiquement. La première solution (et ce à quoi je m'opposerais) est de faire usage d'interfaces de marqueur comme mentionne Richard dans sa réponse, puis de simplement les enregistrer. Vous pouvez ensuite spécifier explicitement si vous voulez que votre client ou le contexte maître s'y trouve.

La deuxième méthode consiste à utiliser les enregistrements nommés, puis à spécifier explicitement les paramètres du constructeur.

ForRequestedType<IContext>().AddInstances(
    i => { 
     i.OfConcreteType<ClientContext>().WithName("Client"); 
     i.OfConcreteType<MasterContext>().WithName("Master"); 
    }); 
ForRequestedType<SomeController>().TheDefault.Is.ConstructedBy(
    i => new SomeController(i.GetInstance<ISomeService>(), 
     i.GetInstance<IClientRepository>(), 
     i.GetInstance<IContext>("Master"), 
     i.GetInstance<IContext>("Client"))); 

Pas particulièrement agréable mais ça fait l'affaire et finalement si c'est seulement dans un ou deux endroits ça pourrait être OK.


Si vous voulez résoudre différemment namespace/assemblage, vous pouvez essayer quelque chose comme ceci: -

ForRequestedType<IContext>().AddInstances(
    i => { 
     i.OfConcreteType<ClientContext>().WithName("Client"); 
     i.OfConcreteType<MasterContext>().WithName("Master"); 
    }).TheDefault.Is.Conditional(c => { 
     c.If(con => con.ParentType.Namespace.EndsWith("Client")) 
      .ThenIt.Is.TheInstanceNamed("Client"); 
     c.If(con => con.ParentType.Namespace.EndsWith("Master")) 
      .ThenIt.Is.TheInstanceNamed("Master"); 
     c.TheDefault.Is.OfConcreteType<ClientContext>(); 
    }); 

Lorsque le prédicat ParentType peut se référer à l'Assemblée (ou tout ce que vous voulez vraiment)

+0

Nous les utilisons en fait partout (des douzaines de contrôleurs).Aux niveaux inférieurs, nous pourrions le séparer de sorte qu'un seul contexte soit utilisé par projet (nous avons des projets Client et Master séparés) - mais je ne voyais pas comment je pourrais configurer StructureMap pour résoudre différemment par projet/assemblage –

+0

Mise à jour concernant la résolution différemment par l'Assemblée –