# Lifecycle-Management für DbContext-Instanzen - 2 (30.10.2024) ## *Ambient* DbContext, *Explicit* DbContext oder *Injected* DbContext Eine der ersten Entscheidungen die beim Start eines neuen EF Core Projekts gefällt werden muss ist die Vorgehensweise beim Übergeben der DbContext-Instanz zu den Methoden die den tatsächlichen Aufruf tätigen. ### Explicit DbContext Mit der expliziten Vorgehensweise wird in der Top-Level Service Methode eine DbContext-Instanz erstellt und anschliessend and die verwendeten Methoden als Parameter übergeben. In einer traditionellen 3-Tier Architektur könnte das folgendermassen aussehen: *ExampleRepository.cs* ```CSharp public class ExampleRepository : IExampleRepository { public Example Get(ExampleDbContext context, int id) { return context.Set<Example>().Find(id); } } ``` *ExampleService.cs* ```CSharp public class ExampleService(IExampleRepository exampleRepository) : IExampleService { public void ClearExampleText(int id) { using (var context = new ExampleDbContext) { var example = exampleRepository.Get(context, id); example.Text = string.Empty(); context.SaveChanges(); } } } ``` #### Vorteile Diese Vorgehensweise ist mit Abstand die Einfachste. Es gibt einem verständlichen Code der auch einfach zu warten ist. Es passiert keine Magie, man weiss immer von wo die Instanz herkommt und ab wann sie nicht mehr existiert. #### Nachteile Der grösste Nachteil ist, dass alle Repository wie auch die meisten Service Methoden mit DbContext Parametern verunreinigt werden. Dieses Problem wird vor allem dann ersichtlich, wenn mehrere DbContext Typen genutzt werden. Mit jedem Typ kommt ein weiterer Parameter dazu, was zu sehr unleserlichem Code wird. ### Ambient DbContext Mit dieser Vorgehensweise erstellt die Top-Level Service Methode nicht nur die Instanz für die aktuelle Geschäftstransaktion, sondern registriert diese auch gleich als eine umgebende Instanz. Diese kann bei Bedarf einfach abgerufen werden. DbContext Instanzen müssen so nicht mehr sinnlos herumgegeben werden. Für diese Vorgehensweise gibt es verschiedene Umsetzungen. Eine einfache Ambient DbContext Implementation kann beispielsweise in einem Blogpost[^2] von Anders Abel gefunden werden. #### Vorteile Die Vorteile sind offensichtlich: Die Service und Repository Methoden sind so frei von DbContext Parametern. Wie auch bei der expliziten Vorgehensweise ist die Erstellung und Entsorgung der Instanz klar ersichtlich und verständlich. #### Nachteile Diese Vorgehensweise arbeitet bereits mit einer gewissen Menge an Magie. Wenn nur der *Data Access* Code angeschaut wird, ist es nicht garantiert einfach die Quelle der DbContext-Instanz zu identifizieren. Man muss einfach hoffen, dass diese vorher bereits irgendwo registriert wurde. Mit der obig verlinkten Implementation ist ausserdem keine asynchrone Nutzung möglich. Es ist möglich den Code dafür anzupassen, verlangt jedoch tiefes Verständnis der inneren Machenschaften von EF Core. ### Injected DbContext Die meist verwendete Vorgehensweise für die Verwaltung von DbContext-Instanzen ist über DI (*Dependency Injection*). Eine einfache Umsetzung könnte folgendermassen aussehen: *ExampleRepository.cs* ```CSharp public class ExampleRepository(ExampleDbContext context) : IExampleRepository { public Example Get(int id) { return context.Set<Example>().Find(id); } } ``` *ExampleService.cs* ```CSharp public class ExampleService(IExampleRepository exampleRepository) : IExampleService { public void ClearExampleText(int id) { var example = exampleRepository.Get(id); example.Text = string.Empty(); } } ``` #### Vorteile Die Vorteile sind ziemlich ähnlich wie bei der Ambient Vorgehensweise und geht sogar noch einen Schritt weiter: Es gibt keine DbContext-Instanzen im Service Code. Das kann auf den ersten Blick zwar sehr gut aussehen, führt aber schnell zu vielen Problemen. #### Nachteile Trotz der Berühmtheit dieser Vorgehensweise kommt sie mit vielen Limitationen und Problemen: ##### Sehr viel Magie Mit DI kommt immer viel Magie. Man weiss nie wo diese Instanzen herkommen, wo und wie die Grenzen der Geschäftstransaktion definiert sind, welche Instanzen geteilt werden und wo neue Instanzen erstellt werden... Die Antworten auf diese Fragen sind nicht immer offensichtlich und können viel Zeit mit Lesen und Überarbeiten der eigenen Konfiguration in Anspruch nehmen. ##### Unklare Grenzen bei Geschäftstransaktionen Ein weiteres sehr grosses Problem ist die Unklarheit bei den Abgrenzungen der Geschäftstransaktionen. Es ist beispielsweise nicht klar, wo die `SaveChange()` Methode aufgerufen wird. Man könnte den DbContext in die Service Klasse injizieren um die Methode aufzurufen. Dies führt jedoch schnell zu Fehlerhaftem Code: Warum sollte die Service Klasse `SaveChanges()` auf einen Context, welcher weder erstellt noch genutzt wird, aufrufen. Alternativ könnte auch eine `SaveChanges()` Methode im Repository implementiert werden welche auf die Methode des DbContext delegiert. Dies könnte sehr irreführenden Code verursachen, da es impliziert, dass jedes Repository ein eigenes Unit of Work hat und die Änderungen unabhängig von einander geschehen. Eine solche Annahme wäre falsch, da im Hintergrund die gleichen DbContext-Instanzen verwendet werden[^1] ## Fazit Es gibt viele verschiedene Möglichkeiten um DbContext-Instanzen in einer Applikation zu verwalten. Alle kommen mit ihren eigenen Vorteilen und Nachteilen, es ist also wichtig, seine Bedürfnisse abzuwägen. Wie bereits erwähnt gibt es keine einzig wahre Option. Es ist jedoch wichtig zu erwähnen, dass die gewählte Möglichkeit einheitlich umgesetzt wird, da mit Mischungen viele neue Probleme aufkommen. Es gibt auch noch eine weitere Möglichkeit welche einigermassen bekannt ist: der DbContextScope von Mehdi El Gueddari. Seine Lösung adressiert viele der Problemen mit den traditionelleren Vorgehensweisen, die wir uns zuvor angeschaut haben. Wenn euch diese interessiert, kann ich euch seinen Blog[^1] zutiefst empfehlen. [^1]: vgl. Managing DbContext the right way with Entity Framework 6: an in-depth guide, in: mehdi.com, 30.10.2024, https://mehdi.me/ambient-dbcontext-in-ef6/ [^2]: vgl. Make the DbContext Ambient with UnitOfWorkScope, in: PASSION FOR CODING, 30.10.2024, https://coding.abel.nu/2012/10/make-the-dbcontext-ambient-with-unitofworkscope/?ref=mehdi.me