Usando Ninject (o algún otro recipiente) ¿Cómo puedo averiguar el tipo que está solicitando el servicio?

StackOverflow https://stackoverflow.com/questions/719346

Pregunta

Supongamos que tengo una interfaz para un servicio:

public interface IFooService
{
   void DoSomething();
}

Y una aplicación concreta de ese servicio que es un genérico:

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

Y tengo alguna otra clase que necesita una instancia de IFooService:

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

Necesito que cablear mi contenedor IoC de tal manera que cuando la barra se crea, que se pasa un argumento del constructor de FooService . Hay muchas otras clases asi como bar. Cada uno también podría tener una instancia de FooService pasado a ellos, donde TRequestingClass es el tipo de la clase que necesita la instancia de IFooService. No necesito para exponer esta peculiaridad a los consumidores de IFooService. Lo único que debería importar es que se puede llamar a los métodos de la IFooService que se aprobaron. Ellos no deberían tener que saber que la aplicación concreta de IFooService se pasaron necesita nada especial para ser construido.

Una alternatative aceptable para FooService haría a ser una clase no genérica que tiene un argumento de cadena en su constructur que contiene el nombre de la clase que está siendo creada. es decir:

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

¿Cómo puedo conectar mi contenedor IoC para construir una dependencia de esta manera?

Si está confundido, por eso me gustaría que una estructura tan extraño, considerar cómo log4net funciona mejor cuando se obtiene una iLOG que se crea con log4net.LogManager.GetLogger (typeof (SomeClass)). No quiero ensuciar mi código con referencias a Log4net, así que me gustaría escribir una interfaz de ILogger simple, y ponerlo en práctica con algo como esto:

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 ....  */
}
¿Fue útil?

Solución

La forma más sencilla sería la creación de una interfaz ILogger<T>:

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

A continuación, se basan en la inferencia de tipo genérico para obtener el tipo correcto. Por ejemplo, en Ninject, la continuación de la unión es todo lo que se necesita:

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

A continuación, los tipos de consumidoras se vería así:

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

Si usted es rotundamente en contra de la interfaz ILogger<T>, se podría hacer algo más creativo, como un proveedor personalizado, que lee el IContext para determinar el tipo de padres.

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

A continuación, los tipos que consumen funcionarían como esto:

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

Otros consejos

Si no te estoy malentendido, ¿por qué no simplemente tiene su GericLogger constructor toma un parámetro que es el tipo de objeto. A continuación, haga lo siguiente:

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

No he leído por completo todavía en Listas de parámetros ninject, pero parece ser una manera de inyectar un tipo de parámetro utilizando un IParameterList.

EDIT:

Parece que este sería el siguiente:

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

A continuación, usted tiene su ILOG

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

no he probado esto, sólo lo que parece ser la fuente de Ninject (estoy mirando a un árbol reciente Ninject2)

EDIT:

Se debe pasar ConstructorArgument, no de parámetros. Actualizado para reflejar.

Este código imprime: "Llamado 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:

Otro método que podría ser útil es utilizar el bind (). Para (). WithConstructorArgument () de unión, lo que podría ser útil cuando se utiliza con cualquiera de auto-unión o con una condición .WhenInjectedInto ().

Para más detalles sobre la idea proveedor personalizado, he aquí una forma sencilla de hacerlo ...

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

A continuación, en las clases que se inyectan:

[Inject]
        public ILog logger { get; set; }    
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top