Laden...

Erstellung von Instanzen mit geteilten Abhängigkeiten

Erstellt von t0ms3n vor 7 Jahren Letzter Beitrag vor 7 Jahren 1.927 Views
T
t0ms3n Themenstarter:in
314 Beiträge seit 2013
vor 7 Jahren
Erstellung von Instanzen mit geteilten Abhängigkeiten

Hallo zusammen,

ich sitze gerade gedanklich vor dem Problem, wie die korrekte Injizierung von Abhängigkeiten aussieht, wenn diese Abhängigkeiten wiederum Abhängigkeiten teilen sollen. Das Stichwort hier ist sicherlich der Scope, aber dessen Verwendung ist mir nicht klar. Konkret geht es um die gemeinsame Nutzung einer Unit und somit auch die gemeinsame Verwendung von Transaktionen.

Als Beispiel könnten folgende Klassen dienen:


 class MyUnitOfWork
{
    public Guid Id { get; set; }

    public MyUnitOfWork()
    {
        Id = Guid.NewGuid();
    }
}

public class MyService1
{
    private readonly MyUnitOfWork _myUnitOfWork;

    public MyService1(MyUnitOfWork myUnitOfWork)
    {
        _myUnitOfWork = myUnitOfWork;
    }
}

public class MyService2
{
    private readonly MyUnitOfWork _myUnitOfWork;

    public MyService2(MyUnitOfWork myUnitOfWork)
    {
        _myUnitOfWork = myUnitOfWork;
    }
}

public class MyService3
{
    private readonly MyService1 _service1;
    private readonly MyService2 _service2;

    public MyService3(MyService1 service1, MyService2 service2)
    {
        _service1 = service1;
        _service2 = service2;
    }
}

Eine Verwendung ohne Container etc. könnte also so aussehen:


var instance1 = new MyService1(new MyUnitOfWork());
var instance2 = new MyService2(new MyUnitOfWork());

var sharedUnit = new MyUnitOfWork();
var instance3 = new MyService3(new MyService1(sharedUnit),  new MyService2(sharedUnit));

Allerdings muss hier der Aufrufende wissen, dass dies so sein muss, was letztlich auch nicht gewollt ist. Wie verbirgt MyService3 also, dass die beiden Abhängigkeiten eine geteilte Instanz von MyUnitOfWork haben sollen?

Edit: Titel von Verwendung von Scopes mit DI geändert. Denke dies ist ohne Bezug zu Container etc. erstmal treffender

16.807 Beiträge seit 2008
vor 7 Jahren

Ja, diesen Aufwand hat mal halt, wenn man keinen Container hat.

W
955 Beiträge seit 2010
vor 7 Jahren

MyService3 müsste ein Uow injiziert bekommen und diesen an den beiden Subservices weiterreichen. Besser wäre Abts Vorschlag mit DIC und geeigneten Lifestyles der injizierten Objekte.

T
t0ms3n Themenstarter:in
314 Beiträge seit 2013
vor 7 Jahren

Hmmm, also doch mit Container als Beispiel. (im Beispiel genutzt SimpleInjector)


var container = new Container();
// Es ginge zwar auch über eine Hybrid LifeTime, aber wie dann der Service3 zu injecten ist, konnte ich noch nicht raus finden. Dafür ist ja ein entsprechendes container.BeginExecutionContextScope() notwendig.
//container.Options.DefaultScopedLifestyle = new ExecutionContextScopeLifestyle();
//container.Register<MyUnitOfWork>(Lifestyle.CreateHybrid(() => container.GetCurrentExecutionContextScope() != null, Lifestyle.Scoped, Lifestyle.Transient));

container.Register<MyUnitOfWork>();
container.Register<MyService1>();
container.Register<MyService2>();
container.Register<MyService3>( () => MyService3Factory.CreateNew(container.GetInstance<MyUnitOfWork>()));

var instance1 = container.GetInstance<MyService1>();
var instance2 = container.GetInstance<MyService2>();
var instance3 = container.GetInstance<MyService3>();

Zusätzlich noch die Factory für MyService3.


public static class MyService3Factory
{
    public static MyService3 CreateNew(MyUnitOfWork unitOfWork)
    {
        return  new MyService3(new MyService1(unitOfWork), new MyService2(unitOfWork));
    }
}

F
10.010 Beiträge seit 2004
vor 7 Jahren

Wozu meinst du überhaupt die Factory zu brauchen?

16.807 Beiträge seit 2008
vor 7 Jahren

Er will sicher gehen, dass der Aufrufer, der keinen Container nutzt, für beide Services (1 und 2) die identische UoW Instanz übergibt.

Ich stelle mal in den Raum, dass es ein Fehldesign darstellt, wenn eine Transaktion nur dann gültig ist, wenn zwei Services diese füllen.
Das entspricht nicht ganz dem Sinn eines Service-Layers.

T
t0ms3n Themenstarter:in
314 Beiträge seit 2013
vor 7 Jahren

Das schließe ich nicht aus. Dann etwas mehr zur tatsächlichen Problemstellung die mich zu der Frage führte:

Ich habe in einer bestehender Implementierung einen Service ItemHistoryCollector. Dieser hat die Aufgabe Historieneinträge zu sammeln und zu speichern. Zusätzlich gibt es nun also einen weiteren Service welcher einen beliebigen Prozess abbildet z.B. Anlegen eines Auftrags. Im Rahmen dieser Prozesse sollen eben auch entsprechend Historieneinträge erzeugt werden, dazu würde der ItemHistoryCollector verwendet werden. Tatsächlich gespeichert werden sollen diese aber nur, wenn die Gesamtaktion erfolgreich war.

In der Theorie also: Beide nutzen die gleiche Datenbankverbindung, der Service zur Prozessabbildung beginnt eine Transaktion und Comitted diese eben acuh wieder.

W
955 Beiträge seit 2010
vor 7 Jahren

Könnte man auch so lösen dass der BL-Service wenn er erfolgreich seine Aufgabe erledigt hat (Autrag angelegt usw) ein History-Objekt daraus erzeugt und dieses in ein Eventsystem/Message Bus o.ä. an den History-Service übergibt und dieser das dann extra wegspeichert. Dann fällt diese extreme Kopplung der Services weg.