Laden...

Entity Framework - Überprüfen ob Datensatz in der Datenbank schon vorhanden ist

Erstellt von Jacyrio vor 8 Jahren Letzter Beitrag vor 8 Jahren 5.742 Views
J
Jacyrio Themenstarter:in
197 Beiträge seit 2006
vor 8 Jahren
Entity Framework - Überprüfen ob Datensatz in der Datenbank schon vorhanden ist

Guten Morgen,

ich habe eine Frage zur Verwendung von Entity Framework bzw. folgende Problemstellung:

Ich bekomme häufiger in der Woche eine XML-Datei gesendet, die immer die selben Daten hat und um ein paar weitere Datensätze erweitert wurde.

Diese XML-Datei lese ich aus und schreibe sie in eine Liste mit Objekten und will die Liste mit den Objekten über die AddRange-Funktion in der Datenbank speichern.

Nun wird erkannt, dass die ersten Datensätze in der DB bereits existieren und es gibt einen Primary-Key-Fehler.

Mir würde es ausreichen, wenn das Programm diese Fehlermeldung einfach ignoriert. Ist das mit EF möglich? Oder muss ich schon beim Erstellen der Liste überprüfen ob die Datensätze in der DB vorhanden sind und diese gar nicht erst in die Liste übertragen? Wären halt immer wieder eine Menge DB-Zugriffe, die ich mit einem "ignorieren" des Fehlers minimieren könnte.

16.806 Beiträge seit 2008
vor 8 Jahren

NoSQL-Datenbanken können das (nennt sich Upsert), SQL-Datenbanken i.d.R. nicht.
Hier musst Du vorher prüfen, ob er Eintrag existiert und wenn ja statt Insert eben nen Update-Befehl fahren.

K
60 Beiträge seit 2014
vor 8 Jahren

ich wollte gerade eine ähnliche Frage stellen.
Nun, mit EF 6 gab es noch AddOrUpdate, EF7 bietet das nicht mehr an.

Ähnlich wie der Threadstarter habe ich eine Tabelle, die folgend aufgebaut ist:


Id (Primärschlüssel)
Wert
Datum (Index)
PersonalNr (Index)

Ich möchte, wenn der Wert aus einer List<Entity> bereits vorhanden ist, geupdatet wird, ansonsten soll der hinzugefügt werden.

Man könnte jetzt durch iterieren und jeden Wert fragen, ob der nicht bereits vorhanden ist, aber bei ~500 Datensätzen geht die Perfomance flöten.

Wie handhabt ihr das?

T
314 Beiträge seit 2013
vor 8 Jahren

Im einfachsten Fall weißt Du ja anhand der Id, ob ein Eintrag vorhanden ist oder nicht.

K
60 Beiträge seit 2014
vor 8 Jahren

Im einfachsten Fall weißt Du ja anhand der Id, ob ein Eintrag vorhanden ist oder nicht.

Sorry, habe ich vergessen zu erwähnen: Die Id ist autoinkrement und wird automatisch gesetzt.
In meiner Liste habe ich die Id nicht.

T
314 Beiträge seit 2013
vor 8 Jahren

Wie füllst Du denn deine Liste? Warum rufst Du die Id nicht mit ab?

K
60 Beiträge seit 2014
vor 8 Jahren

Ich will doch etwas hinzufügen, nicht etwas abrufen.
Die Id will/kann und soll ich doch nicht manuell setzen.

das sieht dann ungefähr so aus:


DateTime a = new DateTime(2016, 01, 01);
var cal = new List<Calendar>();
            for (int i = 0; i < 365; i++)
            {
                cal .Add(new Calendar()
                {
                    Date = a,
                    PersonalNr = 6004,
                    Wert = 123

                });
                a = a.AddDays(1);
            }
            ctx.AddRange(cal);
            ctx.SaveChanges();
16.806 Beiträge seit 2008
vor 8 Jahren

EF7 ist nicht fertig und Version 1 wird auch nicht alle Features von EF6 enthalten
EF7 bzw. wie es jetzt genauer EF Core 1 heisst ist eine vollständige Neuentwicklung und hat mit der ursprünglichen Basis nichts mehr gemeinsam.

Wenn es also eine Funktion nicht mehr gibt kann das folgende Gründe haben:
Sie heisst jetzt anders, sie wurde ersetzt oder sie kommt erst noch.

K
60 Beiträge seit 2014
vor 8 Jahren

