"best practise": Wie in unterschiedlichen Situationen auf SQL Datenbanken zugreifen?

  • VB.NET

Es gibt 8 Antworten in diesem Thema. Der letzte Beitrag () ist von raist10.

    "best practise": Wie in unterschiedlichen Situationen auf SQL Datenbanken zugreifen?

    hi!

    ich arbeite schon länger mit datenbanken (accdb, sql) und vb.net. ich habe bisher auch immer alles erreichen können, was rauskommen sollte :)
    jetzt möchte ich meinen programmierstil in dem bereich ein wenig professioneller gestalten und muss daher einige sachen hinterfragen.

    ich experimentiere hierbei auch gerade mit möglichkeiten, daten von einem sql server in ein datagridview zu übertragen

    dabei habe ich jetzt hier 3 varianten:

    VB.NET-Quellcode

    1. Public Function LoadServerDBToDataset(ByVal sqlStr As String) As DataSet
    2. da = New SqlDataAdapter(sqlStr, conStr)
    3. ds = New DataSet
    4. da.Fill(ds, "Mitglieder")
    5. Return ds
    6. End Function
    7. Public Function LoadServerDBToDataset2(ByVal sqlStr As String) As DataSet
    8. con = New SqlConnection(conStr)
    9. cmd = New SqlCommand(sqlStr, con)
    10. da = New SqlDataAdapter(cmd)
    11. cb = New SqlCommandBuilder(da)
    12. ds = New DataSet
    13. da.Fill(ds, "Mitglieder")
    14. Return ds
    15. End Function
    16. Public Function LoadServerDBToDataset3(ByVal sqlStr As String) As BindingSource
    17. con = New SqlConnection(conStr)
    18. cmd = New SqlCommand(sqlStr, con)
    19. da = New SqlDataAdapter(cmd)
    20. cb = New SqlCommandBuilder(da)
    21. ds = New DataSet
    22. bs = New BindingSource
    23. da.Fill(ds, "Mitglieder")
    24. bs.DataSource = ds
    25. bs.DataMember = "Mitglieder"
    26. Return bs
    27. End Function


    in 2 fällen wird ein dataset über geben, in einem eine bindingsource.
    in jedem fall setze ich diese dann als datasource meines datagridviews, passe bei den beiden ersten fällen noch das datamember an, und die daten werden angezeigt.

    a) wo ist der unterschied bei den ersten beiden varianten?

    b) warum brauche ich in keinem fall explizit eine verbindung zum sql server aufzubauen (con.Open())?

    c) wird die verbindung in obigen fällen automatisch aufgebaut UND getrennt, wenn das dataset gefüllt wurde?

    d) gibt es einen grund/vorteil mit der bindingsource variante zu arbeiten? (ich denke hier an die möglichkeit steuerelemente wie textboxen schnell und einfach via bindingsource an felder der tabelle zu binden, ohne alle einzeln mit einem reader einzulesen und zuzuordnen)



    jetzt zum "best practise" des datenbankzugriffs:

    ausgangssituation:
    - sql server 2008 speichert kundendaten, ehemalige kunden, etc
    - büroanwendung (verwaltung) und clientanwendung arbeiten mit diesen daten
    - gleichzeitiger schreib-/lesezugriff auf datensatz denkbar
    - die anwendungen laden alle kunden in ein datagridview, doppelklick auf einen kunden lädt alle kundendaten mittels reader in dafür vorgesehene textboxen/comboboxen
    - änderungen werden jeweils pro datensatz mittels executeReader und sql update befehl verwirklicht

    e) ist es sinnvoll, bei programmstart alle tabellen in ein dataset der anwendung zu laden, lesezugriff dann nur vom lokalen dataset zu betreiben und bei änderung von datensätzen nur diese auf dem server zu aktualisieren? (gbit netzwerk sollte da keine große wartezeit aufkommen lassen)?


    f) sollte eine der beiden anwendungen (z.b. büroanwendung) daten verändern, möchte ich, dass die andere anwendung (in dem falle die clientanwendung) davon erfährt und entsprechenden datensatz im lokalen dataset aktualisiert, oder - sollte die idee mit dem lokalen dataset, das alle tabellen am anfang einliest käse sein - entsprechenden datansatz vom server holt, sofern er gerade auf dem clientrechner geöffnet ist.
    ich hätte hier die idee, dass sich beide anwendungen via tcp/ip darüber informieren, wer gerade etwas verändert hat. die andere anwendung kann dann darauf reagieren.
    die andere möglichkeit wäre sicher, query notifications zu verwenden. was haltet ihr für sinnvoller?

    praktisches beispiel:
    kunde max mustermann kommt am empfang an, empfangmitarbeiter checkt kunden mit rfid karte ein, die clientanwendung lädt den datensatz vom sql server/lokalen dataset. kundendaten erscheinen auf dem monitor.
    gleichzeitig wird dieser kunde von der büroanwendung aus bearbeitet, erhält z.b. eine sperre. der rechner am empfang soll über die bearbeitung des datensatzes informiert werden, um diesen automatisch oder auf nachfrage aktualisieren zu können (entweder direkt vom sql server in die detailansicht des kunden laden, oder den datensatz im lokalen dataset aktualisieren und von dort aus die detailansicht füllen, wo wir auch wieder bei frage e) sind).
    mit tcp/ip kein thema. kurz eine msg an den client geschickt, dieser führt das dann aus. ist es mit query notification eleganter?
    ein anderes beispiel wäre, dass durch zufall beide anwendungen den gleichen datensatz zur gleichen zeit bearbeiten wollen, hierbei ist es unbedingt notwendig, dass beide anwender wissen, dass der andere den gleichen kunden bearbeitet.


    jetziger zustand:

    - es existiert nur die büroanwendung
    - anfangs werden alle kunden (ID, Name, Vorname) in ein datagridview geladen
    - darunter ist ein textfeld als suchmaske
    - klickt der benutzer auf einen kunden im grid, werden die gesamten daten des kunden rechts daneben in einem detailfenster angezeigt. hierbei lädt ein reader einzeln alle felder in die textboxen/comboboxen
    - es wird also bei jedem aufruf eine verbindung zum server aufgebaut --> siehe frage e): alles in lokales dataset laden und dann von dort aus die detailform füllen?
    - änderungen werden mittels ExecuteReader und sql update befehl in die tabelle auf dem server geschrieben
    g) wäre es hier besser/schneller/eleganter die detailform nicht mittels reader zu füllen und mittels sql update command zu speichern, sondern eine bindingsource zu verwenden und den dataadapter update-befehl bei änderungen zu benutzen?


    VIELEN dank!
    guggemol "Datenbank in 10 Minuten" auf Movie-Tuts

    Mir sagt keine deiner 3 Optionen zu, weil jedesmal ein neues Dataset erstellt wird - und was passiert mit dem alten? Bleibt es iwo bestehen, hast du dann nicht mindestens teilweise Daten doppelt? Beim Abspeichern - welche sollen dann abgespeichert werden?

    IMO darfs von einem Dataset möglichst nur ein Exemplar geben, und alles was geladen wird, wird da reingeladen, und nur dieses Dataset wird rückgespeichert.
    Ich rede übrigens nur von typisierten Datasets - untypisierte Datenobjekte sind im Grunde dirty Code, und sollte IMO gar nicht verwendet werden.
    Das ist mein Exposee für eine redundanzfreie Architektur.
    Bei großen Datenmengen muß man gucken. Übergeordnete Tabellen - etwa die Mitarbeiter einer mittelgroßen Firma - kann man locker zum Start komplett einlesen, aber untergeordnete Tabellen - etwa alle KundenVerträge aller Mitarbeiter - das wird dann evtl. doch bisserl viel, die sollte man lieber individuell nachladen, etwa alle Verträge eines Mitarbeiters oder sowas.

    So ein automatisches Update aller Clients, wenn ein Datensatz geändert wurde - das geht nur mit hässlichem Polling, oder aber auch beim SqlServer mittels SqlDependancy-Objekt.

    Ich rede übrigens nur von typisierten Datasets - untypisierte Datenobjekte sind im Grunde dirty Code, und sollte IMO gar nicht verwendet werden.


    Sorry ... das ist eine völlig falsche Aussage.

    Stehe gerade vor dem Thema (weisst schon WPF mit DataBinding und so ^^) ... und ehrlich ich bekomme das große Kotzen mit diesem Bullcrap von typisierten DataSets. Gehört für mich eindeutig in die Kategorie "größter Unfug ever".

    Das kannste vielleicht noch für Miniatur-Datenbanken nutzen, aber ich brauche für mein Aufgebanstellung 40 - 60 Tables á 40 bis 200 Columns, was typisierte DataSets schonmal vom Umfang her gar nicht packen. Von dem Aufwand für diese Anzahl an Tables und Fields Formulare zu entwerfen will ich jetzt nicht reden ... aber dann nochmal den ganzen Rotz als typisierte DS in die Anwendung einfüllen und dann händisch jedes Field an die passende Property der Datenklasse binden!?!?!? Ich weiss mittlerweilen wieso Du so auf DataGridViews für Anzeigen und gleichzeitig Bearbeitung stehst ... würde beim Umgang mit diesem Rotz auch nix anderes wählen.

    Aber jetzt kommt der Gag ... ich suche mir extra WPF aus um bei Änderungen (was für die Branche für die ich das mache an der Tagesordnung ist, dass man morgen andere/zusätzlichen Daten braucht als heute) an den Tables in der Zukunft nur schell die XAML (für neu hinzu gekommene Tables oder für Table-Änderungen) und das SQL-Script (für die Ergänzung der DB/Tables/Fields) raussenden zu müssen und dank typisierter DataSets muss ich dann wieder im Code rumfummeln und komplett neue Assembly ausliefern ... das ist doch echt völliger Quatsch.

    Sorry ... aber das ist wirklich der größte Mist den sich MS ausgedacht hat, anstatt flexibles Handling für Änderungen/Wartung/Verbesserungen zu bekommen bekommt man ein starres Konzept das jegliche Flexibilität und Reaktionsschnelligkeit abwürgt. Hätten Sie es gescheit gemacht, hätten Sie einen Converter entworfen der aus der Datenbank direkt die Struktur bei App-Start dynamisch ausliest (um neue Änderungen auch mit zu bekommen) und aus den ausgelesenen Schema-Informationen dann automatisch typsierte Zugriffe zur Verfügung stellt.

    Aber so ... slaopp formuliert: Für Anfänger und Miniatur-DB's okay, ansonsten völlig für den A.rsch.

    @ uNki


    jetzt zum "best practise" des datenbankzugriffs:

    ausgangssituation:
    - sql server 2008 speichert kundendaten, ehemalige kunden, etc
    - büroanwendung (verwaltung) und clientanwendung arbeiten mit diesen daten
    - gleichzeitiger schreib-/lesezugriff auf datensatz denkbar
    - die anwendungen laden alle kunden in ein datagridview, doppelklick auf einen kunden lädt alle kundendaten mittels reader in dafür vorgesehene textboxen/comboboxen
    - änderungen werden jeweils pro datensatz mittels executeReader und sql update befehl verwirklicht


    Das ist nicht best practise sondern nogo. *g*

    DBMS darf keine Rolle spielen bei der Anwendungserstellung, Du brauchst genau 1 Klasse die den Zugriff auf das DBMS handelt und Feierabend. Dem Rest musst es völlig egal sein ob da ein MS-SQL-Server oder ein My-SQL-Server oder ein Oracel-SQL-Server oder nur eine simples Access-File als DB dahinter steckt. D.h. Du musst zuerst mal die Entscheidung für das richtige Objektmodell treffen ... OleDB oder ODBC(ADODB). Dazu passend wählst Du dann das globale Arbeitsmodell aus ... für OleDB eben das DataSet oder System.Data.Common.DBDataTables/DBDataReader or whatever und für OCDB eben schlicht das DODB.Recordset. Noch besser wäre es wenn Du nirgendwo in Deiner Anwendung auch nur ein einziges Data-Modell verwendest, sondern dafür eine zweite Klasse einsetzt die z.B. SQL-Strings entgegen nimmt und dann die Ergebnis als Strings/Date oder List of T's oder wenn Du die Zeit und Laune hast als befüllte Datenklasse zurück wirft.

    Glaub mir, das ist zwar mehr Aufwand aber wenn Deine Anwendung langfristig laufen soll tust Du Dir damit einen großen Gefallen ... ändert sich irgendwas im Objektmodell bei MS (was ja nicht selten ist) oder das DBMS oder gar beides musst Du nur diese beiden Klassen ändern und das war's.


    e) ist es sinnvoll, bei programmstart alle tabellen in ein dataset der anwendung zu laden, lesezugriff dann nur vom lokalen dataset zu betreiben und bei änderung von datensätzen nur diese auf dem server zu aktualisieren? (gbit netzwerk sollte da keine große wartezeit aufkommen lassen)?


    Gerade bei einem SQL-Server sind solche Überlegungen Blödsinn. Die SQL-Server beherrschen Multi-User-Zugriffe par excellence ob da 10 oder 100 Leute gleichzeitig darauf zugreifen das interssiert die mal null. Die Frage nach Wartezeiten beim Zugriff ergibt sich vermutlich erst ab einige 1000 Usern die gleichzeitig auf die DB zugreifen wollen.

    Der größte Zeitfresser ist die Übermittlungsgeschwindigkeiten der Daten. Je kleiner die zu transportierende Menge desto schneller kann der User weiter arbeiten.

    Vor allem da Daten die man nicht braucht auf dem Client in den Memory zu laden bei einer Multi-User-Anwendung eh kompletter Schwachsinn ist ... Du musst sie dann bei jedem Zugriff eh aktualisieren da Du ja nicht weisst ob die in der Zwischenzeit bereits von einem anderen User geändert wurden und daher mal "kurz" refreshen musst und das dauert bei großen Tables ... von wegen der Datenmenge die über die Netzwerkverbindung transportiert werden muss.

    Also immer nur das anliefern lassen was Du auch wirklich brauchst, NIE mehr als das.


    f) sollte eine der beiden anwendungen (z.b. büroanwendung) daten verändern, möchte ich, dass die andere anwendung (in dem falle die clientanwendung) davon erfährt und entsprechenden datensatz im lokalen dataset aktualisiert, oder - sollte die idee mit dem lokalen dataset, das alle tabellen am anfang einliest käse sein - entsprechenden datansatz vom server holt, sofern er gerade auf dem clientrechner geöffnet ist.
    ich hätte hier die idee, dass sich beide anwendungen via tcp/ip darüber informieren, wer gerade etwas verändert hat. die andere anwendung kann dann darauf reagieren.
    die andere möglichkeit wäre sicher, query notifications zu verwenden. was haltet ihr für sinnvoller?


    Wie gesagt, alle oder einige Tabellen am Anfang komplett einlesen ist Bulls.hit, Erklärung siehe oben.

    Wichtig ist das Du die Anzeige von Daten und die Bearbeitung von Daten mehr als sauber trennst bei Multi-User-Zugriffen. Will der User einen Datensatz bearbeiten muss er ihn im Grid oder sonstwo anwählen und dann öffnen zur Bearbeitung.

    Daten zur Anzeige werden immer nur ReadOnly abgeholt vom DB-Server und dann in DataGrids or whatever angezeigt. Hier dürfen NIEMALS Datenbearbeitungen möglich sein. Die Aktualisierung der angezeigten Daten kannst Du entweder über Events regeln (SQL-Server können das, frag mich aber bitte gerade nicht genau wie) oder durch automatische Aktualisierung alle 30/60/90/120 Sekunden. Oder auch gar nicht ... denn ganz ehrlich, wie oft kommt es vor das ein User sich 10 Datensätze anzeigen lässt und während er diese fasziniert betrachtet ein anderer User Daten geändert hat die er dringendst sofort jetzt sehen muss. Spätestens wenn er 1 Datensatz zur Bearbeitung auswählt wird ja der neu abgerufen von der DB und dann sieht er schon die aktuellen Änderungen (daher immer nur das holen was Du auch gerade wirklich brauchst, dass ist dann nämlich aktuell ^^).

    Und jetzt musst Du Dir nur noch überlegen wie Du das handeln willst wenn der Fall auftritt das 2 User gleichzeitig den gleichen DS bearbeiten. Die DB würde standardmäßig nacheinander abhandeln ... wer zuletzt kommt gewinnt mit seiner Änderung automatisch da diese alle Änderungen zuvor durch seine überschrieben werden. Was meistens auch okay ist ... kommt aber natürlich auf die Daten an und auf die Anzahl der Nutzer.


    kunde max mustermann kommt am empfang an, empfangmitarbeiter checkt kunden mit rfid karte ein, die clientanwendung lädt den datensatz vom sql server/lokalen dataset. kundendaten erscheinen auf dem monitor.
    gleichzeitig wird dieser kunde von der büroanwendung aus bearbeitet, erhält z.b. eine sperre. der rechner am empfang soll über die bearbeitung des datensatzes informiert werden, um diesen automatisch oder auf nachfrage aktualisieren zu können (entweder direkt vom sql server in die detailansicht des kunden laden, oder den datensatz im lokalen dataset aktualisieren und von dort aus die detailansicht füllen, wo wir auch wieder bei frage e) sind).
    mit tcp/ip kein thema. kurz eine msg an den client geschickt, dieser führt das dann aus. ist es mit query notification eleganter?


    Genau deswegen nichts in den Speicher laden, sondern immer direkt vom Server was Du brauchst abholen ... das ist dann ja eh aktuell.


    ein anderes beispiel wäre, dass durch zufall beide anwendungen den gleichen datensatz zur gleichen zeit bearbeiten wollen, hierbei ist es unbedingt notwendig, dass beide anwender wissen, dass der andere den gleichen kunden bearbeitet.


    Und was willst Du machen wenn nun beide den DS bearbeiten zwar voneinander wissen aber trotzdem unabhängig voneinander speichern? Dann gewinnt wieder der der als letztes gespeichert hat wie oben beschrieben.

    Einzige Möglichkeit das sauber zu verhindern ist einen DS zu sperren wenn ein Mitarbeiter ihn zur Bearbeitung aufgerufen hat ... das der Mitarbeiter bearbeiten will kannst Du ja auf den Punkt nachvollziehen weil Du ja Daten-Anzeige von Daten-Bearbeitung trennst. ;)

    So eine Sperre wird meistens nach der 5-Minuten-Regel eingetragen. User ruft DS zur Bearbeitung auf und automatisch wird er für den DS in der DB als Bearbeiter eingetragen und zwar mit Now + 5 Minuten. Ruft der nächste User den gleichen DS zur Bearbeitung auf wird zuerst dieses zusätzliche Bearbeitungs-Feld geprüft und wenn da eine Zeitpunkt größer als Now drinnen steht bekommt er die Meldung "DS ist bereits in Bearbeitung und kann derzeit nicht bearbeitet werden".

    Dann kann er den DS in 5 Minuten wieder aufrufen und wenn er dann frei ist (nach Beendigung der Bearbeitung hat sich natürlich der Bearbeiter automatisch ausgetragen) kann er ihn jetzt bearbeiten und hat dann auch die Änderungen die der andere User gemacht hat direkt vorliegen.

    Gruß

    Rainer

    raist10 schrieb:

    Ich rede übrigens nur von typisierten Datasets - untypisierte Datenobjekte sind im Grunde dirty Code, und sollte IMO gar nicht verwendet werden.
    Sorry ... das ist eine völlig falsche Aussage.

    Nun - ich kann immerhin auf zig lauffähige Beispiele verweisen - mal irgendein Beispiel von dir würde mich ja sehr interessieren.

    ErfinderDesRades schrieb:


    Nun - ich kann immerhin auf zig lauffähige Beispiele verweisen - mal irgendein Beispiel von dir würde mich ja sehr interessieren.


    Ich hatte nicht gesagt das es nicht läuft, sondern nur das der Aufwand viel zu hoch ist das für größere Datennbanken zu nutzen. Das ist wirklich purer Irrsinn. Und ich denke Du kannst es Dir selber vorstellen wenn Du meine Zahlenangaben von oben mal berücksichtigst ... ich rechne mit einem Minimum von 5000 Fields overall.

    Und da ich jetzt das Zeug eh schreiben muss für meine neue App kann ich Dir in den nächsten Tagen mal die Test-Demo zum angucken geben ... die komplette App werde ich aber mit Sicherheit nicht zugänglich machen. *g*

    Gruß

    Rainer
    @ raist10


    a) also mache ich es im grunde derzeit schon richtig, oder?: ich lade eine kundenübersicht (readonly) in ein datagridview, aus diesem wählt man nun den kunden aus, der dann per sqlreader in die detailform geladen wird.
    wird in einem textfeld der detailform irgendetwas geändert, springt eine variable "dataChanged" auf true, beim drücken auf speichern oder beim verlassen der detailform (z.b. beim schließen der anwendung oder öffnen eines anderen kunden) wird der datensatz dann per executeReader und sql update befehl auf dem server aktualisiert.
    das schreiben und lesen auf und vom server übernimmt eine klasse. die nimmt einen sql string entgegen und gibt tables (zum füllen des datagridviews) bzw. strings (zum füllen der detailform) zurück.
    war mir nur nicht sicher, ob das so "best practise" ist, oder ob man lieber lokal arbeiten sollte.

    b) das problem mit dem gleichzeitigen bearbeiten von datensätzen, bzw. dem datensatz, der auf rechner 1 vor einer minute zum lesen geöffnet wurde und nun aber gerade von rechner 2 aktualisiert wurde werde ich also entweder per tcp/ip benachrichtigung oder der sache mit der sqldependency-klasse lösen. wichtig ist mir nur, dass nutzer 1 beim öffnen des datensatzes weiß, wenn dieser aktuell schon von benutzer 2 geöffnet ist oder gerade bearbeitet wird. sonst gehen ja die änderungen von einem der beiden verloren. dann könnte man nach first come first serve handeln und seine änderungen erst vornehmen, wenn der nutzer, der zuerst "drauf" war, fertig ist, und der datensatz samt änderungen erneut geladen wurde. (diese meldung sollte man über tcp/ip einfach realisieren können.. irgendein icon unten in der statusleiste mit tooltip "achtung, datensatz wird von xxx bearbeitet"). spricht allgemein etwas gegen die tcp/ip benachrichtigungen im lokalen netzwerk?

    c) wenn ich einen reader benutze um sachen zu lesen oder zu schreiben, baue ich vorher via con.Open() die verbindung auf und trenne sie danach auch wieder ordnungsgemäß mit con.Close().
    wie sieht es denn aber aus, wenn ich dieses konstrukt hier nehme, um zu beginn das datagridview mit der kundenliste zu füllen:

    VB.NET-Quellcode

    1. Public Function LoadServerDBToDataset(ByVal sqlStr As String) As DataSet
    2. da = New SqlDataAdapter(sqlStr, conStr)
    3. ds = New DataSet
    4. da.Fill(ds, "Mitglieder")
    5. Return ds
    6. End Function


    wird dort "automatisch" eine verbindung aufgebaut UND wieder geschlossen, ohne dass ich manuell con.Open() und con.Close() aufrufen muss?

    d) ErfinderDesRades hat geschrieben, dass es ungünstig ist, bei jedem aufruf der funktion (siehe c))ein neues dataset zu generieren. wäre es sinnvoller zu beginn der klasse ein dataset ds zu deklarieren und zu initialisieren und innerhalb der unter c) stehenden funktion dann immer nur die tabelle "Mitglieder" zu überschreiben mit "da.Fill(ds, "Mitglieder")"? somit hätte ich ja nur ein dataset mir einer tabelle.

    vielen dank!

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

    uNki schrieb:


    a) also mache ich es im grunde derzeit schon richtig, oder?: ich lade eine kundenübersicht (readonly) in ein datagridview, aus diesem wählt man nun den kunden aus, der dann per sqlreader in die detailform geladen wird.
    wird in einem textfeld der detailform irgendetwas geändert, springt eine variable "dataChanged" auf true, beim drücken auf speichern oder beim verlassen der detailform (z.b. beim schließen der anwendung oder öffnen eines anderen kunden) wird der datensatz dann per executeReader und sql update befehl auf dem server aktualisiert.
    das schreiben und lesen auf und vom server übernimmt eine klasse. die nimmt einen sql string entgegen und gibt tables (zum füllen des datagridviews) bzw. strings (zum füllen der detailform) zurück.
    war mir nur nicht sicher, ob das so "best practise" ist, oder ob man lieber lokal arbeiten sollte.


    Vom Prinzip her würde ich sagen genauso ist es richtig. Bis auf die Ausnahme das Du eigentlich jeden DS den ein User zur Bearbeitung aufruft so sperren solltest das kein andere den bearbeiten kann. Siehe unten.


    b) das problem mit dem gleichzeitigen bearbeiten von datensätzen, bzw. dem datensatz, der auf rechner 1 vor einer minute zum lesen geöffnet wurde und nun aber gerade von rechner 2 aktualisiert wurde werde ich also entweder per tcp/ip benachrichtigung oder der sache mit der sqldependency-klasse lösen. wichtig ist mir nur, dass nutzer 1 beim öffnen des datensatzes weiß, wenn dieser aktuell schon von benutzer 2 geöffnet ist oder gerade bearbeitet wird. sonst gehen ja die änderungen von einem der beiden verloren. dann könnte man nach first come first serve handeln und seine änderungen erst vornehmen, wenn der nutzer, der zuerst "drauf" war, fertig ist, und der datensatz samt änderungen erneut geladen wurde. (diese meldung sollte man über tcp/ip einfach realisieren können.. irgendein icon unten in der statusleiste mit tooltip "achtung, datensatz wird von xxx bearbeitet"). spricht allgemein etwas gegen die tcp/ip benachrichtigungen im lokalen netzwerk?


    Die Benachrichtigungsfunktion ist sicherlich nett, aber mehr auch nicht. Es wird nicht verhindern das User sich trotzdem gegenseitig überschreiben, dass kannst Du nur verhindern mit der im Post zuvor beschriebenen Sperrung nach 5 Minuten Regel. Das ist zwar rigider als dem User einen freundlichen Hinweis mit "Tu das jetzt lieber nicht" zu schicken, aber das einzige was wirklich hilft.


    c) wenn ich einen reader benutze um sachen zu lesen oder zu schreiben, baue ich vorher via con.Open() die verbindung auf und trenne sie danach auch wieder ordnungsgemäß mit con.Close().
    wie sieht es denn aber aus, wenn ich dieses konstrukt hier nehme, um zu beginn das datagridview mit der kundenliste zu füllen:

    VB.NET-Quellcode

    1. Public Function LoadServerDBToDataset(ByVal sqlStr As String) As DataSet
    2. da = New SqlDataAdapter(sqlStr, conStr)
    3. ds = New DataSet
    4. da.Fill(ds, "Mitglieder")
    5. Return ds
    6. End Function


    wird dort "automatisch" eine verbindung aufgebaut UND wieder geschlossen, ohne dass ich manuell con.Open() und con.Close() aufrufen muss?


    Glaube ich jetzt nicht, das der SqlDataAdapter über eine Dispose-Methode verfügt würde ich jetzt vermuten das die durch die Übergabe des Connection-String geöffnete Verbindung nur durch die Dispose Methode geschlossen wird.


    d) ErfinderDesRades hat geschrieben, dass es ungünstig ist, bei jedem aufruf der funktion (siehe c))ein neues dataset zu generieren. wäre es sinnvoller zu beginn der klasse ein dataset ds zu deklarieren und zu initialisieren und innerhalb der unter c) stehenden funktion dann immer nur die tabelle "Mitglieder" zu überschreiben mit "da.Fill(ds, "Mitglieder")"? somit hätte ich ja nur ein dataset mir einer tabelle.


    Ich mag DataSets nicht, sie können viel aber versagen aber auf der anderen Seite bei den wirklich wichtigen Dingen im Umgang mit einer DB gnadenlos.

    Ein DataSet ist quasi eine Art Datenbank im Memory des Clients. Du kannst in ein DataSet mehrere Tabellen packen mit Relations wenig nötig (siehe auch typisiertes DataSet) und arbeitest dann quasi offline ... also völlig los gelöst von der DB.

    Hier findest Du mal einige Aussagen dazu: geekinterview.com/question_details/15809 (wobei da einige Aussagen falsch sind, wie z.B. die Übermittlung von Daten per XML aus einem DataSet ... das geht nämlich genauso mit einem Recordset)

    Den vielfach genannte Vorteil das man mehrere Tables in einem DataSet halten kann, wäre jetzt für mich persönlich kein Vorteil. Wenn ich Daten anzeigen lassen will greife ich eh lieber auf Views/Querys zurück die direkt in der DB liegen und daher extrem schnell sind und wenn ich Daten ändern lassen will brauche ich nicht zig-Tables im Memory sondern nur das Table worauf sich die Änderung bezieht und wenn fertig geändert geht es ab in die DB und den anderen Usern liegen die Daten aktuell vor. Da ist sogar offline arbeiten eher nachteilig.

    Hier kannst Du Dir ja mal die Members von DataSet ansehen: msdn.microsoft.com/de-de/libra…members%28v=vs.80%29.aspx

    Was massiv auffällt ist das die Klasse DataSet keinerlei Möglichkeiten beinhaltet mit der Datenbank zu kommunizieren. Weder Änderungen zu übermitteln noch geänderte Daten aus der DB abzufragen. Das muss alles über die DataTable-Klasse geregelt werden. Guckst Du hier: msdn.microsoft.com/de-de/library/33y2221y%28v=vs.80%29.aspx

    DataSets hat einiges an Vorteilen gegenüber den althergebrachten Recordsets, aber gerade beim Mutli-User-Zugriff kann ich nur Nachteile erkennen. Selbst die SQLDependency-Klasse arbeitet nicht mit einem DataSet zusammen sondern braucht ein Command-Object um zu funktionieren. Autowerte/Autoeinträge die durch AutoInkrement-Spalten oder durch Trigger ausgelöst werden sind in einem DataSet ein echtes Problem ... weil es keine Chance hat diese ohne Datenbank-Connection zu empfangen.

    Der größte Nachteil dürfte aber der Memory-Verbrauch eines Data-Sets sein und das Problem das das Ding für größere DB's schlicht ungeeignet ist ... man kann halt keine 200 oder mehr komplett im Memory halten.

    Es gibt sicherlich Anwendungszwecke in denen ein DataSet Vorteile hat, aber meiner Meinung nach gibt es auch mehr als genug Fälle wo es das überhaupt nicht hat: Große DB-Anwendungen (von wegen x hundert Tables im Memory halten), DB's mit vielen Trigger-/AutoInkrement-Funktionen (kommt ja nix zurück), flexibeles Handling (hier ist der Einsatz als typisiertes DataSet gemeint) und natürlich ganz besonders bei Multi-User-Datenbanken. Gerade dafür ist es mehr als offensichtlich ein völliger Krampf weil es keinerlei Funktionen beherrscht die Mutli-User-Handling unterstützen sogar im Gegenteil noch die klassischen Möglichkeiten Multi-User zu handeln fast unmöglich macht.

    Es kann also nichts was für komplexeres Mutli-User-Datenbank-Handling nötig ist ... kann doch also nur ein "Spielzeug" für Anfänger und Mini-DB's sein ... sonst hätten man längst sinnvolle Möglichkeiten eingebaut Mutli-User zu handeln, wie z.B. Refreshen von geänderten Datensätzen durch andere Nutzer.

    Das einzige was Du nutzen kannst ist die Query Notification, guckst Du hier: msdn.microsoft.com/en-us/library/ms130764.aspx

    Was aber auch auf einem völligen anderen Level abläuft als das DataSet und Du daher dann die komplette Aktualisierungsroutine ... welche Notifications abonniert man wann und wie bringt man die die Änderungen in das DataSet zurück ... vor allem so schonend das DS die in Bearbeitung sind beim User nicht auf einmal refreshed werden und alle Bearbeitungen vom User sind futsch.

    Meine persönliche ehrliche Meinung ist: Selten ein schlechteres Objektmodell für Datenbankhandling gesehen als das DataSet, bzw. das ist vermutlich das schlechteste Objektmodell überhaupt ... es ist eigentlich völlig sinnlos, ausser man handelt nur Single-User-DB's und die dürfen dann aber auch nicht zu groß sein.

    Gruß

    Rainer

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

    @unki: Jetzt haste 2 Meinungen vorgelegt bekommen:
    1. Zugriff in typisierte Datenklassen wrappern, z.B. typisiertes Dataset
    2. Arbeiten ohne typisierte Datenklassen
    Ich empfehle noch, auf anneren Foren zu suchen oder anzufragen, vlt. ActiveVB oder myCsharp, weil vom EntitiyFramework und von Hybernate war noch nicht die Rede.
    Wenn man dieses "CrossPosting" offen mit angibt, wird sich da ja niemand drüber aufregen können, weil der Crosspost-Effect (an verschiedenen Stellen machen sich Leuts dieselben Gedanken - was an einer Stelle reichen täte) tritt ja nicht auf.
    So, habe jetzt nochmal ein wenig gesucht und bin dabei auf mehreres gestossen, unter anderem einen älteren Artikel der aber recht gut auf die Problematik Multi-User eingeht: msdn.microsoft.com/de-de/library/bb979079.aspx (Absatz: Aktualisierung der Datenbank).

    Man muss tatsächlich wie ich bereits vermutet habe eine komplette Konfliktlösungs-Logik in die Anwendung einbauen um das Thema Multi-User in den Griff zu bekommen. Denn im Gegensatz zu den vorbeugenden Maßnahmen wie LockPessimistic oder In-Use-Flag arbeitet man mit DataSet auf Basis von Konfliktlösungen. Sprich die normale Vorgehensweise bei einer DB Last-Save-Wins wird umgewandelt in First-Save-Wins.

    Was aber bei den Usern aber extrem unbeliebt ist weil es im Zweifelsfall heisst das die ganze Arbeit die man in die Änderung des Datensatzes gesteckt für den A.rsch war da man ihn nicht speichern kann. Nun muss der User erstmal eine der angebotenen Konfliktlösungen auswählen und zusätzlich bearbeiten, was wiederum im Zweifel heisst er muss anhakeln welche seiner Daten gespeichert werden sollen und welche dann doch nicht auf Grund der vorherigen Datenänderung.

    Was aber auch einen riesen Aufwand für den Entwickler darstellt eigene Konfliktlösungs-Rules zu erzeugen und im Zweifel ausführliche Möglichkeiten zur Konfliktlösung anzubieten.

    Davon ab das man wohl wirklich auch gezwungen bleibt den grossen Vorteil der DataSets - ohne DB-Bindung und damit Belegung von DB-Ressourcen zu arbeiten - quasi ein Stück weit wieder aufhebt, da man als Programmierer genötigt ist die Daten im DataSet die der User in Verwendung hat regelmäßig zu aktualisieren über die DataTables damit auch sicher gestellt ist das die Anzeige beim User aktuell ist.

    Gut, dementsprechend habe ich mich mal mit DataTables befasst ... und dann tatsächlich das grossen Kotzen bekommen. DataTables kenne auch kein refresh wenn man einen OleDB-Driver wählt, die können nur refreshen wenn man mit dem NET Framework Data Provider arbeitet! o_O

    Will man also ein geladendes DataTable innerhalb einer Verbindung mit einem OleDB-Provider mit einer DB aktualisieren gibt es nur eine Möglichkeit: Alle Daten im DataTable löschen (.Clear()) und dann wieder komplett neu laden!

    Guckst Du hier: social.msdn.microsoft.com/foru…c-413b-b09d-bfb0d800999d/

    Da es dazu auch passend genug Einträge mit neuerem Datum im Internet gibt wo User hilflos vor dem Problem stehen wie sie ein DataTable refreshen und unter den aktuellen Objekt-Member von DataTable auf msdn auch keine Refresh- oder Requery-Methode aufgelistet ist, dürfte das wohl noch immer aktuell sein. Es passt auch auf die konsequente und stringente Auslegung auf DataSets mit LockOptimistic und nachträglicher Konfliktlösung ... wozu vorab Daten aktualisieren, reicht doch wenn der User nachdem er seinen DS bearbeitet hat erfährt das jemand anders den schon vor Stunden geändert hat.

    Ich glaube ich bin mir mittlerweilen mehr als sicher das ich erstmal auf ADO NET 5.0 warte bevor ich mir diesen Murks antue, vor allem weil ich derzeit noch keine Anwendung mit weit über 100 Multi-Usern-Zugriffen laufen habe die es nötig machen würde aus Performance-/Ressorucen-Gründen über disconnected arbeiten nachzudenken und mich eher mit User-Rechnern rumschlage die noch 512 MB oder 1 GB Ram haben, was wiederum massiv gegen den Einsatz von InMemory-DB's spricht ... spätestens wenn Daten von RAM in Auslagerungsdatei umgespeichert werden müssen und vise verca knallt es die Client-Performance gen Null runter bis hin zu Korrumptions-Fehlern und Abstürzen.

    Aber ich werde die Zugriffe so kapseln das ich später mal wenn es nötig, bzw. sinnvoll ist nur noch die Zugriffsklasse austauschen muss um von Recordsets auf DataSets umzusteigen. Werde daher dann als Kommunikationstypen auf die Basis-Klassen aus System.Data.Common zurück greifen und wrappen. Erscheint mir derzeit die sinnvollste und zukunftssicherste Lösung zu sein.

    Ich vermute ja massiv nachdem sie nun LINQ mehr oder weniger wieder gekillt haben und nun alle Entwicklerressourcen auf das Framework Entity konzentrieren das sie da bald mit besseren/ausgereifteren Lösungen aufwarten. Das kann einfach nicht der Weisheit letzter Schluss sein ... aber nur meine persönliche Meinung. ;)

    Gruß

    Rainer

    PS: Keine Sorge den "Forschungs-" Aufwand betreibe ich nicht für diesen Thread hier, sondern weil ich wie schon geschrieben gerade selber vor dem Thema stehe welches Objekt-Modell setze ich für den Daten-Zugriff in meiner neu entstehenden WPF-Anwendung ein.