Supongamos que tengo el Service
que recibe dependencias a través del constructor pero también necesita inicializarse con datos personalizados (contexto) antes de que pueda usarse:
public interface IService
{
void Initialize(Context context);
void DoSomething();
void DoOtherThing();
}
public class Service : IService
{
private readonly object dependency1;
private readonly object dependency2;
private readonly object dependency3;
public Service(
object dependency1,
object dependency2,
object dependency3)
{
this.dependency1 = dependency1 ?? throw new ArgumentNullException(nameof(dependency1));
this.dependency2 = dependency2 ?? throw new ArgumentNullException(nameof(dependency2));
this.dependency3 = dependency3 ?? throw new ArgumentNullException(nameof(dependency3));
}
public void Initialize(Context context)
{
// Initialize state based on context
// Heavy, long running operation
}
public void DoSomething()
{
// ...
}
public void DoOtherThing()
{
// ...
}
}
public class Context
{
public int Value1;
public string Value2;
public string Value3;
}
Ahora, los datos de contexto no se conocen de antemano, por lo que no puedo registrarlos como una dependencia y usar DI para inyectarlos en el servicio
Así es como se ve el cliente de ejemplo:
public class Client
{
private readonly IService service;
public Client(IService service)
{
this.service = service ?? throw new ArgumentNullException(nameof(service));
}
public void OnStartup()
{
service.Initialize(new Context
{
Value1 = 123,
Value2 = "my data",
Value3 = "abcd"
});
}
public void Execute()
{
service.DoSomething();
service.DoOtherThing();
}
}
Como puede ver, hay un acoplamiento temporal e inicialización de olores de código de método involucrados, porque primero necesito llamar service.Initialize
para poder llamar service.DoSomething
y service.DoOtherThing
luego.
¿Cuáles son los otros enfoques en los que puedo eliminar estos problemas?
Aclaración adicional del comportamiento:
Cada instancia del cliente debe tener su propia instancia del servicio inicializada con datos de contexto específicos del cliente. Por lo tanto, los datos de contexto no son estáticos ni se conocen de antemano, por lo que DI no puede inyectarlos en el constructor.
Service
tiene dependencias distintas de laContext
que no se proporcionarían por elClient
, se pueden proporcionar a través de DI alServiceFactory
que se pasaráService
cuandocreateService
se llame.ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1);
y quedarte con tu servicio parcialmente configurado, luego más tardeService s = partial.context(context).build()
El
Initialize
método debe eliminarse de laIService
interfaz, ya que este es un detalle de implementación. En su lugar, defina otra clase que tome la instancia concreta de Servicio y llame al método de inicialización. Entonces esta nueva clase implementa la interfaz IService:Esto mantiene el código del cliente ignorante del procedimiento de inicialización, excepto donde
ContextDependentService
se inicializa la clase. Al menos limita las partes de su aplicación que necesitan saber sobre este procedimiento de inicialización inestable.fuente
Me parece que tienes dos opciones aquí
p.ej.
p.ej.
Inyectar una fábrica está bien si solo desea evitar pasar el contexto como parámetro. Digamos que solo esta implementación particular necesita un contexto y no desea agregarlo a la interfaz
Pero esencialmente tiene el mismo problema, ¿qué pasa si la fábrica aún no tiene un contexto inicializado?
fuente
No debe depender de su interfaz con ningún contexto db ni método de inicialización. Puedes hacerlo en constructor de clase concreto.
Y, una respuesta a su pregunta principal sería Inyección de propiedades .
De esta manera, puede llamar a todas las dependencias por inyección de propiedad . Pero podría ser un gran número. Si es así, puede usar la inyección de constructor para ellos, pero puede establecer su contexto por propiedad comprobando si es nulo.
fuente
Misko Hevery tiene una publicación de blog muy útil sobre el caso que has enfrentado. Ambos necesitan nuevos e inyectables para su
Service
clase y esta publicación de blog puede ayudarlos.fuente