it-swarm.com.de

Referenzieren von Datenbankwerten in der Geschäftslogik

Ich denke, dies ist eine weitere Frage zu Hardcodierung und Best Practices. Angenommen, ich habe eine Liste von Werten, z. B. Obst, die in der Datenbank gespeichert ist (sie muss sich in der Datenbank befinden, da die Tabelle für andere Zwecke wie SSRS-Berichte verwendet wird), mit einer ID:

1 Apple 
2 Banana 
3 Grapes

Ich kann sie dem Benutzer präsentieren, er wählt eine aus, sie wird in seinem Profil als FavouriteFruit gespeichert und die ID in seinem Datensatz in der Datenbank gespeichert.

Was sind die Empfehlungen für die Zuweisung von Logik zu bestimmten Werten, wenn es um Geschäftsregeln/Domänenlogik geht? Angenommen, der Benutzer hat Trauben ausgewählt, für die ich eine zusätzliche Aufgabe ausführen möchte. Wie lässt sich der Traubenwert am besten referenzieren:

// Hard coded name
if (user.FavouriteFruit.Name == "Grapes")

// Hard coded ID
if (user.FavoriteFruit.ID == 3) // Grapes

// Duplicate the list of fruits in an enum
if (user.FavouriteFruit.ID == (int)Fruits.Grapes)

oder etwas anderes?

Da die FavouriteFruit natürlich in der gesamten Anwendung verwendet wird, kann die Liste hinzugefügt oder bearbeitet werden.

Jemand könnte entscheiden, dass "Trauben" in "Trauben" umbenannt werden sollen, und dies würde natürlich die Option für fest codierte Zeichenfolgen aufheben.

Die fest codierte ID ist jedoch nicht vollständig klar. Wie gezeigt, können Sie einfach einen Kommentar hinzufügen, um schnell zu identifizieren, um welches Element es sich handelt.

Bei der Option enum werden Daten aus der Datenbank dupliziert, was falsch erscheint, da sie möglicherweise nicht mehr synchron sind.

Trotzdem vielen Dank im Voraus für Kommentare oder Vorschläge.

46
Kate

Vermeiden Sie unbedingt Strings und magische Konstanten. Sie kommen überhaupt nicht in Frage, sie sollten nicht einmal als Optionen betrachtet werden. Dies scheint Ihnen nur eine praktikable Option zu lassen: Bezeichner, dh Aufzählungen. Es gibt jedoch noch eine weitere Option, die meiner Meinung nach die beste ist. Nennen wir diese Option "Vorinstallierte Objekte". Mit vorinstallierten Objekten können Sie Folgendes tun:

if( user.FavouriteFruit.ID == MyApplication.Grape.ID )

Was hier gerade passiert ist, ist, dass ich offensichtlich die gesamte Zeile von Grape in den Speicher geladen habe, sodass ich die ID für Vergleiche bereit habe. Wenn Sie zufällig Object-Relational Mapping (ORM) verwenden, sieht es noch besser aus:

if( user.FavouriteFruit == MyApplication.Grape )

(Deshalb nenne ich es "vorinstallierte Objekte".)

Was ich also mache, ist, dass ich während des Startvorgangs alle meine "Aufzählungstabellen" (kleine Tabellen wie Wochentage, Monate des Jahres, Geschlechter usw.) in die Hauptklasse der Anwendungsdomäne lade. Ich lade sie mit Namen, weil offensichtlich MyApplication.Grape muss die Zeile "Traube" erhalten, und ich behaupte, dass jeder einzelne von ihnen gefunden wird. Wenn nicht, haben wir beim Start einen garantierten Laufzeitfehler, der der am wenigsten bösartige aller Laufzeitfehler ist.

32
Mike Nakis

Die Überprüfung anhand der Zeichenfolge ist am besten lesbar, hat jedoch eine doppelte Aufgabe: Sie wird sowohl als Kennung als auch als Beschreibung verwendet (die sich aus nicht verwandten Gründen ändern kann).

