web-dev-qa-db-fra.com

Avec Unity, comment injecter une dépendance nommée dans un constructeur?

J'ai la IRespository enregistrée deux fois (avec les noms) dans le code suivant:

// Setup the Client Repository
IOC.Container.RegisterType<ClientEntities>(new InjectionConstructor());
IOC.Container.RegisterType<IRepository, GenericRepository>
    ("Client", new InjectionConstructor(typeof(ClientEntities)));

// Setup the Customer Repository
IOC.Container.RegisterType<CustomerEntities>(new InjectionConstructor());
IOC.Container.RegisterType<IRepository, GenericRepository>
    ("Customer", new InjectionConstructor(typeof(CustomerEntities)));

IOC.Container.RegisterType<IClientModel, ClientModel>();
IOC.Container.RegisterType<ICustomerModel, CustomerModel>();

Mais ensuite, quand je veux résoudre ceci (pour utiliser la IRepository), je dois faire une résolution manuelle comme ceci:

public ClientModel(IUnityContainer container)
{
   this.dataAccess = container.Resolve<IRepository>(Client);

   .....
}

Ce que je voudrais faire est de le résoudre dans le constructeur (comme IUnityContainer). J'ai besoin d'un moyen de dire quel type nommé à résoudre.

Quelque chose comme ceci: (NOTE: code non réel)

public ClientModel([NamedDependancy("Client")] IRepository dataAccess)
{
   this.dataAccess = dataAccess;

   .....
}

Existe-t-il un moyen de faire fonctionner mon faux code?

64
Vaccano

Vous pouvez configurer des dépendances avec ou sans noms dans l'API, des attributs ou via le fichier de configuration. Vous n'avez pas mentionné XML ci-dessus, alors je suppose que vous utilisez l'API.

Pour que le conteneur résolve une dépendance nommée, vous devez utiliser un objet InjectionParameter. Pour votre exemple ClientModel, procédez comme suit:

container.RegisterType<IClientModel, ClientModel>(
    new InjectionConstructor(                        // Explicitly specify a constructor
        new ResolvedParameter<IRepository>("Client") // Resolve parameter of type IRepository using name "Client"
    )
);

Cela indique au conteneur "Lors de la résolution de ClientModel, appelez le constructeur utilisant un seul paramètre IRepository. Lors de la résolution de ce paramètre, résolvez-le avec le nom" Client "en plus du type."

Si vous voulez utiliser des attributs, votre exemple fonctionne presque, il vous suffit de changer le nom de l'attribut:

public ClientModel([Dependency("Client")] IRepository dataAccess)
{
   this.dataAccess = dataAccess;

   .....
}
75
Chris Tavares

C'est une réponse très tardive, mais la question est toujours visible dans Google.

Bref, 5 ans plus tard ...

J'ai une approche assez simple. Généralement, lorsque vous devez utiliser la "dépendance nommée", c'est parce que vous essayez d'implémenter un type de modèle de stratégie. Dans ce cas, je crée simplement un niveau d'indirection entre Unity et le reste de mon code appelé StrategyResolver afin de ne pas dépendre directement de Unity.

public class StrategyResolver : IStrategyResolver
{
    private IUnityContainer container;

    public StrategyResolver(IUnityContainer unityContainer)
    {
        this.container = unityContainer;
    }

    public T Resolve<T>(string namedStrategy)
    {
        return this.container.Resolve<T>(namedStrategy);
    }
}

Usage:

public class SomeClass: ISomeInterface
{
    private IStrategyResolver strategyResolver;

    public SomeClass(IStrategyResolver stratResolver)
    {
        this.strategyResolver = stratResolver;
    }

    public void Process(SomeDto dto)
    {
        IActionHandler actionHanlder = this.strategyResolver.Resolve<IActionHandler>(dto.SomeProperty);
        actionHanlder.Handle(dto);
    }
}

Enregistrement:

container.RegisterType<IActionHandler, ActionOne>("One");
container.RegisterType<IActionHandler, ActionTwo>("Two");
container.RegisterType<IStrategyResolver, StrategyResolver>();
container.RegisterType<ISomeInterface, SomeClass>();

