Zwei Klassen haben:
public class Parent
{
public int Id { get; set; }
public int ChildId { get; set; }
}
public class Child { ... }
Soll ich beim Zuweisen ChildId
zu Parent
zuerst überprüfen, ob es in der Datenbank vorhanden ist, oder warten, bis die Datenbank eine Ausnahme auslöst?
Zum Beispiel (mit Entity Framework Core):
HINWEIS: Diese Überprüfungsarten sind auch in den offiziellen Microsoft-Dokumenten im gesamten Internet verfügbar: https://docs.microsoft.com/en-us/aspnet/mvc/overview/getting-started/getting-started-with-ef-using- mvc / handling-concurrency-with-the-entity-framework-in-einem-asp-net-mvc-application # Abteilung-Controller modifizieren, aber es gibt zusätzliche Ausnahmebehandlung fürSaveChanges
Beachten Sie außerdem, dass die Hauptabsicht dieser Überprüfung darin bestand, dem Benutzer der API eine angezeigte Nachricht und einen bekannten HTTP-Status zurückzugeben und Datenbankausnahmen nicht vollständig zu ignorieren. Und der einzige Ort, an dem eine Ausnahme ausgelöst wird, ist inside SaveChanges
oder SaveChangesAsync
call ... also gibt es keine Ausnahme, wenn Sie FindAsync
oder anrufen Any
. Wenn also ein untergeordnetes Element vorhanden ist, das jedoch zuvor gelöscht wurde, SaveChangesAsync
wird eine Nebenläufigkeitsausnahme ausgelöst.
Dies geschah aufgrund der Tatsache, dass foreign key violation
die Formatierung der Ausnahme für die Anzeige "Kind mit der ID {parent.ChildId} konnte nicht gefunden werden" erheblich schwieriger sein wird.
public async Task<ActionResult<Parent>> CreateParent(Parent parent)
{
// is this code redundant?
// NOTE: its probably better to use Any isntead of FindAsync because FindAsync selects *, and Any selects 1
var child = await _db.Children.FindAsync(parent.ChildId);
if (child == null)
return NotFound($"Child with id {parent.ChildId} could not be found.");
_db.Parents.Add(parent);
await _db.SaveChangesAsync();
return parent;
}
gegen:
public async Task<ActionResult<Parent>> CreateParent(Parent parent)
{
_db.Parents.Add(parent);
await _db.SaveChangesAsync(); // handle exception somewhere globally when child with the specified id doesn't exist...
return parent;
}
Das zweite Beispiel in Postgres löst einen 23503 foreign_key_violation
Fehler aus: https://www.postgresql.org/docs/9.4/static/errcodes-appendix.html
Der Nachteil der Behandlung von Ausnahmen auf diese Weise in ORM wie EF ist, dass sie nur mit einem bestimmten Datenbank-Backend funktionieren. Wenn Sie jemals zu SQL Server oder etwas anderem wechseln wollten, funktioniert dies nicht mehr, da sich der Fehlercode ändert.
Wenn die Ausnahme für den Endbenutzer nicht richtig formatiert wird, können einige Dinge angezeigt werden, die nur von Entwicklern angezeigt werden sollen.
Verbunden:
https://stackoverflow.com/questions/308905/sollte-es-ein-Transaktion-für-Leseanfragen sein
quelle
Child with id {parent.ChildId} could not be found.
. Und die Formatierung "Fremdschlüsselverletzung" finde ich in diesem Fall schlimmer.Antworten:
Eher eine verwirrte Frage, aber JA, Sie sollten zuerst prüfen und nicht nur eine DB-Ausnahme behandeln.
Zunächst befinden Sie sich in Ihrem Beispiel auf der Datenebene und verwenden EF direkt in der Datenbank, um SQL auszuführen. Ihr Code entspricht dem Ausführen
Die Alternative, die Sie vorschlagen, ist:
Die Verwendung der Exception zur Ausführung der bedingten Logik ist langsam und allgemein verpönt.
Sie haben eine Racebedingung und sollten eine Transaktion verwenden. Dies kann jedoch vollständig in Code erfolgen.
Der Schlüssel ist, sich zu fragen:
Wenn nicht, dann setzen Sie es sicher ein und lösen Sie eine Ausnahme aus. Behandeln Sie die Ausnahme jedoch wie jeden anderen Fehler, der auftreten kann.
Wenn Sie damit rechnen, ist dies NICHT außergewöhnlich und Sie sollten zuerst prüfen, ob das Kind vorhanden ist. Wenn dies nicht der Fall ist, antworten Sie mit der entsprechenden freundlichen Nachricht.
Bearbeiten - Es gibt eine Menge Kontroversen darüber, wie es scheint. Bevor Sie abstimmen, bedenken Sie:
A. Was wäre, wenn es zwei FK-Einschränkungen gäbe? Würden Sie es befürworten, die Ausnahmemeldung zu analysieren, um herauszufinden, welches Objekt fehlt?
B. Wenn Sie einen Fehler haben, wird nur eine SQL-Anweisung ausgeführt. Nur Treffer verursachen den Mehraufwand einer zweiten Abfrage.
C. Normalerweise ist Id ein Ersatzschlüssel. Es ist schwer vorstellbar, dass Sie einen kennen und nicht sicher sind, ob er sich in der Datenbank befindet. Überprüfen wäre seltsam. Aber was ist, wenn es sich um einen natürlichen Schlüssel handelt, den der Benutzer eingegeben hat? Das könnte eine hohe Wahrscheinlichkeit haben, nicht anwesend zu sein
quelle
Das Prüfen auf Eindeutigkeit und anschließende Einstellen ist ein Gegenmuster. Es kann immer vorkommen, dass die ID gleichzeitig zwischen Prüf- und Schreibzeit eingefügt wird. Datenbanken sind in der Lage, dieses Problem durch Mechanismen wie Einschränkungen und Transaktionen zu lösen. Die meisten Programmiersprachen sind es nicht. Wenn Sie Wert auf Datenkonsistenz legen, überlassen Sie dies daher dem Experten (der Datenbank).
quelle
Ich denke, was Sie "schnell scheitern" nennen und was ich es nenne, ist nicht dasselbe.
Erklären der Datenbank eine Änderung vornehmen und die Fehlerbehandlung, das ist schnell. Ihr Weg ist kompliziert, langsam und nicht besonders zuverlässig.
Ihre Technik geht nicht schnell kaputt, sie ist "Preflighting". Es gibt manchmal gute Gründe, aber nicht, wenn Sie eine Datenbank verwenden.
quelle
Dies begann als Kommentar, wurde aber zu groß.
Nein, wie in den anderen Antworten angegeben, sollte dieses Muster nicht verwendet werden. *
Bei Systemen, die asynchrone Komponenten verwenden, gibt es immer eine Race Condition, bei der sich die Datenbank (oder das Dateisystem oder ein anderes asynchrones System) zwischen der Prüfung und der Änderung ändern kann. Eine Überprüfung dieses Typs ist einfach keine zuverlässige Methode, um die Art von Fehler zu verhindern, die Sie nicht behandeln möchten.
Schlimmer noch, es gibt auf den ersten Blick den Eindruck, dass verhindert werden sollte, dass der doppelte Aufzeichnungsfehler ein falsches Sicherheitsgefühl hervorruft.
Die Fehlerbehandlung benötigen Sie trotzdem.
In Kommentaren haben Sie gefragt, was passiert, wenn Sie Daten aus mehreren Quellen benötigen.
Immer noch nein.
Das grundlegende Problem verschwindet nicht, wenn das, was Sie überprüfen möchten, komplexer wird.
Die Fehlerbehandlung brauchen Sie trotzdem.
Auch wenn diese Überprüfung eine zuverlässige Möglichkeit darstellt, um den bestimmten Fehler zu verhindern, vor dem Sie sich schützen möchten, können dennoch andere Fehler auftreten. Was passiert, wenn Sie die Verbindung zur Datenbank verlieren oder der Speicherplatz knapp wird oder?
Wahrscheinlich benötigen Sie ohnehin noch eine andere datenbankbezogene Fehlerbehandlung. Die Behandlung dieses speziellen Fehlers sollte wahrscheinlich ein kleiner Teil davon sein.
Wenn Sie Daten benötigen, um zu bestimmen, was geändert werden soll, müssen Sie diese offensichtlich irgendwo abrufen. (Je nachdem, welche Tools Sie verwenden, gibt es wahrscheinlich bessere Möglichkeiten als separate Abfragen, um sie zu erfassen.) Wenn Sie bei der Prüfung der von Ihnen erfassten Daten feststellen, dass Sie die Änderung nicht vornehmen müssen, großartig, nehmen Sie die Änderungen nicht vor Veränderung. Diese Feststellung ist völlig unabhängig von den Bedenken hinsichtlich der Fehlerbehandlung.
Die Fehlerbehandlung brauchen Sie trotzdem.
Ich weiß, dass ich mich wiederhole, aber ich halte es für wichtig, dies klar zu machen. Ich habe dieses Durcheinander schon einmal aufgeräumt.
Es wird irgendwann scheitern. Wenn dies nicht gelingt, wird es schwierig und zeitaufwändig sein, den Grund zu finden. Das Lösen von Problemen, die sich aus den Rennbedingungen ergeben, ist schwierig. Sie treten nicht durchgehend auf, sodass es schwierig oder sogar unmöglich ist, sie isoliert zu reproduzieren. Sie haben zunächst nicht die richtige Fehlerbehandlung vorgenommen, sodass Sie wahrscheinlich nicht viel zu tun haben: Möglicherweise ein Bericht eines Endbenutzers über einen kryptischen Text (den Sie ursprünglich verhindern wollten). Möglicherweise weist eine Stapelverfolgung auf die Funktion hin, dass der Fehler sogar möglich sein sollte, wenn Sie sich die Funktion anschauen, die offenkundig leugnet.
* Es kann berechtigte geschäftliche Gründe geben, diese vorhandenen Prüfungen durchzuführen, um zu verhindern, dass die Anwendung teure Arbeiten dupliziert, aber dies ist kein geeigneter Ersatz für eine ordnungsgemäße Fehlerbehandlung.
quelle
Ich denke, eine sekundäre Sache, die Sie hier beachten sollten - einer der Gründe, warum Sie dies möchten, ist, dass Sie eine Fehlermeldung formatieren können, die der Benutzer sehen kann.
Ich kann Ihnen nur empfehlen:
a) Zeigen Sie dem Endbenutzer für jeden die gleiche generische Fehlermeldung .
b) protokollieren Sie die tatsächliche Ausnahme an einem Ort, auf den nur die Entwickler zugreifen können (wenn sie sich auf einem Server befindet), oder an einem Ort, den Ihnen Fehlerberichterstellungstools senden können (wenn der Client bereitgestellt ist).
c) Versuchen Sie nicht, die von Ihnen protokollierten Fehlerausnahmedetails zu formatieren, es sei denn, Sie können weitere nützliche Informationen hinzufügen. Sie möchten nicht versehentlich die einen nützlichen Informationen "formatiert" haben, mit denen Sie ein Problem hätten aufspüren können.
Kurz gesagt - Ausnahmen sind voll von sehr nützlichen technischen Informationen. Nichts davon sollte für den Endbenutzer bestimmt sein, und Sie verlieren diese Informationen auf eigene Gefahr.
quelle