Ich habe kürzlich festgestellt, dass nicht jeder, der mit EF arbeitet, weiß, wie man es kocht. Darüber hinaus sind sie nicht begierig zu verstehen. Probleme in einem sehr frühen Stadium - Aufbau.
Auch nach einer erfolgreichen Konfiguration treten Probleme mit Datenanforderungen auf. Nicht weil die Leute LINQ nicht kennen, sondern weil nicht alles von Objekten auf relationale Modelle abgebildet werden kann. Denn wenn man mit einem Link arbeitet, denkt man in Tabellen. Zeichnen Sie SQL-Abfragen und versuchen Sie, sie in LINQ zu übersetzen.
Dies und vielleicht noch etwas, worüber ich in dem Artikel sprechen möchte.
Anpassung
Eine Person kam zu dem Projekt, sah EF dort zum ersten Mal, staunte über ein solches Wunder, lernte es zu benutzen und beschloss, es für persönliche Zwecke zu benutzen. Erstellt ein neues Projekt, ... Und was tun?
Sie fangen an zu googeln, zu versuchen, zu irren. Um EF einfach mit einem Projekt zu verbinden, ist der Entwickler mit unverständlichen Problemen konfrontiert.
1. Und wie konfiguriere ich es so, dass es mit dem erforderlichen DBMS funktioniert?
2. Und wie konfiguriere ich die Arbeit von Migrationen?
Ich bin sicher, dass es mehr Probleme gibt, aber dies sind wahrscheinlich die häufigsten. Reden wir über alles in Ordnung.
1. Nun, hier ist es zu googeln. :) Ihre Aufgabe ist es, einen EntityFramework Core-Anbieter für Ihr DBMS zu finden.
Anweisungen zur Einrichtung finden Sie auch in der Beschreibung des Anbieters.
Für PostgreSQL müssen Sie beispielsweise das Nuget-Paket Npgsql.EntityFrameworkCore.PostgreSQL installieren
Erstellen Sie Ihren eigenen Kontext, indem Sie DbContextOptions akzeptieren, und erstellen Sie das Ganze so
var opts = new DbContextOptionsBuilder<MyDbContext>()
.UseNpgsql(constring);
var ctx = new MyDbContext(opts.Options);
Wenn Sie eine ASP .NET Core-Anwendung haben, wird der Kontext im Container anders registriert. Das können Sie aber schon in Ihrem Arbeitsprojekt sehen. Oder google.
2. Wenn es in Ihrem Unternehmen keine Kindermädchen gibt, wissen Sie höchstwahrscheinlich, wie Sie die erforderlichen Tools für die Arbeit mit Migrationen installieren. Ob für einen Paketmanager oder eine NET Core CLI.
Was Sie jedoch möglicherweise nicht wissen, ist, dass das ausgewählte Startprojekt (--startup-project) bei der Arbeit mit Migrationen gestartet wird. Dies bedeutet, dass wenn das Startprojekt für Migrationen dasselbe Projekt ist, das Ihre Anwendung startet, und wenn Sie aus irgendeinem Grund Migrationen über ctx.Database.Migrate () ausführen, wenn Sie versuchen, eine zuvor erstellte Migration zu erstellen oder eine andere zu erstellen, dann Die zuletzt erstellte Migration wird auf die Basis übertragen. ÜBERRASCHUNG!
Wenn Sie jedoch versuchen, die erste Migration zu erstellen, werden Sie möglicherweise so etwas feststellen
Für diesen DbContext wurde kein Datenbankanbieter konfiguriert. Ein Anbieter kann durch Überschreiben der DbContext.OnConfiguring-Methode oder durch Verwendung von AddDbContext auf dem Anwendungsdienstanbieter konfiguriert werden. Wenn AddDbContext verwendet wird, stellen Sie außerdem sicher, dass Ihr DbContext-Typ ein DbContextOptions <TContext> -Objekt in seinem Konstruktor akzeptiert und an den Basiskonstruktor für DbContext übergibt.
Dies liegt daran, dass das Tool, das mit Migrationen arbeitet, diese basierend auf Ihrem Kontext erstellt, dafür jedoch eine Instanz benötigt. Um dies bereitzustellen, müssen Sie die IDesignTimeDbContextFactory-Schnittstelle im Starterprojekt implementieren. Dies ist nicht schwierig, es gibt nur eine Methode, die eine Instanz Ihres Kontexts zurückgeben sollte.
Modelle einrichten
Sie haben bereits Ihre erste Migration erstellt, diese ist jedoch aus irgendeinem Grund leer. Obwohl Sie Modelle haben.
Der Punkt ist, dass Sie Ihrem Kontext nicht beigebracht haben, Ihre Modelle in Ihre Datenbank zu übersetzen.
Damit EF eine Migration erstellen kann, um eine Tabelle für Ihr Modell in der Datenbank zu erstellen, müssen Sie in Ihrem Kontext mindestens eine Eigenschaft vom Typ DbSet <MyEntity> erstellen.
Beispiel: Durch diese Eigenschaft
public DbSet <MyEntity> MyEntities {get; einstellen; }
Die MyEntities-Tabelle wird mit Feldern erstellt, die den Eigenschaften der MyEntity-Entität entsprechen.
Wenn Sie kein DbSet haben oder die Standardregeln für die Tabellenerstellung für eine Entität beeinflussen möchten, müssen
protected override void OnModelCreating(ModelBuilder modelBuilder)
Sie die Methode Ihres Kontexts überschreiben .
Wie das geht, wissen Sie höchstwahrscheinlich. Darüber hinaus sind Sie wahrscheinlich mit den Attributen vertraut, mit denen Sie die Zuordnungsregeln steuern können. Aber hier ist die Sache. Attribute geben keine vollständige Kontrolle über die Zuordnung, was bedeutet, dass Sie in OnModelCreating Verbesserungen vornehmen müssen. Das heißt, Sie haben Regeln für die Zuordnung von Entitäten sowohl in Form von Anmerkungen als auch in Form einer fließenden API. Suchen Sie dann, warum das Feld den falschen Namen hat, den Sie erwartet haben, oder die falschen Einschränkungen.
- Nun, dann ist alles einfach, - Sie sagen - ich werde alles über OnModelCreating konfigurieren.
Nachdem Sie die 20. Entität aus 10 Feldern eingerichtet haben, fangen Ihre Augen an, sich zu kräuseln. Sie versuchen, die Feldeinstellung für eine Entität zu finden, aber alles schwebt vor Ihren Augen aus einem einheitlichen Block mit einer Länge von 200 bis 500 Zeilen.
Ja, Sie können diesen Block in 20 Methoden aufteilen und es wird ein wenig einfacher. Es ist jedoch hilfreich zu wissen, dass es eine IEntityTypeConfiguration <TEntity> -Schnittstelle gibt, indem Sie implementieren, welche für eine bestimmte Entität Sie in dieser Implementierung die Zuordnungsregeln für eine bestimmte Entität beschreiben können. Nun, damit der Kontext ihn aufnimmt, müssen Sie in OnModelCreating
modelBuilder.ApplyConfigurationsFromAssembly (AssemblyWithConfigurations) schreiben .
Und natürlich ist es möglich, das allgemeine Verhalten auf dasselbe OnModelCreating zu übertragen. Wenn Sie beispielsweise eine allgemeine Regel für Bezeichner aller oder vieler Entitäten haben, können Sie diese wie folgt konfigurieren
foreach (var idEntity in modelBuilder.Model.GetEntityTypes()
.Where(x => typeof(BaseIdEntity).IsAssignableFrom(x.ClrType))
.Select(x => modelBuilder.Entity(x.ClrType)))
{
idEntity.HasKey(nameof(BaseIdEntity.Id));
}
Abfragen erstellen
Nun, wir haben die Dinge in Ordnung gebracht, es wurde ein bisschen angenehmer.
Jetzt müssen die Abfragen unseres Heimprojekts von SQL nach Linq wiederholt werden. Ich habe bereits Anfragen bei der Arbeit geschrieben, es ist so einfach wie das
Schälen von Birnen ctx.MyEntities.Where (Bedingung) .Select (Karte) .GroupBy (Ausdruck) .OrderBy (Ausdruck) ... Leichtigkeit.
Also, was ist unsere Anfrage?
SELECT bla bla bla FROM table
RIGHT JOIN.......
Ja
ctx.LeftEntities.RightJoin(.... f@#$
Ich habe Linq und seine Erweiterungsmethoden verwendet, lange bevor ich EF kennengelernt habe. Und ich hatte nie eine Frage, aber wo ist RIGHT JOIN, LEFT JOIN darin ...
Was ist LEFT JOIN in Bezug auf Objekte?
Das
class LeftEntity
{
public List<RightEntity> RightEntities { get; set; }
}
Es ist nicht einmal Magie. Wir haben nur eine bestimmte Entität, die sich auf eine Liste anderer Entitäten bezieht, aber möglicherweise keine.
Dies ist das
ctx.LeftEntities.Include(x => x.RightEntities)
Was ist RIGHT JOIN? Dies ist ein invertierter LEFT JOIN, was bedeutet, dass wir nur mit einer anderen Entität beginnen.
Aber alles passiert, manchmal müssen Sie jedes Bundle als separate Entität kontrollieren, auch wenn die linke Entität keiner rechten (rechten NULL) zugeordnet ist. Daher kann explizit LEFT JOIN so ausgeführt werden
ctx.LeftEntities.SelectMany(x => x.RightEntities.DefaultIfEmpty(), (l, r) => new { Left = l, Right = r })
Dies gibt uns die Kontrolle über die Bindung, wie im relationalen Modell. Warum brauchst du es vielleicht? Angenommen, ein Kunde muss Daten in Form einer Tabelle anzeigen und sortieren und / oder paginieren.
Ich mag diese Idee auch nicht, aber jeder hat seine eigenen Macken und endlose Liebe in Excel. Decken wir also die Matte mit einem Husten ab, fluchen zu einer Faust und arbeiten weiter. Was kommt als nächstes für uns?
FULL JOIN
Nun, ich habe bereits verstanden, wir denken nicht mit SQL, wir denken mit Objekten wie diesem und jetzt so ... verdammt noch mal. Wie kann man das darstellen?
Nirgendwo ohne Tupel.
Wenn wir mit Tupeln arbeiten müssen, verlieren wir im Allgemeinen das Verständnis für die Art der Verbindung (1-1, 1-n, nn) und im Allgemeinen können wir theoretisch Fliegen und Elefanten kreuzen. Das ist schwarze Magie.
Machen wir das!
Nehmen wir viele zu viele als Grundlage.
Wir haben also 3 Arten von Entitäten
LeftEntity
RightEntity
LeftRight (zum Organisieren der Verbindung)
LeftRight Ich werde mit einem zusammengesetzten Schlüssel erstellen. Ich benötige keinen direkten Zugriff auf diese Entität, externe Verweise darauf sind nicht erforderlich, sodass keine unnötigen Felder und Indizes erstellt werden.
Als Ergebnis der Abfrage möchte ich eine Reihe von Tupeln erhalten, die die linke und die rechte Entität enthalten. Wenn die jungfräuliche Entität nichts mit der richtigen zu tun hat, ist das richtige Objekt null. Gleiches gilt für die rechten Einheiten.
Es stellt sich heraus, dass LeftRight nicht als Ausgabe für uns geeignet ist. Aufgrund seiner Einschränkungen können wir kein Bundle ohne eine der Entitäten erstellen. Wenn Sie DDD üben, werden Sie Inkonsistenzen feststellen.
Auch wenn Sie nicht üben, sollten Sie nicht. Erstellen wir einen neuen Typ für die
LeftRightFull- Ausgabe , der noch einen Verweis auf die linken und rechten Entitäten enthält.
Wir haben also
links
L1
L2
rechts
R1
R2
links rechts
L2 R2
Wir wollen am Ausgang
L1 n
L2 R2
n R1
Beginnen wir mit der linken Verbindung
var query = ctx.LeftEntities
.SelectMany(x => x.RightLinks.DefaultIfEmpty(), (l, lr) => new LeftRightFull
{
LeftEntity = l,
RightEntity = lr.RightEntity
})
Jetzt haben wir bereits
L1 n
L2 R2
Was kommt als nächstes? Stick RightEntity über SelectMany? Nun, so erhalten wir
L1 n R1 n
L1 n R2 L2
L2 R2 R1 n
L2 R2 R2 L2
Wir können unnötige (L2 R2 R1 n und L1 n R2 L2) nach Bedingungen herausfiltern, es bleibt jedoch unklar, wie L1 n R1 n transformiert werden soll in
L1 n
n R1
Vielleicht haben uns die Ausgrabungen in die falsche Steppe geführt. Verbinden wir diese ähnlichen Abfragen einfach mit linken Verknüpfungen für linke und rechte Entitäten über Union.
L1 n
L2 R2
UNION
L2 R2
n R1
Hier haben Sie möglicherweise Fragen zur Leistung. Ich werde sie nicht beantworten, da alles vom spezifischen DBMS und der Sorgfalt seines Optimierers abhängt.
Alternativ können Sie eine Ansicht mit Ihrer gewünschten Abfrage erstellen. Union funktioniert nicht mit EF Core 2, daher musste ich eine Ansicht erstellen. Ich empfehle jedoch, es nicht zu vergessen. Plötzlich entscheiden Sie sich, das weiche Löschen von Entitäten über das IsDeleted-Flag zu implementieren. In der Ansicht müssen Sie dies unterstützen. Wenn Sie vergessen, ist nicht bekannt, wann Sie einen Fehlerbericht erhalten.
Übrigens, wie kann man ein weiches Löschen implementieren, ohne den gesamten Code neu zu schreiben? Ich werde das nächste Mal darüber sprechen. Vielleicht etwas anderes.
Auf Wiedersehen an alle.