Question

Supposons que j'ai une interface pour un service:

public interface IFooService
{
   void DoSomething();
}

Et une mise en œuvre concrète de ce service qui est générique:

public class FooService<TRequestingClass> : IFooService
{
   public virtual void DoSomething() { }
}

Et j'ai une autre classe qui a besoin d'une instance de IFooService:

public class Bar
{
   private IFooService _fooService;
   public Bar(IFooService fooService)
   {
      this._fooService = fooService;
   }
}

Je dois câbler mon conteneur IoC de sorte que lorsque Bar est créé, il est transmis un argument constructeur de FooService . Il y a beaucoup d'autres classes, tout comme Bar. Chacun peut aussi avoir besoin d'une instance de FooService passé les où TRequestingClass est le type de la classe qui a besoin de l'instance de IFooService. Je ne ai pas besoin d'exposer cette bizarrerie aux consommateurs de IFooService. Tout ce qu'ils devraient se soucier est qu'ils peuvent appeler les méthodes de l'IFooService ils ont été adoptés. Ils ne devraient pas avoir besoin de savoir que la mise en œuvre concrète de IFooService ils ont été transmis quoi que ce soit nécessaire spécial à construire.

Un alternatative acceptable pour FooService serait d'être une classe non générique qui a un argument de chaîne dans son constructur qui contient le nom de la classe, il est créé. i.e.:

public class FooService : IFooService
{
   public FooService(string requestingClassName) { }
}

Comment puis-je brancher mon conteneur IoC pour construire une dépendance de cette façon?

Si vous êtes confus pourquoi je voudrais une telle structure wierd, examiner comment fonctionne log4net mieux quand vous obtenez un ILog qui est créé avec log4net.LogManager.GetLogger (typeof (SomeClass)). Je ne veux pas la litière mon code avec des références à Log4net, donc je voudrais écrire une interface simple ILogger, et mettons en œuvre avec quelque chose comme ceci:

public class GenericLogger<T> : ILogger
{
    private readonly ILog log;

    public GenericLogger()
    {
        this.log = log4net.LogManager.GetLogger(typeof(T));
    }

    public void Debug(object message)
    {
        this.log.Debug(message);
    }

    /* .... etc ....  */
}
Était-ce utile?

La solution

La façon la plus simple serait de créer une interface ILogger<T>:

public class ILogger<T> : ILogger { }
public class GenericLogger<T> : ILogger<T> { ... }

Ensuite, compter sur l'inférence de type générique pour obtenir le type correct. Par exemple, dans Ninject, le tout est que vous auriez besoin de liaison suivante:

Bind(typeof(ILogger<>)).To(typeof(GenericLogger<>));

Alors vos types de consommation se présente comme suit:

public class FooService : IFooService {
  public FooService(ILogger<FooService> logger) { ... }
}

Si vous êtes inflexiblement contre l'interface ILogger<T>, vous pourriez faire quelque chose de plus créatif, comme un fournisseur personnalisé, qui lit le IContext pour déterminer le type de parent.

public class GenericLogger : ILogger {
  public class GenericLogger(Type type) { ... }
}

public class LoggerProvider : Provider<ILogger> {
  public override ILogger CreateInstance(IContext context) {
    return new GenericLogger(context.Target.Member.ReflectedType);
  }
}

Alors types consommateurs travailleraient comme ceci:

public class FooService : IFooService {
  public FooService(ILogger logger) { ... }
}

Autres conseils

Si je ne suis pas vous malentendu, pourquoi ne pas tout simplement votre GericLogger Constructor prendre un paramètre qui est le type de l'objet. Ensuite, faites ceci:

ILog = kernel.Get<ILog>(ParameterList);

Je n'ai pas lu complètement encore sur les listes de paramètres ninject, mais il semble être un moyen d'injecter un type de paramètre à l'aide d'un IParameterList.

EDIT:

On dirait que cela fonctionnerait comme ceci:

ILog = kernel.Get<ILog>(new ConstructorArgument[] { 
    new ConstructorArgument("ClassName", this.GetType().Name) 
})

Ensuite, vous avez votre ILog

class GenericLogger : Ilog
{
    GenericLogger(string ClassName) {};
}

Je n'ai pas testé cela, tout ce qui semble être de la source Ninject (je suis à la recherche d'un arbre récent Ninject2)

EDIT:

Vous voulez passer ConstructorArgument, pas de paramètre. Mise à jour pour refléter.

Ce code Prints: "Appelés De Init"

class Init {
    public void Run() {
        StandardKernel kernel = new StandardKernel( new MyLoader() );
        kernel.Get<Tester>( new ConstructorArgument[] { 
            new ConstructorArgument( "ClassName", 
                this.GetType().Name 
            ) 
        } );            
    }
}

public class Tester {
    public Tester(string ClassName) {
        Console.WriteLine("Called From {0}", ClassName);
    }
}

EDIT:

Une autre méthode qui pourrait être utile est d'utiliser le bind (). Pour (). WithConstructorArgument () de liaison, ce qui pourrait être utile lorsqu'il est utilisé soit avec autolimitation ou avec une condition .WhenInjectedInto ().

Pour élaborer l'idée de fournisseur personnalisé, voici un moyen simple de le faire ...

internal class LogModule : StandardModule
    {
        private class log4netILogProvider : SimpleProvider<log4net.ILog>
        {
            protected override ILog CreateInstance(IContext context)
            {
                return LogManager.GetLogger(context.Instance.GetType());
            }
        }

        public override void Load()
        {
            Bind<log4net.ILog>().ToProvider(new log4netILogProvider());
        }
    }

Ensuite, dans les classes qui sont injectées:

[Inject]
        public ILog logger { get; set; }    
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top