DBContextFactory oder Select * from in kompliziert

Man liest Twitter um keine der neuesten Trends zu verpassen. Ideal auch die Interaktion, mit dem Autor. Wobei Antworten ala “Es ermöglicht eine bessere, flexiblere Software Architektur.” aus dem Parteiprogramm von Sonneborn stammen könnten. Besonders interessiert mich alles neue um .NET Core, das nun bald .NET 5 heißen wird. Über .NET 5 habe ich schon 2015 Konferenzvorträge gehalten. Lustig wird das übrigens aus SEO Sicht, wenn man Code für dotnet sucht und sich über fehlende Interfaces wundert.  Das führt ganz wunderbar zum eigentlichen Thema.

Wie viele Design Patterns passen in ein Select * from?

Der von mir geschätzte Jeremy Likness per Tweet “Our official guidance for using #entityframework core #efcore in #AspNetCore #blazor server apps is now available! “ Da wird man natürlich neugierig. Ist das der nächste neue heiße Scheiß?

Die aktuelle Wetterkarte. Grafik: WetterOnline..

Im Kern beschreibt der Artikel, der von sich behauptet Doku zu sein, ein Interface IDbContextFactory im Context von Datenbank Zugriff mit dem Entity Framework. Der Autor, samt seiner ganzen Reviewerschaft stecken noch in der Umgewöhnungsphase oder ich bin blind. Ich würde wetten das ist ein Feature das mit .NET 5 im Herbst final kommt. Nix core und nix 3.1

image

Aber gut: Doku, hergeleitet von Sudoko ist eine japanische Logiksportart in der fehlendes durch den Spieler ergänzt wird. Macht ja Spaß.

Ich versuche also der Anleitung zu folgen und erstelle mit Visual Studio eine neue Blazor Server App. Dazu noch die beiden EF Pakete

image

Na schon aufgefallen? Hat core im Namen und wer sich lange genug mit Softwarenentwicklung beschäftigt, weis: “Namen spielen eine Rolle”

Nur dann- und darüber schweigt die Doku-wenn die Preview von EF 5 installiert wird, ist das Fabric Pattern vorhanden. Dazu später mehr.

Zuerst wird per Gerüstbau in der Nuget Packet Manager Console folgendes Kommando ausgeführt

Scaffold-DbContext "Server=(localdb)\mssqllocaldb;Database=AufgabenDB;Trusted_Connection=True;" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models

 

Der Datenbank name muss- in Österreich sagen wir – “no ned nana “ oder der Wiener auch „na no na ned“ mäßig den Namen der Datenbank austauschen.

Das wars auch schon, so einfach und schnell. Jetzt per new eine DBContext Instanz und eine Tabelle aus dem SQL Server besorgen. Halllllllo Microsoft! SQL SERVER heißt das Produkt aus eurem Hause und nicht MySQL oder SQLite

The sample uses a local SQLite database

Inspiriert vom der Doku schreibe ich sinnfreien C# Code um die Geschwindigkeit zu messen. Ja ich weis, das ist tief unseriös und Corona ist eine Erfindung.

   1:   protected override async Task OnInitializedAsync()
   2:  {
   3:    var t = DateTime.Now.Ticks;
   4:    var ef = new AufgabenDBContext();
   5:   for (int i = 0; i < 10000; i++)
   6:   {
   7:     var daten = ef.Aufgabe.ToArray();
   8:   }
   9:   dauer = DateTime.Now.Ticks - t;
  10:  }

Ja das geht, ist noch immer Blazor und eine .razor Page. Ich verrate so viel, läuft Fehlerfrei und braucht 12617111 Ticks.

Als nächstes werfe ich mich der Design Pattern Community vor die Fuße und nehme den .NET core eigenen Dependency Injection Container. Also ab in die Startup.cs und neuen zusätzlichen Code erzeugt

   1:  public void ConfigureServices(IServiceCollection services)
   2:          {
   3:        ..   
   4:    services.AddDbContext<AufgabenDBContext>();
   5:    

Alternativ könnte man auch versuchen ein bisschen Performance mit erweiterten Connection Pooling hinzubekommen. AddDbContextPool. Wollen wir hier mal nicht vertiefen.

In der Razor Page wird das Objekt injiziert und damit nach Bedarf vom DI Container als Scoped (Pattern) erzeugt.

   1:   @inject AufgabenDBContext ef

Dafür entfällt- tatsächlich – die Zeile mit new (Listing oben Zeile 4). Damit kann ich leben auch wenn in reinen C Sharp Klassen die DI Erzeugung ein Stück mehr Aufwand ist.

Der Tickcounter meldet 12318439, also unwesentlich schneller, was aber eher Zufall sein wird. Überraschend erhöht sich der Wert beim Refresh der Page auf zb 22413104. Mir fehlt dazu aktuell noch eine Erklärung oder auch nur These. Starkes Warnsignal.

Nun gibt es eine kleine aber laute Minderheit, denen ist das alles viel zu einfach. Ein Factory Pattern muss her (was nicht mal die Gang of Four so anerkennt). Im Kern wird ein statisches Objekt genutzt das mit einer Create Methode ein Objekt erzeugt.

In der Doku wird dazu eine Hilfsklasse mit Service Provider erstellt. Die grenzdebilen Comments habe ich weggelassen. Entweder man erkennt in den 10 Zeilen was sie tun, oder Coder sollte umschulen zum Melker.

   1:  public class DbContextFactory<TContext> : 
IDbContextFactory<TContext> where TContext : DbContext
   2:  {
   3:    private readonly IServiceProvider _provider;
   4:    public DbContextFactory(IServiceProvider provider)
   5:         {
   6:              _provider = provider;
   7:          }
   8:    public TContext Create()
   9:     {
  10:      throw new NotImplementedException();
  11:     }
  12:  public TContext CreateDbContext()
  13:    {
  14:    if (_provider == null)
  15:      {
  16:       throw new InvalidOperationException($"You must configure an instance of IServiceProvider");
  17:       }
  18:    return ActivatorUtilities.CreateInstance<TContext>(_provider);
  19:   }
  20:  }

Die Factory wird nun anstatt beim DI Container in startup.cs angemeldet

   1:  services.AddDbContextFactory<AufgabenDBContext>();

In den Options lässt sich dann auch der Scope konfigurieren. Ich habe nicht auf anhieb den Standard gefunden, würde aber auf Singleton tippen für die Fabrik. Die Doku scheibt dazu… nichts.

In der Blazor Komponente (also Page) wird nun passend mit der Spritze und Interface…

   1:  @inject IDbContextFactory<AufgabenDBContext> DbFactory

Die Factory hat nun die Aufgabe Objekte zu produzieren

   1:   using var ef = DbFactory.CreateDbContext();
   2:   for (int i = 0; i < 10000; i++)
   3:    {
   4:     var daten = ef.Aufgabe.ToArray();
   5:     }
   6:   dauer = DateTime.Now.Ticks - t;

Heute haben wir Regen und unter 20°.  Keine neue Hitzewelle in Sicht. Performance? noch langsamer mit 66477399 Ticks bzw runter auf 43880935  nach mehrfachen refresh der Page.

Kommentare sind geschlossen