web-dev-qa-db-fra.com

Création d'une instance à l'aide de Ninject avec des paramètres supplémentaires dans le constructeur

J'ai décidé de commencer à utiliser Ninject et à faire face à un problème. Disons que j'ai le scénario suivant. J'ai une interface IService et 2 classes implémentant cette interface. Et j'ai aussi une classe, qui a un constructeur obtenant IService et un int. Comment puis-je créer une instance de cette classe avec Ninject (je ne veux pas câbler cet int, je veux le passer chaque fois que j'obtiens une instance)?

Voici un code illustrant la situation:

interface IService
{
    void Func();
}

class StandardService : IService
{
    public void Func()
    {
        Console.WriteLine("Standard");
    }
}

class AlternativeService : IService
{
    public void Func()
    {
        Console.WriteLine("Alternative");
    }
}


class MyClass
{
    public MyClass(IService service, int i)
    {
        this.service = service;
    }

    public void Func()
    {
        service.Func();
    }

    IService service = null;
}
class Program
{
    static void Main(string[] args)
    {
        IKernel kernel = new StandardKernel(new InlineModule(
            x => x.Bind<IService>().To<AlternativeService>(),
            x => x.Bind<MyClass>().ToSelf()));

        IService service = kernel.Get<IService>();

        MyClass m = kernel.Get<MyClass>();
        m.Func();
    }
}
63
StuffHappens

Le With.ConstructorArgument Existait en 1.0 à cet effet. En 2.0, la syntaxe a légèrement changé: - With.Parameters.ConstructorArgument avec ninject 2.

Voir Injecter la valeur dans la dépendance injectée pour plus de détails et des exemples sur la façon d'utiliser le contexte, les fournisseurs et les arguments pour transmettre plus correctement des choses comme celle-ci.

EDIT: comme Steven a choisi de prétendre que mon commentaire n'est pas pertinent, je ferais mieux de clarifier ce que je dis avec quelques exemples (pour 2.0):

MyClass m = kernel.Get<MyClass>( new ConstructorArgument( "i", 2) );

qui à mes yeux est très clair et indique exactement ce qui se passe.

Si vous êtes dans une position où vous pouvez déterminer le paramètre d'une manière plus globale, vous pouvez enregistrer un fournisseur et le faire comme ceci:

class MyClassProvider : SimpleProvider<MyClass>
{
    protected override MyClass CreateInstance( IContext context )
    {
        return new MyClass( context.Kernel.Get<IService>(), CalculateINow() );
    }
}

Et enregistrez-le comme ceci:

x => x.Bind<MyClass>().ToProvider( new MyClassProvider() )

NB le bit CalculateINow() est l'endroit où vous mettriez votre logique comme dans la première réponse.

Ou rendez-le plus complexe comme ceci:

class MyClassProviderCustom : SimpleProvider<MyClass>
{
    readonly Func<int> _calculateINow;
    public MyClassProviderCustom( Func<int> calculateINow )
    {
        _calculateINow = calculateINow;
    }

    protected override MyClass CreateInstance( IContext context )
    {
        return new MyClass( context.Kernel.Get<IService>(), _calculateINow() );
    }
}

Que vous enregistreriez comme ceci:

x => x.Bind<MyClass>().ToProvider( new MyClassProviderCustom( (  ) => new Random( ).Next( 9 ) ) )

MISE À JOUR: Des mécanismes plus récents qui présentent des modèles bien améliorés avec moins de passe-partout que les précédents sont incorporés dans l'extension Ninject.Extensions.Factory, Voir: https://github.com/ninject/ninject.extensions.factory/wiki

Comme indiqué précédemment, si vous devez passer un paramètre différent à chaque fois et que vous avez plusieurs niveaux dans le graphique de dépendance, vous devrez peut-être faire quelque chose comme ça .

Une dernière considération est que parce que vous n'avez pas spécifié un Using<Behavior>, Il va par défaut être le défaut comme spécifié/par défaut dans les options pour le noyau (TransientBehavior dans l'exemple) qui pourrait rendre fait que l'usine calcule i à la volée [par exemple, si l'objet était mis en cache]

Maintenant, pour clarifier certains autres points dans les commentaires qui sont FUD et passés sous silence. Certaines choses importantes à considérer à propos de l'utilisation de DI, que ce soit Ninject ou quoi que ce soit d'autre:

  1. Faites autant que possible par injection de constructeur afin que vous n'ayez pas besoin d'utiliser des attributs et des astuces spécifiques au conteneur. Il y a un bon article de blog à ce sujet appelé Votre conteneur IoC s'affiche .

  2. Minimisez le code allant au conteneur et demandant des choses - sinon votre code est couplé à a) le conteneur spécifique (que la CSL peut minimiser) b) la manière dont l'ensemble de votre projet est présenté. Il y a de bons articles sur ce blog montrant que CSL ne fait pas ce que vous pensez qu'il fait. Ce sujet général est appelé Emplacement du service vs Injection de dépendance . MISE À JOUR: Voir http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx pour une justification détaillée et complète.

  3. Minimisez l'utilisation de la statique et des singletons

  4. Ne présumez pas qu'il n'y a qu'un seul conteneur [global] et qu'il est OK de l'exiger à chaque fois que vous en avez besoin comme une variable globale Nice. L'utilisation correcte de plusieurs modules et Bind.ToProvider() vous donne une structure pour gérer cela. De cette façon, chaque sous-système séparé peut fonctionner seul et vous n'aurez pas de composants de bas niveau liés aux composants de niveau supérieur, etc.

Si quelqu'un veut remplir les liens vers les blogs dont je parle, je l'apprécierais (ils sont déjà tous liés à partir d'autres articles sur SO cependant, donc tout cela est une simple interface utilisateur de duplication a été introduite dans le but d'éviter la confusion d'une réponse trompeuse.)

Maintenant, si seulement Joel pouvait entrer et me mettre au clair sur la syntaxe de Nice et/ou la bonne façon de le faire!

MISE À JOUR: Bien que cette réponse soit clairement utile compte tenu du nombre de votes positifs qu'elle a recueillis, je voudrais faire les recommandations suivantes:

  • Ce qui précède se sent comme il est un peu daté et pour être honnête reflète beaucoup de pensée incomplète qui se sent presque embarrassante depuis la lecture Injection de dépendance en .net - Courez et achetez-le maintenant - il ne s'agit pas seulement de DI, le la première moitié est un traitement complet de toutes les préoccupations d'architecture qui l'entourent d'un homme qui a passé beaucoup trop de temps ici à traîner la balise d'injection de dépendance.
  • Allez lire Les articles les mieux notés de Mark Seemann ici sur SO en ce moment - vous apprendrez de précieuses techniques de chacun
93
Ruben Bartelink