Normalerweise teile ich beide Aufgaben in separate Felder auf:

id  code    description
 1  grape   Grapes
 2  Apple   Apple

Wo sich die Beschreibung ändern kann (aber nicht "Trauben" zu "Banane"), der Code sich jedoch niemals ändern darf.

Dies liegt zwar hauptsächlich daran, dass unsere IDs fast immer automatisch generiert werden und daher nicht gut passen. Wenn Sie IDs frei wählen können, können Sie möglicherweise garantieren, dass sie immer korrekt sind, und diese verwenden.

Wie oft bearbeitet jemand wirklich "Trauben" zu "Trauben"? Vielleicht ist nichts davon notwendig.

8
RemcoGerlich

Es ist nicht so schlimm, sie an beiden Orten (in einer Tabelle und in einer ENUM) zu speichern. Die Argumentation ist die folgende:

Durch Speichern in einer Datenbanktabelle können wir die referenzielle Integrität in der Datenbank über Fremdschlüssel erzwingen. Wenn Sie also einer Frucht eine Person oder eine andere Entität zuordnen, ist nur eine Frucht in der Datenbanktabelle vorhanden.

Das Speichern als ENUM ist auch deshalb sinnvoll, weil wir Code ohne magische Zeichenfolgen schreiben können und der Code dadurch besser lesbar wird. Ja, sie müssen synchron bleiben, aber wie schwierig wäre es wirklich, der ENUM eine Zeile und der Datenbank eine neue Einfügeanweisung hinzuzufügen.

Eine Sache, sobald eine ENUM definiert ist, ändern Sie nicht ihren Wert. Zum Beispiel, wenn Sie hatten:

  • Apple
  • Traube

Benennen Sie Trauben NICHT in Trauben um. Fügen Sie einfach eine neue ENUM hinzu.

  • Apple
  • Traube
  • Trauben

Wenn Sie Daten migrieren müssen, wenden Sie ein Update an, um alle Trauben in Trauben zu verschieben.

5
Jon Raynor

Was Sie hier erwarten, ist, dass die Programmierlogik automatisch an sich ändernde Daten angepasst werden kann. Einfache statische Optionen wie Enum funktionieren hier nicht, da Sie zur Laufzeit keine zusätzlichen Enums hinzufügen können.

Einige Muster, die ich gesehen habe:

  • Enums + Standard zum Schutz vor einem brandneuen Datenbankeintrag, der den Tag Ihres Programms ruiniert.
  • Codierung der auszuführenden Aktionen (Geschäftslogik) in der Datenbank selbst. In vielen Fällen ist dies sehr gut möglich, da viele Logiken wiederverwendet werden. Die Implementierung der Logik sollte im Programm erfolgen.
  • Zusätzliche Attribute/Spalten in der Datenbank, um den brandneuen Wert im Programm als "zu ignorieren" zu markieren, bis das Programm ordnungsgemäß bereitgestellt wird.
  • Fehlerhafte schnelle Mechanismen um den Codepfad, der die Werte aus der Datenbank lädt/neu lädt. (Wenn die entsprechende Aktion nicht im Programm enthalten ist UND nicht als ignoriert markiert ist, führen Sie keine Aktualisierung durch.).

Im Allgemeinen mag ich es, wenn Daten vollständig sind und sich auf Aktionen beziehen, die sie implizieren - auch wenn die Aktionen selbst möglicherweise an anderer Stelle implementiert werden. Jeder Code, der Aktionen unabhängig von den Daten bestimmt, hat gerade Ihre Datendarstellung zerstört, was höchstwahrscheinlich divergiert und zu Fehlern führt.

Sie haben Recht, diese Frage zu stellen. Es ist eigentlich eine nette Frage, wenn Sie versuchen, sich gegen die Bewertung ungenauer Bedingungen zu verteidigen.