Maintenant, l’avantage de Nice, c’est que je n’aurai plus jamais besoin de toucher au StrategyResolver lors de l’ajout de nouvelles stratégies.

C'est très simple. Très propre et j'ai gardé la dépendance à Unity au strict minimum. La seule fois où j'aurais touché le StrategyResolver, c'est si je décidais de changer de technologie de conteneur, ce qui est très peu probable.

J'espère que cela t'aides!

Edit: Je n’aime pas vraiment la réponse acceptée, car lorsque vous utilisez l’attribut Dependency dans le constructeur de votre service, vous dépendez réellement de Unity. L'attribut Dependency fait partie de la bibliothèque Unity. À ce stade, vous pouvez également passer une dépendance IUnityContainer partout.

Je préfère que mes classes de service dépendent d'objets que je possède complètement plutôt que de dépendre fortement d'une bibliothèque externe. De plus, l’utilisation de l’attribut Dependency rend les signatures des constructeurs moins simples et plus propres.

De plus, cette technique permet de résoudre les dépendances nommées au moment de l'exécution sans devoir coder en dur les dépendances nommées dans le constructeur, dans le fichier de configuration de l'application ou utiliser InjectionParameter, qui sont toutes des méthodes nécessitant de savoir quelle dépendance nommée utiliser lors de la conception.

Edit (2016-09-19): Pour ceux qui pourraient se poser des questions, le conteneur saura se transmettre lorsque vous demanderez IUnityContainer en tant que dépendance, comme indiqué dans la signature du constructeur StrategyResolver.

Edit (2018-10-20): Voici une autre méthode, en utilisant simplement une usine:

public class SomeStrategyFactory : ISomeStrategyFactory
{
    private IStrategy _stratA;
    private IStrategy _stratB;

    public SomeFactory(IStrategyA stratA, IStrategyB stratB)
    {
        _stratA = stratA;
        _stratB = stratB;
    }

    public IStrategy GetStrategy(string namedStrategy){
        if (namedStrategy == "A") return _stratA;
        if (namedStrategy == "B") return _stratB;
    }
}

public interface IStrategy {
    void Execute();
}

public interface IStrategyA : IStrategy {}

public interface IStrategyB : IStrategy {}

public class StrategyA : IStrategyA {
    public void Execute(){}
}

public class StrategyB : IStrategyB {
    public void Execute() {}
}

Usage:

public class SomeClass : ISomeClass
{
    public SomeClass(ISomeStrategyFactory strategyFactory){

        IStrategy strat = strategyFactory.GetStrategy("HelloStrategy");
        strat.Execute();

    }
}

Enregistrement:

container.RegisterType<ISomeStrategyFactory, SomeStrategyFactory>();
container.RegisterType<IStrategyA, StrategyA>();
container.RegisterType<IStrategyB, StrategyB>();
container.RegisterType<ISomeClass, SomeClass>();

Cette deuxième suggestion est la même chose mais en utilisant le modèle de conception d'usine.

J'espère que cela t'aides!

22
TchiYuan

Vous devriez pouvoir utiliser ParameterOverrides

var repository = IOC.Container.Resolve<IRepository>("Client");
var clientModel = IOC.Container.Resolve<ClientModel>(new ParameterOverrides<ClientModel> { {"dataAccess", repository } } );

edit: Je ne suis pas sûr de savoir pourquoi vous passez autour de UnityContainer - personnellement, nous injectons nos dépendances dans le constructeur (ce qui est "normal" de ce que j'ai vu) Mais peu importe, vous pouvez spécifier un nom dans vos méthodes RegisterType et Resolve.

IOC.Container.RegisterType<IRepository, GenericRepository>("Client");
IOC.Container.Resolve<IRepository>("Client");

et il vous donnera le type que vous avez enregistré pour ce nom.

3
Kyle W

Ne faites pas cela - créez simplement un class ClientRepository : GenericRepository { } et utilisez le système de types.

0
mcintyre321