Datensatzsperre - Multi-User Anwendung / Business Logik - Best Practice

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von GerhardW.

    Datensatzsperre - Multi-User Anwendung / Business Logik - Best Practice

    Hallo zusammen,
    ich habe bereits im Forum nach 'Datensatzsperre' gesucht und bin leider nicht fündig geworden.

    Ich bin auf der Suche nach einem Beispiel / Best Practice, dass
    mir die Erklärung bzw. vll. sogar die Erleuchtung bringt, beim
    Zusammenspiel zwischen Programmierumgebung VS mit VB oder C# und einer Datenbank
    unter MS SQL unter dem Aspekt einer Datensatzsperre.

    Versuche anhand einem einfachen Bespiel zu erklären, worauf ich hinaus will und würde
    mich über entsprechende Hinweise oder Beschreibungen recht freuen.

    In einer MS SQL Datenbank gibt es die Tabelle ADRESSE mit ID, Vorname, Name, Firma, PLZ etc.
    auf diese Datenbank haben 10 Nutzer Zugriff.
    Nutzer A bearbeitet den Eintrag mit der ID 10 in einem Form, den er über ein GRID ausgewählt hat.
    Nutzer B möchte nun den Eintrag mit selbiger ID bearbeiten und soll die Meldung bekommen:
    "Der Nutzer A hat den Datensatz mit der ID 10 in Bearbeitung, eine Bearbeitung durch Sie ist
    daher derzeit nicht möglich"

    Meine Frage ist also, wie ist das a) im Code zu betrachten / bearbeiten bzw. lässt dies eine Instanziierung zu
    wo die Instanzen voneinander wissen und den Bearbeitungstatus eines Datensatzes kennen?
    und oder b) die einezelne Datensatzsperre hauptsächlich die Aufgabe des SQL-Servers bleibt?

    Wenn b) zutreffend ist, wie wäre die Herangesehsweise? Einen TimeStamp der Tabelle bzw. des Datensatzes abfragen und gegenprüfen? :/

    Hilfestellungen / Kommentare von Erfahrenen in diesem Bereich wären spitze :rolleyes: :thumbsup:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „DMO“ ()

    Stichworte: DbConcurrency, optimistic lock, pessimistic lock, "first-in-wins", "last-in-wins" - Strategie.

    Was du beschreibst ist pessimistic lock-Funktionalität.
    Eine vollständige Lösung ist aufwändig: Das Proggi muss in einen Edit-Modus gehen, und dabei einen Timestamp setzen - das nenne ich mal "sich den Lock holen".
    Erst wenn der Timestamp abgelaufen ist, können andere Anwender "sich den Lock holen".
    Ist der Edit-Vorgang abgeschlossen, so setzt das Proggi den Timestamp wieder auf jesu christi geburt.
    Trödelt der User herum, sodasss der Timestamp abläuft, ehe er abspeichert (oder abbricht), so wird ihm das Abspeichern auch verweigert.

    Es empfiehlt sich wohl, für diese Logik einen modalen Dialog zu bauen, der beim Öffnen den Lock holt, und beim Schliessen wieder freigibt.

    Forensuche nach "Timestamp" müsste weitere Diskussionen dieses Themas erbringen.
    Vollzitat des direkten Vorposts an dieser Stelle entfernt ~VaporiZed

    Das geht natürlich schon in die richtige Richtung. Ein Modaldialog wäre jetzt hier nicht meine bevorzugte Wahl, sondern; wie du das auch schon an einem praktischen Beispiel gesehen hast, ein MDI Container mit TABs
    wo zwischen "Listenansicht" > GRID und "Bearbeitungsansicht" Form hin und her gewechselt werden kann.
    Die Felder in der Bearbeitungsansicht werden "überwacht" und Änderungen daran sollen den einzelnen Datensatz in die Bearbeitung "Sperre" bringen.

    Und warum soll so eine Lösung aufwendig sein? Ein einfaches Beispiel wie von mir beschrieben, sollte doch nicht viel Zeit in Anspruch nehmen?
    Gibt es vll. eine Libary oder ähnliches, dass man sich ansehen und ggf. auch in VS dafür ziehen kann?
    Mein Problem ist ja auch irgendwo, dass ich die Theorie verstehe, nur es praktisch / technisch zu übersetzen fehlt mir der Zugang / die Erfahrung.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    ich mache es so, dass ich in jeder tabelle ein feld LOCK habe. in dem Moment, wo ein user bei diesem datenatz anfängt zu editieren prüft das prog ob ein LOCK drauf ist und wenn nein, wird es gesetzt. mir ist auch egal, wann der lock passiert. sobald die zeile verlassen wird, wird der lock entfernt.

    und ja, falls das programm abraucht, ist der lock noch da...dafür gibts dann eine manuelle entsperrfunktion

    mir ist klar, dass dabei übelst viele updates passieren, aber ich wüsste nicht wie es anders geht, außer mit den bereits angesprochenen update-strategien

    im prinzip habe ich damit EDR zitiert :D :D :D :D :whistling: :whistling: :whistling: :love:

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „rrobbyy“ ()

    DMO schrieb:

    Und warum soll so eine Lösung aufwendig sein? Ein einfaches Beispiel wie von mir beschrieben, sollte doch nicht viel Zeit in Anspruch nehmen?
    Ich schätze 40h für jmd, der sich auskennt - vielleicht ist das in deim Verständnis ja nicht so viel.
    Man muss halt die DB ändern, den Dialog basteln, Logik, Logik Logik bauen für alle Fälle die auftreten können, testen, testen, testen, und am Ende noch aufräumen.
    Höchstwahrscheinlich auch konzipieren, welche Elemente mehrfachverwendbar sind, und was für jede Tabelle extra geschrieben werden muss.
    Wenn auch Löschung in das Pessimistic-Lock-System einbezogen werden müssen, wirds nochmal so kompliziert, denn dann sind auch die Locks aller untergeordneten Datensätze zu beachten.

    DMO schrieb:

    Gibt es vll. eine Libary oder ähnliches, dass man sich ansehen und ggf. auch in VS dafür ziehen kann?
    Ich weiss von nix.
    Ich weiss nur, dass das Thema im Forum schon öfter aufkam, und das letzte Mal auch in aller Ausführlichkeit. Wenn du das findest, kannste vlt. gezielt jmd ansprechen, der solch schon umgesetzt hat (ich nämlich nicht).
    Achso, und rrobbyy kannste natürlich auch fragen, nach dem Aufwand, und ob er dir was zur Verfügung stellen kann.
    Sorry das ich mich einmische.

    Normalerweise (in den Meißen größeren Anwendungen mit vielen Benutzern) wird es so gemacht das automatisch bei jeder Datensatzveränderung der Timestamp der letzten Änderung in eine Spalte geschrieben wird.

    Speichert ein User wird die Speicherung in SQL ausgedrückt in etwas so gemacht:

    SQL-Abfrage

    1. ​UPDATE table_name SET column1 = value1, column2 = value2, WHERE id=123 AND ChangedTimestamp < {der Zeitpunkt der Daten die vorliegen};


    Bekommen wir hier zurück das 0 Datensätze von der Änderung betroffen waren wurde des Datensatz von anderer Stelle editiert und nun können wir entweder einen Dialog öffnen wo der User entscheiden kann oder die Daten neu abrufen und der User muss abermals editieren.

    Ist einfacher zu implementieren und ich muss nicht sperren und wieder freigeben usw.

    Nur als Denkansatz gedacht. Ich fahre mit sowas ganz gut.

    Grüße
    Sascha
    If _work = worktype.hard Then Me.Drink(Coffee)
    Seht euch auch meine Tutorialreihe <WPF Lernen/> an oder abonniert meinen YouTube Kanal.

    ## Bitte markiere einen Thread als "Erledigt" wenn deine Frage beantwortet wurde. ##

    Hab sowas schon mal implementiert. Hierzu gab es eine separate Tabelle in der alle Datensätze drin standen die momentan „geöffnet“ sind.

    Beim öffnen eines Datensatzen (in einem Fenster) findet die Prüfung statt ob der Datensatz readonly oder editierbar geöffnet wird und ggf. die ID wird in besagte Tabelle geschrieben. Beim schließen des Datensatzes wird ggf. die ID wieder entfernt.

    Das ganze wiederum habe ich in einem UserControl umgesetzt damit man dieses in verschiedenen Programmbereichen nutzen kann.

    Edit: Finde die Variante von @Nofear23m auch ganz schick.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen

    Nofear23m schrieb:

    Sorry das ich mich einmische.
    wieso sorry? ist doch ausdrücklich erwünscht, dass möglichst viele sich zu äussern.

    Das von dir beschriebene Vorgehen entspricht glaub dem Optimistic-Lock-Pattern.
    Den kann man beim SqlServer auch mit einer Spalte vom Typ RowVersion umsetzen.
    RowVersion ist ähnlich Autoincrement datenbankseitig generiert, also der Client muss diese Art 'Timestamp' nichtmal setzen.
    Ich glaub sogar, die sqlCommandBuilder-Klasse kann so eingestellt werden, dass sie Update-Commands so generiert, dass automatisch die RowVersion abgeprüft wird.
    Und DataAdapter werfen dann die DbConcurrencyException, wenn ein UpdateCommand sich auf keinen Datensatz auswirkt (weil eben die RowVersion sich zwischenzeitlich geändert hat).
    Dennoch bleibt Aufwand, die Exception zu catchen und dem User anzuzeigen, dass Speichern nicht geklappt hat, und dass er seine Daten refreshen muss und nochmal versuchen kann.
    Jo - ist weniger Aufwand als pessimistic lock, aber so ein Ätsch!-Design ist dem User natürlich auch nicht so angenehm, wie wenn er beim pessimistic Lock gleich gesagt bekommt, dass er sich die Mühe sparen kann, weils grad nicht geht.

    Aber CommandBuilder + DataAdapter sind für dich ja glaub garnet interessant, du bist ja immer mit EF unterwegs.
    Das ist auch ein Aspekt, welche Technologie man da verwendet.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    @DMO

    Schau einmal hier. Da habe ich ein ähnliches Problem behandelt und auch eine statische Klasse geschrieben. Vielleicht hilft es dir als Idee. Wir setzen z.B. meine LockManager Klasse erfolgreich in unserer Umgebung ein. Allerdings mit einer MySQL Datenbank, aber das müsste ja egal sein, wenn die Klasse entsprechend der Datenbankanbindung adaptiert wird.

    Datenbank Anwendung im Netzwerk