Das ist ja alles richtig. Jedoch bin ich und der Threadersteller mit hoher Sicherheit nicht die einzigen, die nach einer Lösung oder nur einen Lösungsansatz suchen, die auch performant ist.
Das EF7 oder Core das von Haus aus nicht bietet, ist verständlich. Angeblich sollte dieses Feature in EF6 auch nur für die Migrationen genutzt werden. Wie auch immer, wie macht ihr das oder wie würdet ihr das machen?

16.806 Beiträge seit 2008
vor 8 Jahren

Ganz ehrlich: das EF ist für Bulk-Insert das völlig falsche Mittel, zudem ist das gar nicht Aufgabe des EF bzw. jedes anderen ORM. Und ja, ihr seid nicht die Einzigen mit dieser Erkenntnis - und auch nicht die Einzigen, die diese Antwort bekommen haben.

Die Art und Weise wie ein ORM funktioniert kann gar keine hoch-performanten Aufgaben erfüllen - sondern hat einen völlig anderen Fokus.
Um sowas zu vermeiden gibt es andere Mittel wie zB ein Key über alle Spalten zu legen oder eben ein Insert mit einer Where-Clause kombinieren.

Von daher bin ich der Meinung, dass Du und der Thread-Ersteller hier eine Funktion von einem ORM erwartet, wofür er primär nicht da ist bzw ganz andere Funktionalitäten berücksichtigt.

T
314 Beiträge seit 2013
vor 8 Jahren

Dann bleibt dir eben nur übrig den Datensatz erst abzufragen und dann entsprechend einzufügen oder zu aktualisieren.

Du kannst natürlich auch einfach ein Insert durchführen und im Fall der Schlüsselverletzung machste halt ein Update. Ob dies nun sinnvoller, schneller und sauberer ist als einfach kurz zu prüfen, ob der Datensatz vorhanden ist, ist natürlich fraglich.

16.806 Beiträge seit 2008
vor 8 Jahren

AddOrUpdate hat übrigens so funktioniert, dass das EF das Keyfeld kannte.
Es hat geschaut, ob diese Eigenschaft gesetzt ist. Wenn nein, dann gabs ein Add. Wenn ja, dann gabs ein Update.

Arg intelligent was AddOrUpdate nie und war dafür auch nie gedacht.

P
1.090 Beiträge seit 2011
vor 8 Jahren

Nun beim "Standard" Insert würde ich keine Prüfung machen.

Für größere Mengen an Daten kann man die BulkInsert extension verwenden.
CodePlex:EntityFramework.BulkInsert

AddOrUpdate kann man im zweifel ja selber schreiben.
Und bei einen BulkInsert kann man, vorher die Abfrage so schreiben, das man eine Liste zurück bekommt von Werten die noch nicht in der DB sind.

Sollte man mal gelesen haben:

Clean Code Developer
Entwurfsmuster
Anti-Pattern

K
60 Beiträge seit 2014
vor 8 Jahren

@Palin, diese Extension gibt es für EF7 nicht.
Dann bleibt wohl nichts anderes übrig als einen eigenen Insert Sql String zu schreiben.

T
314 Beiträge seit 2013
vor 8 Jahren

Wegen 500 Einträgen? Und ein eigenes INSERT bringt dich ja auch nicht weiter.

16.806 Beiträge seit 2008
vor 8 Jahren

Ich versteh nicht Kingside, wie man von einer noch nicht mal fertigen Framework wie EF Core 1 sowas verlangen will / kann. Geschweige denn Erweiterungen, die sowas abdecken, die selbst nicht fertig sein können.
Es ist nicht mal für den produktiven Einsatz empfohlen geschweige denn feature complete.
Sorry, hab ich genau Null Verständnis für so ne Haltung.

K
60 Beiträge seit 2014
vor 8 Jahren

@Abt: Welche Alternative bietet sich denn an um sowas durchzusetzen?
Wahrscheinlich in die tiefere ADO Ebene, oder?

16.806 Beiträge seit 2008
vor 8 Jahren

Erst mal Allgemein, das bis inklusive EF6 gilt:

EF ist für diesen Zweck (aktuell) vollkommen ungeeignet, weil es sehr viel Reflection verwendet und das eben Zeit frisst - verhältnismäßig viel Zeit.
Pro Objekt werden viele weitere Objekte erstellt, viel Variablen hin und her geschubst: das kostet alles Zeit!!

