Frage

Angenommen, ich habe eine Schnittstelle für einen Dienst:

public interface IFooService
{
   void DoSomething();
}

Und eine konkrete Umsetzung dieser Dienst, ist ein allgemeiner:

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

Und ich habe eine andere Klasse, die eine Instanz von IFooService benötigt:

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

Ich brauche meine IoC-Container verkabeln, dass, wenn Bar erstellt wird, es ein Konstruktor Argument von FooService übergeben wird. Es gibt viele andere Klassen wie Bar. Jeder könnte auch eine Instanz von FooService brauchen an sie übergeben, wo TRequestingClass der Typ der Klasse, die die Instanz von IFooService benötigt. Ich brauche nicht diese Schrulligkeit an die Verbraucher von IFooService auszusetzen. Alles, was sie über sollten Pflege ist, dass sie die Methoden des IFooService nennen können sie übergeben wurden. Sie sollen nicht wissen müssen, dass die konkrete Umsetzung von IFooService sie besondere aufgebaut werden mußte etwas übergeben wurden.

Ein akzeptabler alternatative zu FooService würde eine nicht-generische Klasse sein, die ein String-Argument in seiner Constructur hat, der den Namen der Klasse enthält es erstellt wird. das heißt:

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

Wie kann ich meine IoC-Container verdrahten, um eine Abhängigkeit auf diese Weise zu bauen?

Wenn Sie sind verwirrt, warum ich so eine seltsame Struktur wollen würde, überlegen, wie log4net funktioniert am besten, wenn Sie einen ILog erhalten, die mit log4net.LogManager.GetLogger erstellt wird (typeof (Someclass)). Ich will nicht zu Wurf meinen Code mit Verweisen auf log4net, so möchte ich eine einfache ILogger Schnittstelle zu schreiben, und setzen es mit so etwas wie folgt aus:

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 ....  */
}
War es hilfreich?

Lösung

Der einfachste Weg wäre, eine ILogger<T> Schnittstelle zu erstellen:

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

verlassen dann auf generische Typinferenz den richtigen Typ zu erhalten. Zum Beispiel in Ninject ist die folgende Bindung alles, was Sie brauchen würden:

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

Dann wird Ihr raubend Typ würde wie folgt aussehen:

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

Wenn Sie unerbittlich gegen die ILogger<T> Schnittstelle sind, könnten Sie etwas kreativer, wie ein benutzerdefinierten Anbieter tun, die die IContext liest den Muttertyp zu bestimmen.

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);
  }
}

Dann raubend Typen würden wie folgt funktionieren:

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

Andere Tipps

Wenn ich dich nicht bin Missverständnis, warum nicht einfach haben Ihre GericLogger Constructor einen Parameter übernehmen, die die Art des Objekts ist. Dann tun Sie dies:

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

Ich habe noch nicht ganz auf Ninject Parameterlisten, aber es scheint ein Weg zu sein, einen Parametertyp unter Verwendung eines IParameterList zu injizieren.

EDIT:

Sieht aus wie dies wie dies funktionieren würde:

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

Dann haben Sie Ihre ILog

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

Ich habe das nicht testen, genau das, was von der Ninject Quelle zu sein scheint (ich kürzlich bei einem Ninject2 Baum suchen)

EDIT:

Sie wollen ConstructorArgument passieren, nicht Parameter. Aktualisiert widerzuspiegeln.

Dieser Code druckt: "Called Von 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:

Eine andere Methode, die nützlich sein könnte, ist die Bind () zu verwenden. Um (). WithConstructorArgument () Bindung, die, wenn sie entweder mit Selbstbindung oder mit einer .WhenInjectedInto () Zustand verwendet nützlich sein könnte.

auf der benutzerdefinierten Anbieter Idee zu erarbeiten, hier ist eine einfache Möglichkeit, es zu tun ...

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());
        }
    }

Dann in Klassen, die injiziert werden:

[Inject]
        public ILog logger { get; set; }    
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top