Die Bewertung (Ihre if -Bedingungen) muss jedoch nicht unbedingt im Mittelpunkt Ihrer Umgehung stehen. Achten Sie stattdessen darauf, wie Sie die Änderungen verbreiten, die zu einem nicht synchronen Problem führen würden.

String-Ansatz

Wenn Sie Zeichenfolgen verwenden müssen, können Sie die Funktionalität zum Ändern der Liste über die Benutzeroberfläche verfügbar machen. Entwerfen Sie das System so, dass Sie beim Ändern von Grape in Grapes beispielsweise alle Datensätze aktualisieren, die derzeit auf Grape verweisen.

ID-Ansatz

Ich würde es immer vorziehen, auf eine ID zu verweisen, trotz des Kompromisses der Lesbarkeit. The list may be added to kann wieder etwas sein, über das Sie benachrichtigt werden, wenn Sie eine solche UI-Funktion verfügbar machen. Wenn Sie sich mit der Neuordnung von Elementen befassen, die die ID ändern, übertragen Sie eine solche Änderung erneut auf alle abhängigen Datensätze. Ähnlich wie oben. Eine andere Option (gemäß der richtigen Normalisierungskonvention wäre es, eine Enum/ID-Spalte zu haben - und auf eine detailliertere FruitDetail -Tabelle zu verweisen, die eine Spalte 'Order' enthält, die Sie nachschlagen können).

In beiden Fällen können Sie sehen, dass ich vorschlage, die Änderung oder Aktualisierung Ihrer Liste zu kontrollieren. Ob Sie dies mithilfe eines ORM oder eines anderen Datenzugriffs tun, hängt von den Besonderheiten Ihrer Technologie ab. Was Sie im Wesentlichen tun, ist, dass Leute, die sich von der DB entfernen, solche Änderungen vornehmen - was ich für in Ordnung halte. Die meisten Haupt-CRMs stellen die gleichen Anforderungen.

2
JᴀʏMᴇᴇ

Ein sehr häufiges Problem. Während das Duplizieren der Datenclientseite möglicherweise gegen die Prinzipien von DRY verstößt, liegt dies tatsächlich am Paradigmenunterschied zwischen den Ebenen.

Es ist auch nicht ungewöhnlich, dass die Aufzählung (oder was auch immer) nicht mit der Datenbank übereinstimmt. Möglicherweise haben Sie einen anderen Wert in eine Metadatentabelle verschoben, um eine neue Berichtsfunktion zu unterstützen, die im clientseitigen Code noch nicht verwendet wird.

Es passiert manchmal auch umgekehrt. Auf der Clientseite wird ein neuer Aufzählungswert hinzugefügt, aber die DB-Aktualisierung kann erst erfolgen, wenn der DBA die Änderungen anwenden kann.

1
Robbie Dee

Unter der Annahme, dass es sich um eine statische Suche handelt, ist die dritte Option - die Aufzählung - im Grunde die einzig vernünftige Wahl. Es ist das, was Sie tun würden, wenn die Datenbank nicht beteiligt wäre, also macht es Sinn.

Die Frage wird dann zu der Frage, wie Aufzählungen und statische/Nachschlagetabellen in der Datenbank synchron gehalten werden sollen. Leider ist dies kein Problem, auf das ich noch keine vollständige Antwort habe.

Nach Wahl führe ich meine gesamte Schema-Wartung im Code durch und kann daher eine Beziehung zwischen einem Build der Anwendung und einer erwarteten Schema-Version aufrechterhalten, so dass es einfach ist, die Suche und die Aufzählung synchron zu halten, aber es ist etwas, an das man sich erinnern muss tun. Es wäre besser, wenn es automatisierter wäre (und auch ein automatisierter Integrationstest, um sicherzustellen, dass die Aufzählungen und Suchvorgänge übereinstimmen, würde helfen), aber das habe ich noch nie implementiert.

1
Murph