Das EF ist (bisher) nicht dazu konzipiert, dass es viele hundert Objekte zeitgleich verwaltet. Dementsprechend hoch wird eben auch der Overhead und damit die Performance niedrig.

Übrigens ein Grund, wieso Stackoverflow mit Dapper ihren eigenen MicroORM geschrieben hat, der vollständig auf dem TypeDescriptor basiert - damit ohne Reflection arbeitet.
Das wäre auch eher der Weg für einen Batch. Da braucht man keine tiefere ADO Ebene. Arg viel tiefer als SQL Code selbst mit ADO Befehlen zu schreiben geht eh nicht; außer Du willst dann mit Sockets arbeiten 😉

Nun bezogen auf EF Core 1 (bitte EF7 abgewöhnen, das gibt es nicht)
Mit EF Core 1 verändert sich das Verhalten ein wenig, weshalb hier sehr vermutlich in Sachen Performance bei normalen SQL Befehlen das EF Core 1 vor allem aktuell bekannten ORM Alternativen die Nase vorn haben wird.
Das liegt daran, dass das EF hier sehr viel intelligenter geworden ist und zB. Queries automatisiert zusammenfassen kann, sodass die I/O sinken und damit die Performance steigt.
Als Coffeebean und ich letzten November in Redmond waren hat Rowan Miller sogar zeigen können, dass EF Core 1 schneller ist als Plain SQL Code mit ADO.NET.

Was ich damit sagen will:
Versteht endlich, dass EF Core 1 nicht fertig ist! Es ist aktuell RC1, wobei mit der Version 1 keine neuen Features mehr zu erwarten sind. Das wurde transparent auch immer gesagt: EF Core 1 wird in der ersten Version nicht alle bisherigen Features abdecken und es gibt durchaus Szenarien, in den weiterhin EF6 eher geeignet ist.

Es wird sicherlich sowas wie eine Batch Extension geben - das gabs bisher immer. Aber wartet doch mal ab, bis das Produkt selbst überhaupt soweit ist.
Du kannst doch auch kein Flugzeug fliegen wollen, wenn Du weißt, dass es bald Flügel bekommt aber aktuell noch keine hat.

Wenn Du jetzt mich ach und krach eben EF Core 1 verwenden willst - dann tus.
Aber wunder Dich nicht, dass Du aktuell eben noch viel Code drum herum selbst schreiben musst.

MS ist bei den Themen ASP.NET, .NET Core und EF wirklich sehr sehr sehr transparent, alles wird auf GitHub gepflegt und angekündigt.
Das ist alles kein Geheimnis.

K
60 Beiträge seit 2014
vor 8 Jahren

@Abt: Ich habe deine Botschaft realisiert und verstanden 😃 .
Natürlich hast du recht, wenn du sagst es ist noch nicht fertig und wir sollen von einem noch nicht fertigen Produkt zuviel erwarten.

Nun, eigentlich geht meine Frage aber über EF hinaus. Welche Methode ist die beste um eine
List<Class> in eine Mssql Datenbank zu schmeißen und dabei zu überprüfen, ob bestimmte Daten bereits existieren. Wenn ja -> Update, Wenn nein -> Insert.
Müssen dafür Stored Procedures gebaut werden?
Macht es Sinn einen Trigger anzulegen, der dies überprüft?
Geht es vielleicht sogar mit einer eleganten performanten C# Lösung?
Ich will einfach das Rad nicht neu erfinden, denn ich bin mir sicher, dass es ganz viele Entwickler gibt, die bereits die Lösung gebaut haben.

Noch kurz zu EF Core 1: Wenn du dir mal die neuste documentation ansiehst, dort wird auch noch der Begriff EF7 verwendet: https://ef.readthedocs.org/en/latest/

16.806 Beiträge seit 2008
vor 8 Jahren

Müssen dafür Stored Procedures gebaut werden?

Nein, SP ist immer die aller aller aller aller letzte Lösung. Selbst hier würde ich nen Bulk Insert via Code vorziehen.

Macht es Sinn einen Trigger anzulegen, der dies überprüft?

Nein

Geht es vielleicht sogar mit einer eleganten performanten C# Lösung?

Ja. Immer.

