Parallele Nutzung von DB reglementieren

  • VB.NET

Es gibt 45 Antworten in diesem Thema. Der letzte Beitrag () ist von Nils_Kr.

    Parallele Nutzung von DB reglementieren

    Hi,

    ich arbeite zur Zeit an einer MS SQL2003-DB (veraltet, aber lässt sich nicht ändern). Ich habe zwei Nutzergruppen, die später gleichzeitig mit der DB arbeiten können sollen. Die erste Gruppe besteht aus
    ungefähr 20 Leuten die lesend zugreifen oder einen einfachen Insert-Befehl nutzen.

    Die zweite Nutzergruppe besteht aus 5-10 Personen, die Einträge in der Datenbank bearbeiten. Prinzipiell kommen die sich nicht in die Quere, weil jeder seinen eigenen
    Zuständigkeitsbereich hat, trotzdem möchte ich sichergehen, dass auch von der technischen Seite aus geklärt ist, dass es da keine Überschneidungen gibt.
    Microsoft bietet ja die "Sperren"-Funktion an, allerdings ist die Dokumentation relativ knapp gehalten. Ich weiß bis jetzt auch nicht, ob die Funktion zu meinem Programmablauf passt.

    Momentan werden alle Daten mit dem DB-Helper vom ErfinderDesRades in das DataSet des Programmes geladen, es findet keine Zugriffsbegrenzung statt.

    Das Projekt befindet sich im Anfangsstadium, von daher sind grundliegende Designänderungen möglich.

    Hat jemand eine Idee, wie ich mich der Thematik am besten annähern kann?

    greetings
    Nils
    Option strict = on

    If it's stupid and it works it ain't stupid.
    Ich verstehe ehrlich gesagt nicht wo das Problem ist.
    Die Datenbank hat doch verschiedene Lock-Typen die dafür sorgen, dass Datensätze die exklusiv zum bearbeiten von einem Nutzer geöffnet sind, nicht mehr von anderen Benutzern exklusiv geöffnet werden können.

    Vielleicht verstehe ich auch nicht recht wo genau das Problem liegt.
    Es war einmal ein kleiner Bär... der wollte eine Geschichte hörn... Da erzählte ihm seine Mutti:
    Es war einmal ein kleiner Bär... der wollte eine Geschichte hörn... Da erzählte ihm seine Mutti:
    Es war einmal ein kleiner Bär... der wollte eine Geschichte hörn... Da erzählte ihm seine Mutti:
    ... Nun solltest es selber wissen. :'D
    Momentan ist es so, dass die Daten ins Programm gezogen werden und die Verbindung geschlossen wird. Zum Speichern wird die Verbindung wieder geöffnet
    und alles geänderte wird hochgeladen.

    VB.NET-Quellcode

    1. Dim adp2 = New DatasetAdapter(SqlClient.SqlClientFactory.Instance, ConStringDecrypt, _
    2. Data.ConflictOption.OverwriteChanges)
    3. DataSet01.Adapter(adp2).Register(Me).Fill()


    Bis auf den Insert-Befehl werden die Daten nur indirekt bearbeitet. Ich ziele übrigens darauf ab, dass nicht nur einzelne Einträge gesperrt sind, sondern alle mit einem bestimmten Fremdschlüssel.
    Option strict = on

    If it's stupid and it works it ain't stupid.
    Naja, das Thema ist schon etwas komplexer.
    Man sollte dies DEFINITIV durchdenken. Afaik sind typ. Datasets bzw die Adapter sind da standardmässig auf last commit win?
    Denke mal da wird der @ErfinderDesRades was dazu sagen können.

    Grundsätzlich würde ich denken das optimistic concurrency das Günstigste ist. Also versuchen ein Update zu machen und ggf. einen Fehler zurückbekommen wenn jemand anderes den Datensatz bereits geändert hat. Ich glaube das unterstützen die Adapter auch.
    Das ist meine Signatur und sie wird wunderbar sein!

    Mono schrieb:

    Denke mal da wird der @ErfinderDesRades was dazu sagen können.

    nicht wirklich viel.
    Datasets sind nicht standardmäßig auf last-commit-win - das ist einstellbar.
    Im post#3 kann man allerdings ablesen, dass last-commit-win konfiguriert ist (Data.ConflictOption.OverwriteChanges).
    Aber das Problem ist nicht damit gelöst, dass auf ConflictOption.ThrowException umzustellen, sondern dann gehts erst richtig gemein los, wie die Exception dann nämlich zu behandeln wäre (was tatsächlich so gut wie unmöglich ist).

    Also ich hab noch nie eine Multi-User-Db gebastelt, mit Rollen und Rechten - gut möglich, dass man sich was anneres ausdenken muss als DbExtensions.
    Aber vlt. auch nicht. Also die DbExtensions können DataTables befüllen und rückspeichern, nicht mehr und nicht weniger.
    Datensätze zu sperren, bzw. Datensatz-Sperren abzufragen und so Kram ist vlt. eine andere Baustelle.
    Na das weiß ich jetzt schonmal was Sache ist. Für ReadOnly-Sachen kann ich weiter die DB-Extensions nutzen und die Bearbeitung bekommt ihr eigenes SQL-handling. Das ist auch garnicht so schlimm, weil bei der Bearbeitung arbeite ich mit eigenen User-Controls, die sind haben nur wenig mit dem DataSet-Designer zu tun.
    Option strict = on

    If it's stupid and it works it ain't stupid.
    Du musst die Verbindung zur DB schon offen lassen.
    Wenn jemand anfängt einen Datensatz zu ändern musst du den Datensatz in der DB locken.
    Änderungen schreiben wenn der Benutzer fertig ist und lock aufheben.

    Du musst beim Versuch einen Datensatz zu locken dann abfragen ob er schon locked ist, dann muss er eine Meldung bekommen.
    Die deutsche Sprache ist Freeware, du kannst sie benutzen, ohne dafür zu bezahlen. Sie ist aber nicht Open Source, also darfst du sie nicht verändern, wie es dir gerade passt.
    Nein das muss man nicht. Es gibt 2 Ansätze. Pessimistisches oder optimistisches Locking.

    Also entweder man versucht ein Update und behandelt die Exception, falls gelockt (also einfach mal optimistisch drauf los). Das ginge sicher "Irgendwie" mit den offline Datasets.
    Oder eben pessimistisch, dass heißt man prüft immer vorher, ob es ein Lock gibt und lässt den User dann den Datensatz nicht mehr bearbeiten. Dies wird mit einer Offline Datasetlösung glaub ich eher unschön.
    Das ist meine Signatur und sie wird wunderbar sein!
    Was führt man sich da am besten zu Gemüte? Konkrete Beispiele wären denke ich auch eine große Hilfe.

    Die Theorie dahinter ist mir schon klar, nur wie man das in der Praxis stabil zum laufen bekommt kann ich mir nicht so recht vorstellen.
    Bei DataSets ist z.B. schwierig einzelne Datensätze zu sperren.
    Eine Idee wäre die Änderungszeit mit abzulegen und dann könnte man kontrollieren, ob sich währenddessen etwas getan hat.

    Wenn ich die Verbindung offen halte und etwas Locke müsste ich ja auch erstmal bestimmen, was genau bearbeitet wird und das ist auch garnicht
    so einfach wenn das Programm standartmäßig alles ausliest.

    E: Momentan ist es so, dass ich mit einer Schleife durch einen Teil des DataSets durchgehe und alle relevanten Daten in Usercontrol-Objekte ablege.
    Da könnte ich mir den Umweg über das DataSet sparen und direkt die Datenbank durchsuchen. Da müsste ich dann nur die entsprechenden ID's
    für den Zeitraum der Bearbeitung sperren.
    Wo habt ihr die konkreten Befehle dazu her?
    Option strict = on

    If it's stupid and it works it ain't stupid.

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

    Mono schrieb:

    Oder eben pessimistisch, dass heißt man prüft immer vorher, ob es ein Lock gibt und lässt den User dann den Datensatz nicht mehr bearbeiten. Dies wird mit einer Offline Datasetlösung glaub ich eher unschön.
    Ich kann mir eiglich auch nur pessimistisches Locken vorstellen. Weil wenn beim optimistischen dann die Concurrency-Exception auftritt, dann ist das Kind ja schon in' Brunnen gefallen, und zwei User haben gleichzeitig ihre Arbeitszeit reingesteckt, und zwei Versionen desselben Datensatzes sind bei rausgekommen, und eines Users Bearbeitung muss man nun weghauen - also man kann evtl. ein entsprechendes Exception-Handling coden, aber dass ist mit Sicherheit unbefriedigend für den User, dessen Bearbeitung dann nachträglich verworfen wird.

    Also pessimistisch locken:
    Abrufen ist immer zulässig, aber zum Bearbeiten eines Datensatzes muss die Anwendung in einen besonderen Zustand gehen. Dabei wird zuerst ein TryLock gesendet, der - falls erfolgreich - andere daran hindert, diesen Datensatz ebenfalls zu okkupieren.
    Jo, und nach Bearbeitun, nach Cancel oder TimeOut wird der Lock dann aufgehoben.

    Ich seh's übrigens nicht so, dass ein typDataset der Umsetzung dieses Konzepts prinzipiell iwie in Wege stünde.
    Aber sicher bin ich nicht, mangels praktischer Erfahrung.

    Man muss auch die Anwendung genau angucken, ob dieser Heckmeck auch wirklich nötig ist.
    Oft ergibt sich ja auch durch Zuständigkeiten oder Rechte-Verteilung, dass die meisten Datensätze garnet von mehreren Nutzern gleichzeitig bearbeitet werden können/sollen.
    Da wär man ja schön blöd, wenn man für diese Entitäten trotzdem sonen Aufriss aufrisse.
    Es gibt verschiedene Bereiche. Jeder Bereich hat einen Meister oder Vorarbeiter. Im Normalfall bearbeitet jeder Meister nur seinen Bereich.
    Allerdings gibt es Urlaub/Krankheit und sonstige Vorkomnisse die dafür sorgen, dass jemand vertretungsweise eben doch auf einen anderen Bereich zugreifen muss.
    Da die Bedienung aber so simpel wie nur irgend möglich gehalten werden soll, macht so eine dynamische Sperre glaub ich schon Sinn.

    Zusätzlich soll es eine Auswertung nach fast beliebigen Faktoren geben und da ist es hilfreich wenn die Daten nicht strikt voneinander getrennt sind.
    Option strict = on

    If it's stupid and it works it ain't stupid.
    Naja - nach meim Dafürhalten klingt das nicht so, als obs der Normalfall wäre, dass da mehrere Meister gleichzeitig auf denselben Datensätzen rumorgeln werden.
    Auch eine Vertretung führt ja nicht dazu, dass zwei Meister gleichzeitig drin rumfuhrwerken.
    Also vlt. kann man da doch auch optimistisch locken, und als Exception-Handling dann den einen verwerfen, und ihm eine ÄtschBätsch-RückMeldung geben, wie: "Selber schuld, wenn ihr euch so schlecht koordiniert!"
    Sowas wird schon knifflig genug.

    Weil mit pessimistischem Lock ists wie gesagt ungleich aufwändiger, den Plan so wasserdicht umzusetzen, dass die Exception garnet auftreten kann.

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

    ich klinke mich hier mal ein da ich vor der selben problemstellung stehe:

    wenn ich mit dieser kalsse Dataset->Db
    jede Änderung, zB über ein Leave Event eines Steuerelements speicher, dann hält sich der Schaden doch in Grenzen wenn ein Datensatz gleichzeitig bearbeitet wird.
    Da immer nur die bearbeiteten Rowstates übertragen werden dürfte sich auch der Traffic in Grenzen halten wenn die Klasse so oft bemüht wird...

    Dann wäre es doch vermschmerzbar die optimistische Variante an zu gehen oder?
    Vorallem wenn sich die Gleichzeitigkeit der User zumindest theoretisch in Grenzen hält.

    Was meint Ihr?

    Ich würde dann einfach immer .Save und danach ein .Fill abrufen, dann wäre auch die Änderung von anderen Usern in der Kiste.
    Oder holt er sich bei Fill immer alles?
    Gruß Hannes
    jo, holt alles. Deshalb habich die Methode ja FillAll() genannt. Alternativ kann man auch CustomFill() nehmen, das heißt ja CustomFill(), weil man da benutzerdefiniertes Sql zur Ausführung bringt. Aber das muss man evtl. auch anpassen - wie gesagt: So oder so oder so: Sowas wird kompliziert.

    Aber würde mich interessieren, wie @Nils_Kr es denn nun gemacht hat.
    ok, ich werde es einfach mit fill all testen bis sich jemand beschwert und das dann so lösen dass ich nach jeder änderung datensätze rein schreibe.
    und wenn es zu langsam wird kann man die intensivem parts immernoch mit custom fill nachladen/vorladen.
    Gruß Hannes
    Bis jetzt hab ich noch Garnichts dahingehend gemacht.
    Ich benutze die DB-Extensions und habe folgenden Fehler bekommen(6x):
    Eine Ausnahme (erste Chance) des Typs "System.InvalidOperationException" ist in System.Data.dll aufgetreten.

    Der Con-String funktioniert, weil mit einem Insert-Befehl kann ich Daten einfügen. Ich denke, das DataSet passt nicht zu meinen Datenbanktabellen.
    Aber wie finde ich jetzt heraus, wo genau der Fehler liegt?
    Option strict = on

    If it's stupid and it works it ain't stupid.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Nils_Kr“ ()

    normalerweise kannst du dir eines generieren lassen von SqlServer. typisierter Db-Zugriff mit Connectoren

    (Dabei kriegst du auch typisierte TableAdapter mit-generiert, aber die funktionieren oft nicht richtig - schmeiß die besser gleich runter - spart auch pro Tabelle ca. 500 Zeilen generierten Code ein)