Angenommen, ich habe die Service
, die Abhängigkeiten über den Konstruktor empfängt, aber auch mit benutzerdefinierten Daten (Kontext) initialisiert werden muss, bevor sie verwendet werden können:
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;
}
Jetzt - die Kontextdaten sind nicht im Voraus bekannt, daher kann ich sie nicht als Abhängigkeit registrieren und DI verwenden, um sie in den Dienst einzufügen
So sieht ein Beispielclient aus:
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();
}
}
Wie Sie sehen können, handelt es sich um zeitliche Kopplungs- und Initialisierungsgerüche von Methodencode, da ich zuerst aufrufen muss, um aufrufen service.Initialize
zu können, service.DoSomething
und service.DoOtherThing
danach.
Was sind die anderen Ansätze, mit denen ich diese Probleme beseitigen kann?
Zusätzliche Klarstellung des Verhaltens:
Jede Instanz des Clients muss über eine eigene Instanz des Dienstes verfügen, die mit den spezifischen Kontextdaten des Clients initialisiert wurde. Diese Kontextdaten sind also nicht statisch oder im Voraus bekannt, sodass sie nicht von DI in den Konstruktor eingefügt werden können.
Service
andere Abhängigkeiten als die vorhandenContext
sind, die von der nicht bereitgestellt würdenClient
, können sie über DI an die bereitgestelltServiceFactory
werden, die an denService
Zeitpunkt übergeben werden sollen, an demcreateService
aufgerufen wird.ServiceBuilder partial = new ServiceBuilder().dependency1(dependency1_1).dependency2(dependency2_1).dependency3(dependency3_1);
und mit Ihrem teilweise eingerichteten Dienst zurückbleiben, dann späterService s = partial.context(context).build()
Die
Initialize
Methode sollte von derIService
Schnittstelle entfernt werden, da dies ein Implementierungsdetail ist. Definieren Sie stattdessen eine andere Klasse, die die konkrete Instanz von Service verwendet und die Initialisierungsmethode aufruft. Dann implementiert diese neue Klasse die IService-Schnittstelle:Dadurch wird der Clientcode über die Initialisierungsprozedur nicht informiert, es sei denn, die
ContextDependentService
Klasse wird initialisiert. Sie beschränken zumindest die Teile Ihrer Anwendung, die über dieses Wonky-Initialisierungsverfahren Bescheid wissen müssen.quelle
Es scheint mir, dass Sie hier zwei Möglichkeiten haben
z.B.
z.B.
Das Injizieren einer Factory ist in Ordnung, wenn Sie nur vermeiden möchten, den Kontext als Parameter zu übergeben. Angenommen, nur diese bestimmte Implementierung benötigt einen Kontext und Sie möchten ihn nicht zur Schnittstelle hinzufügen
Aber Sie haben im Wesentlichen das gleiche Problem: Was ist, wenn die Fabrik noch keinen initialisierten Kontext hat?
quelle
Sie sollten Ihre Schnittstelle nicht von einem Datenbankkontext und einer Initialisierungsmethode abhängig machen. Sie können dies im konkreten Klassenkonstruktor tun.
Eine Antwort auf Ihre Hauptfrage wäre Property Injection .
Auf diese Weise können Sie alle Abhängigkeiten per Eigenschaftsinjektion aufrufen . Aber es könnte eine riesige Zahl sein. In diesem Fall können Sie Constructor Injection für sie verwenden, aber Sie können Ihren Kontext anhand der Eigenschaft festlegen, indem Sie überprüfen, ob er null ist.
quelle
Misko Hevery hat einen sehr hilfreichen Blog-Beitrag über den Fall, mit dem Sie konfrontiert sind. Sie benötigen beide neue und injizierbare Elemente für Ihre
Service
Klasse, und dieser Blog-Beitrag kann Ihnen helfen.quelle