Hier führen viele Wege nach Rom.
Bei 500 Einträgen würde ich kein Aufwand machen und das Read-Write-Gedöhns einfach selbst schreiben.
Bei Mass-Inserts (aktuell hab ich ein Projekt, bei dem jede Nach 16 Millionen Einträge hinzugefügt und/oder aktualisiert werden) im höheren Bereich verwende ich aktuell Dapper und schreibe die Statements selbst (bzw. hab mir hierfür Statement-Helper geschrieben, die Insert und Update automatisiert erzeugen).
Das erzeugte Statement wird pro Typ im Cache abgelegt, sodass das nur einmalig passieren muss.

Noch kurz zu EF Core 1: Wenn du dir mal die neuste documentation ansiehst, dort wird auch noch der Begriff EF7 verwendet

Alle Dokumentationen werden erst nach und nach gerade gezogen.
Das gilt vor allem für ASP.NET und EF ist organisatorisch Teil von ASP.NET.

Die Bezeichnung EF7 ist tot. Dazu gibt es offizielle Ankündigungen.

Entity Framework 7 is now Entity Framework Core 1.0 or EF Core 1.0 colloquially.

Da gibts auch recht wenig Diskussionsspielraum 😉

K
60 Beiträge seit 2014
vor 8 Jahren

Was meinst du mit Read-Write Gedöhns?
Meinst du, mit EF Read Datensatz -> If null -> insert...
Oder per einen Insert String ala "IF NOT EXISTS...." zu schreiben?

16.806 Beiträge seit 2008
vor 8 Jahren
  1. via EF ein Get aufrufen, und dann eben Insert / Update ausführen
  2. SQL Code selbst schreiben zB mit INSERT (...) WHERE ... ()

Frag Dich hier selbst, ob Du die höhere Performance bei gerade mal 500 Einträgen brauchst.
Das macht prozentual sicherlich 30-40% aus. Aber ob das nun 10 oder 20 Sekunden braucht; ist das relevant?

K
60 Beiträge seit 2014
vor 8 Jahren

Es soll später eine Art Kalendar werden. Wenn der Benutzer nun die Maske speichert, werden die Einträge gesichert.
Das Problem ist, wenn er die Maske verlässt und wieder aufruft und der Speichervorgang noch nicht abgeschlossen ist.
Ich denke schon, dass die 30-40% sehr viel ausmachen.
Kurz: Wenn es relevant ist, rätst du dazu einen SQL Code selbst zu schreiben?

16.806 Beiträge seit 2008
vor 8 Jahren

Das Problem ist, wenn er die Maske verlässt und wieder aufruft und der Speichervorgang noch nicht abgeschlossen ist.

.. dann stimmt für mich hier was an der Applikationslogik nicht...

K
60 Beiträge seit 2014
vor 8 Jahren

du meinst der Benutzer muss warten bis der Speichervorgang abgeschlossen ist?

B
110 Beiträge seit 2008
vor 8 Jahren

Müssen dafür Stored Procedures gebaut werden?
Nein, SP ist immer die aller aller aller aller letzte Lösung.

Wieso denn das?

16.806 Beiträge seit 2008
vor 8 Jahren

Es spricht vieles, das meiste gegen SPs

  • Testbarkeit (Aufwand viel höher, manche sagen es sei vorteilhaft unabhängig von der Anwendung zu testen; im Sinne des ALM aber natürlich nicht so)
  • Skalierbarkeit (Datenbanken sind ohnehin oft das Nadelör: wozu hier auch noch Logik ablegen, die man nicht bis kaum gut skalieren kann?)
  • Logik gehört nicht in die Datenbank (Verantwortlichkeitsverletzung), Business Rules haben in der DB nichts verloren
  • Fehleranfälligkeit von SPs ist viel höher. App Code ist robuster
  • Du machst Dich von einer DB Engine abhängig und hast evtl. zusätzliche Aufwände beim Aktualisieren
  • SPs sind i.d.R. für den selben Funktionsumfang viel viel viel Complexer
  • Die Wiederverwendung von SPs ist dafür viel geringer
  • Aus HR-Sicht sind fähige DB-Entwickler viel schwieriger zu finden, dafür aber viel teurer
  • Datenbanken gehören i.d.R. zu einer Applikation, weshalb das Schema auch Teil der Applikationsversionierung sein sollte
  • Benutzerverwaltung über Datenbanken sind kein tolles Erlebnis

Für SPs sprich sehr wenig, weshalb sie nur sehr bewusst eingesetzt werden sollten und Gott sei dank auch immer seltener werden.

Wenn das in ne Diskussion ausartet trenn ich das in ein eigenes Thema ab.
Aber eigentlich gibts dazu genug Lyrik hier im Forum oder via Google.