Laden...

Frage zum DAL im Bezug auf DataSets

Erstellt von andreas-82 vor 14 Jahren Letzter Beitrag vor 14 Jahren 2.109 Views
andreas-82 Themenstarter:in
42 Beiträge seit 2006
vor 14 Jahren
Frage zum DAL im Bezug auf DataSets

verwendetes Datenbanksystem: SQLServer2005

Tagchen,
ich versuche mich gerade in der Aufteilung einer Datenanwendung in 3 Schichten und möchte den absolut richtigen Weg einschlagen (im Bezug auf DataSets).
Es geht in meinem Übungsbeispiel um ein Koch-Rezept-Programm (Freundin freut sich 😉)

Ein typisiertes DataSet mit Fremdschlüsseleinschränkungen etc. (ohne TableAdapter) stellt den 'DatenContainer' dar.

Die Datenschicht soll gegen ein Interface programmiert werden, in etwa so:


    int AktualisiereZutaten(ref DSKochbuch.tblZutatDataTable tblZutat);
    int AktualisiereEinheit(ref DSKochbuch.tblEinheitDataTable tblEinheit);
    int AktualisiereKategorie(ref DSKochbuch.tblKategorieDataTable tblKategorie);
    int AktualisiereRezept(ref DSKochbuch dsRezept);
    ...

Zu meinen Fragen:

  1. Bei Aktualisierungs-, Insert- oder Löschmethoden in der DAL:
    Ist es gängig ein DataSet oder DataTable, in denen die entsprechende 'RowStates' geändert sind, aus der BL der DAL per Reference zu übergeben und dort per DataAdapter bearbeiten zu lassen? Oder lieber 'normal' übergeben, die Anzahl der aktualisierten Zeilen zurückzugeben und AcceptChanges() manuell aufrufen (nicht so komfortabel irgendwie).

  2. Welcher Weg wäre am besten um nur einen Teil meines DataSets in der BL zu aktualisieren:
    Wenn ich z.B. über eine Methode aus der DAL eine DataTable bekommen würde. Dann müsste ich ja aus dem DataSet ein DataTable.Clear() aufrufen und die neue Table mit DataTable.Merge() in mein bestehendes DS einfügen (nicht sonderlich elegant).
    Oder auch hier lieber das DataTable-Objekt oder gleich das ganze DS per Reference einer Aktualisierungsmethode übergeben und mit dem DataAdapter 'aktualisieren lassen' ?

Würde mich über eure Meinung und Erfahrungen sehr freuen!
Solche 'trivialen Dinge' lassen mich nämlich machmal nicht los.
Ich sag mir immer, wenn ich schon programmiere, dann wenigstens sauber =)

~ There's no knowledge that is not power~

5.299 Beiträge seit 2008
vor 14 Jahren

... und möchte den absolut richtigen Weg einschlagen (im Bezug auf DataSets).

Hohoho! Das ist noch _niemandem _gelungen! 😁

Bei Aktualisierungs-, Insert- oder Löschmethoden in der DAL:
Ist es gängig ein DataSet oder DataTable, in denen die entsprechende 'RowStates' geändert sind, aus der BL der DAL per Reference zu übergeben und dort per DataAdapter bearbeiten zu lassen? Oder lieber 'normal' übergeben, die Anzahl der aktualisierten Zeilen zurückzugeben und AcceptChanges() manuell aufrufen (nicht so komfortabel irgendwie).

Also son DataAdapter checkt ja selber, was zu updaten, inserten, deleten ist. Da werde ich eine Deibel tun, das ganze je nach Rowstate und was wieder auseinanderzuklamüsern.
Ich hab mir son DatasetAdapter gebastelt. Der kriegt mein Highlander-Dataset ("es kann nur eines geben"), und speichert, was zu speichern ist, und das wars dann auch schon mittm DAL

  1. Welcher Weg wäre am besten um nur einen Teil meines DataSets in der BL zu aktualisieren:
    Wenn ich z.B. über eine Methode aus der DAL eine DataTable bekommen würde. Dann müsste ich ja aus dem DataSet ein DataTable.Clear() aufrufen und die neue Table mit DataTable.Merge() in mein bestehendes DS einfügen (nicht sonderlich elegant).
    Oder auch hier lieber das DataTable-Objekt oder gleich das ganze DS per Reference einer Aktualisierungsmethode übergeben und mit dem DataAdapter 'aktualisieren lassen' ?

Äh - es geht um Kochrezepte?
Unter welchen Umständen bekommt man da einzelne Tabellen aus der DAL ins BL?
Geht sowas überhaupt - die Dinger sind doch mittnander verknüppert?

Also bei mir hat sich mit der Zeit eine etwas anarchistische Einstellung gegenüber so Design-Pattern breitgemacht. So Jesusmäßig: "Die Gesetze sind für die Menschen da, nicht der Mensch für die Gesetze" (ist ja auch grad Sonntag)

Und ganz wichtiger Pattern ist auch: Kirche im Dorf lassen.

Aber super-coolen Tabellen-Designer hasteda - was ist das, wo ist der her?
Meiner macht immer nur so, das mit den Properties inne Tabellen gfallt mir sehr gutt.
🙂

Der frühe Apfel fängt den Wurm.

m
233 Beiträge seit 2006
vor 14 Jahren

Aber super-coolen Tabellen-Designer hasteda - was ist das, wo ist der her?
Meiner macht immer nur so, das mit den Properties inne Tabellen gfallt mir sehr gutt.
😃

Das ist ein Diagramm im SQL Server Management Studio.

2.187 Beiträge seit 2005
vor 14 Jahren

Hallo andreas-82,

Wie ErfinderDesRades schon sagte, gibt es keinen richtigen Weg. Es gibt nur verschiedene Wege mit verschiedenen Vor- und Nachteilen.

Das wichtigste ist, dass möglichst wenig nach passendem Wort such "Steuerung" aus der Datenschicht in der Logikschicht landet.
Ich gebe einfach alles (DataRow, DataTable, DataSet, etc.) was ich speichern will nach "unten" und gut ist.
Da her sollte es so wenig Methoden und Objekte in der "Signatur" der Datenschicht geben wie möglich.

Vielleicht helfen Beispiele:


public IEnumerable<DataRow> LadeRezepte();
// ist besser als
public DataTable LadeRezepte();
// da im ersten eine Klasse weniger in der Logikschicht bekannt sein muss


public IEnumerable<IRezept> LadeRezepte();
// ist besser als
public IEnumerable<RezeptDataRow> LadeRezepte();
// da es ein Interface verwendet und keine Klasse

Arg, ich kann's mir doch nicht verkneifen:
Ich möchte noch darauf hinweisen, das DataTable/DataRow/DataSet nicht die einzige Möglichkeit sind. Ich persönlich würde einen O/R Mapper DataSet/~Table/~Row vorziehen, da (meiner Meinung nach) diese Klassen gegen das OOP-Grundprinzip der Kapselung verstossen. (Aber dazu kann man mit der Forumssuche einige Diskusionen finden ... ich bin nur im Moment zu blöd dafür kopf kraz)

Gruß
Juy Juka

3.728 Beiträge seit 2005
vor 14 Jahren

Hallo andreas-82,

den richtigen oder besten Weg kann ich Dir auch nicht sagen. Aber ich arbeite sehr viel mit DataSets in meinen Projekten und kann Dir Vorgehensweisen nennen, die sich in der Praxis bewährt haben.

  1. Bei Aktualisierungs-, Insert- oder Löschmethoden in der DAL:
    Ist es gängig ein DataSet oder DataTable, in denen die entsprechende 'RowStates' geändert sind, aus der BL der DAL per Reference zu übergeben und dort per DataAdapter bearbeiten zu lassen? Oder lieber 'normal' übergeben, die Anzahl der aktualisierten Zeilen zurückzugeben und AcceptChanges() manuell aufrufen (nicht so komfortabel irgendwie).

Übergabe "By Reference" ist nicht üblich und sollte auch nicht zum Einsatz kommen. Also 'normal' deklarieren. Normal heißt übrigtens "By Value". Der Grund dafür ist simpel. DataTables/DataSets sind Verweistypen. Übergabe "By Value" bedeutet bei Verweistypen, dass ein eine neue Referenz auf die Daten im Speicher erzeugt wird und diese übergeben wird. Bei Übergabe "By Reference" wird die original Referenz direkt übergeben. Das kann zu unbeabsichtigtem Verhalten führen. Wenn z.B. in der Aufgerufenen Funktion eine "By Reference" übergebene DataSet-Variable auf null gesetzt wird, ist die Variable im Gültigkeitsbereich des Aufrufers ebenfalls null, da beide die Selbe Referenz verwenden. Ein weiterer Nachteil von Übergabe "By Reference" ist, dass es Probleme machen kann, wenn Du Deine Anwendung verteilbar machen wilst (Also Tiers statt Layers).

Wenn Du mit DataSets/DataTables arbeitest, solltest Du grundsätzlich Stapel und nicht einzelne Datensätze verarbeiten. Das heißt, dass der Benutzer z.B. ein neues Rezept und alle Zutaten auf einmal eingibt und diese Änderungen auf einen Rutsch speichert. Die Speicherung selbst mit dem DataAdapter musst Du pro DataTable vornehmen. Es hat sich in der Praxis als sinnvoll erwiesen, pro DataTable eine SaveXXX-Methode zu schreiben. Wenn Du Änderungen an mehreren DataTables im DataSet ausführen willst, sollte noch eine SaveDataSet-Methode dazukommen. Das ist besonders wichtig, um die Persistenz der Änderungen in verschiedenen Tabellen innerhalb EINER Transaktion ausführen zu können. Ich würde übrigens nicht die Standard-ADO.NET-Transaktionen verwenden, sondern Systems.Transaction-Transaktionen.

  1. Welcher Weg wäre am besten um nur einen Teil meines DataSets in der BL zu aktualisieren:
    Wenn ich z.B. über eine Methode aus der DAL eine DataTable bekommen würde. Dann müsste ich ja aus dem DataSet ein DataTable.Clear() aufrufen und die neue Table mit DataTable.Merge() in mein bestehendes DS einfügen (nicht sonderlich elegant).
    Oder auch hier lieber das DataTable-Objekt oder gleich das ganze DS per Reference einer Aktualisierungsmethode übergeben und mit dem DataAdapter 'aktualisieren lassen' ?

Das mit der Merge-Methode ist aber der empfohlene Weg, sowas zu tun. Und es ist sogar sehr elegant, da Du so z.B. bei Windows.Forms den Designer voll ausnutzen kannst und Dir jede Menge arbeits sparst. In den Formularen wäre es tödlich bei jedem Aufruf ein neues DataSet zuzuweisen, da ja Steuerelemnente über BindingSources daran gebunden sind. Mit Merge kannst Du bereits gebundene DataSets/DataTables aktualisieren, ohne dass Du irgendwelchen "Reparaturcode" für die Datenbindung schreiben musst. Ich fülle meine DataSets clientseitig grundsätzlich über Merge. Der Client startet in 95% der Fälle mit einem leeren typisierten DataSet, welches zur Entwurfszeit als Komponente plaziert wird.

Merge ist auch noch in anderer Weise elegant. Mann kann nämlich damit sehr gut typisierte und untypisierte DataSet/DataTables miteinander kombinieren.

Falls Du Deine Anwendung verteilbar angelegt hast, kann es sinnvoll sein, bein Persistieren GetChanges zu verwenden, um nur Zeilen zum Applikationsserver serialisieren zu müssen, die auch wirklich Änderungen aufweisen.

@JuyJuka: IEnumerable<IRezept> wird nicht klappen, da DataTables vom Designer generiert werden und der legt keine Interfaces an. Natürlich kann man sowas selber reinbauen, aber spätestens bei der nächsten Anpassung im Designer ist alles wieder weg. Generell ist die Vorgehensweise gut, aber für DataSets passt sie nicht. Außerdem sollte man nicht versuchen, die DataRows in Objektlisten zu verwalten. Eine DataRow ist ohne ihre DataTable nicht lebensfähig. Deshalb hat DataRow auch keinen öffentlichen Konstruktor. Man möchte - wenn man mit DataSets/DataTables arbeitet - auch die Möglichkeiten dieser Konstrukte nutzen. Das geht aber nur, wenn man in ganzen DataTables denkt. Wenn man z.B. innerhalb einer DataTable Zeilen mit bestimmetn Eigenschfaten finden will (Select-Methode der DataTable), oder die Table im Speicher anders sortieren oder filtern will (DataView auf DataTable) braucht man immer die DataTable.
Bei typisierten DataSets geht man auch den Weg, dass triviale Geschäftslogik (z.B. die Summen der Positionen eines Angebots ausrechnen) direkt im DataSet implementiert wird. Die Datenstruktur wird dadurch intelligent. Natürlich muss man damit vorsichtig sein, sollte z.B. nicht aus einer DataSet-Funktion aufs Dateisystem, irgendwelche Server oder gar direkt auf die Datenbank zugreifen. Wer sich entscheidet DataSets als Container für seine Daten zu verwenden, sollte das bewusst tun und genau wissen warum.

2.187 Beiträge seit 2005
vor 14 Jahren

Guten Morgen,

Folgendes ist jetzt vermutlich Hypotetisch, da ich aber eh nicht freiwillig mit DataSet/~Table und Co. arbeiten würde g :

IEnumerable<IRezept> wird nicht klappen, da DataTables vom Designer generiert werden und der legt keine Interfaces an.

Ist ein vom Designer generierte DataTable nicht partial? Könnte man das Interface nicht in der "nicht-designer-datei" machen?

Eine DataRow ist ohne ihre DataTable nicht lebensfähig.

Wie wäre es mit folgendem interface für die Rezept-Factory:


public interface IRezeptFactory
{
  IEnumerable<IRezept> LadeRezepte(); 
  IEnumerable<IRezept> LadeRezepte(IBedingung<IRezept> bedingung); 
  // okey, bedingung könnte man der Einfachheit halber weg lassen.
  IRezept New();
  void Save(IEnumerable<IRezept> rezepte);
}

Das müsste doch gleichermassen mit DataTable's wie auch mit O/R-Mapper-Objekten zu bedienen sein.

Gruß
Juy Juka

3.728 Beiträge seit 2005
vor 14 Jahren

Ist ein vom Designer generierte DataTable nicht :::

Ja schon, aber man müsste Änderungen am DataSet von Hand im Interface nachziehen. Das ist nicht das, was man haben möchte, wenn man einen Code-Generator benutzt. Statt typisierte DataSets in ein OR-Mapping-Korsett zu zwängen, würde ich dann lieber das ADO.NET Entity Framework einsetzen. Allerdings nicht bei verteilten Anwendungen. Da hat es zu viele Nachteile.

Das müsste doch gleichermassen mit DataTable's wie auch mit O/R-Mapper-Objekten zu bedienen sein.

Dann hätte man aber nur den kleinsten gemeinsamen Nenner und könnte die Vorteile von DataSets (In-Memory-Abfragen, Filtern, Sortieren, Gute Offline-Synchronisation) nicht mehr nutzen. Datenzugriff muss auch performant sein. Wenn man zu viele Abstraktionsschichten und Wrapper hineinbringt ist das nicht mehr gegeben. Wenn Abstraktion ein wichtiger Punkt für ein Projekt ist, macht vielleicht auch wieder das Entity Framework Sinn. Ich neige dazu konkret zu programmieren. Das passende Tool oder Framework auswählen und so einsetzen, wie es vom Hersteller vorgesehen ist. Das ergibt am Ende einfache, saubere und robuste Anwendungen. Wenn ich OR-Mapping mit flachen Objekten will, dann würde ich einen passenden OR-Mapper einsetzen und nicht den Versuch unternehmen aus einem Hund ein Pferd zu machen. Beide Varianten haben ihre Daseinsberechtigung. Und sogar noch eine Dritte: DataReader-Direkt!
In der Genesis steht "Und der HERR sah, dass es gut war.". Genau das möchte ich auch sagen können, wenn ich ein Projekt fertig habe. Falls die Architektur im Projekt aber zum Selbstzweck geworden ist, werde ich das nicht sagen können.

b
121 Beiträge seit 2006
vor 14 Jahren

.....Ich würde übrigens nicht die Standard-ADO.NET-Transaktionen verwenden, sondern Systems.Transaction-Transaktionen.

und warum?

3.728 Beiträge seit 2005
vor 14 Jahren

ADO.NET-Transaktionen hängen an der Connection und sind nicht verteilbar. Außerdem sollte man Transaktionen in der BL und nicht in der DAL aufspannen. Bei ADO.NET-Transaktionen geht das schlecht bis garnicht. Auch Transaktionen über Verschiedene RDBMS sind mit Standard-Transaktionen nicht möglich. Wenn z.B. Oracle und SQL Server an der selben Transaktion teilnehmen sollen/müssen, guckt man mit Standard-Transaktionen in die Röhre. Deshalb System.Transactions einsetzen!

5.299 Beiträge seit 2008
vor 14 Jahren

Hi andreas!

Zum "richtigen und perfekten" Proggen ist mir noch was eingefallen:
Erstmal gefällt mir dein Datenmodell ausgezeichnet, würde bei mir strukturell dasselbe bei rauskommen.
Nur zusammengesetzte PrimKeys lehne ich für mich ab (also ist mir bisher gelungen, ohne auszukommen). Das vereinfacht weitergehende Verarbeitungen, wenn ich weiß: Der PrimKey ist genau eine Spalte. - dann muß ich nicht bei jedem Zugriff damit erstmal checken: existiert ein PrimKey? wieviele Spalten hatter? und so.

Dann kann man noch anne Benamung rumbosseln: tbl-Prefix ist ziemlich ungünstig.
Erstmal ist superklar, daß in einer Datenbank Tabellen sind (gut, es gibt dort noch andere Sachen, aber die zeichnen sich dann eigens aus), und im Dataset gibts nur Tabellen, und wenn die dann alle mit tbl anfangen, werden Rows und EventArgs entsprechend generiert:


KochbuchDataset.tblRezeptDataTable
KochbuchDataset.tblRezeptRow
KochbuchDataset.tblRezeptRowChangedEventArgs

Und "tbl" als Prefix einer DataRow ist eben iwie falsch.

Das isses ja mittm Designer: Der generiert erst die Datasetnamen aus der Datenbank, und dann die Codenamen aus der Dataset.

Und der Spaltenname "Name" taucht prinzipiell in fast jeder Tabelle auf (_muss _ja).
Das erweist sich dann bei der Verarbeitung schnell als unübersichtlich.
Da würde ichs machen, entsprechend dem, wiedes mitte ID gemacht hast:
"RezeptName", "ZutatName", etc.

Das wars aber auch schon von meiner Seite 👍

Der frühe Apfel fängt den Wurm.

3.003 Beiträge seit 2006
vor 14 Jahren

Hi andreas!

Zum "richtigen und perfekten" Proggen ist mir noch was eingefallen:
Erstmal gefällt mir dein Datenmodell ausgezeichnet, würde bei mir strukturell dasselbe bei rauskommen.
Nur zusammengesetzte PrimKeys lehne ich für mich ab (also ist mir bisher gelungen, ohne auszukommen).

Das ist seltsam. Bist du dir ganz sicher, dass du in diesen Fällen nicht die ZeilenIDENTITÄT mit dem ZeilenSCHLÜSSEL verwechselst?

Den Fehler findet man erstaunlich häufig - ich mach mal ein Beispiel.
Artikel und Kategorien: m:n, dh jeder Artikel kann in mehrere Kategorien, jede Kategorie kann mehrere Artikel haben. Die Beziehung wird in einer Tabelle festgehalten. Wenn man wie du argumentiert, dass es nur EINEN PK geben soll, dann ist man gezwungen, neben den Spalten ArtikelID und KategorieID noch einen PK einzuführen, also eine Spalte, die ich mal RelationID nenne.
Peng, Identität und Schlüsseleigenschaft verwechselt. Denn dieses Konstrukt erlaubt einen doppelten Eintrag mit derselben ArtikelID/KategorieID-Kombination.

Korrekt wäre, als PK die Kombination aus ArtikelID/KategorieID, und als Identität eine zusätzliche Spalte RelationID (verbietet einem ja keiner, wenn man glaubt, dass man sie benötigt) einzuführen. Denn die ID der Beziehung ist nur eine Eigenschaft der Beziehung, aber nichts, was sie definiert.

Gruß,

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)

5.299 Beiträge seit 2008
vor 14 Jahren

Jo, du hast recht. Ist mir bisher zwar noch nix mit passiert, aber issn Argument.👍

Der frühe Apfel fängt den Wurm.

andreas-82 Themenstarter:in
42 Beiträge seit 2006
vor 14 Jahren

N'abend allerseits!
Wow, danke schonmal direkt der starken Resonanz hier 🙂
Also der Reihe nach 👅:

@ErfinderDesRades

Also son DataAdapter checkt ja selber, was zu updaten, inserten, deleten ist. Da werde ich eine Deibel tun, das ganze je nach Rowstate und was wieder auseinanderzuklamüsern.
Ich hab mir son DatasetAdapter gebastelt. Der kriegt mein Highlander-Dataset ("es kann nur eines geben"), und speichert, was zu speichern ist, und das wars dann auch schon mittm DAL
...
...
Äh - es geht um Kochrezepte?
Unter welchen Umständen bekommt man da einzelne Tabellen aus der DAL ins BL?
Geht sowas überhaupt - die Dinger sind doch mittnander verknüppert?

Hmm, ok irgendwie ist da was dran... also 'Gedanken-Planänderung':
-> Eine Zentrale Methode in dem DAL in der ich einfach mein DS reinwerfe quasi.
Welche Tables bzw. Rows eine Änderung besitzen, weiß das DS ja selber (RowState). Dazu weiter unten mehr...

Und ganz wichtiger Pattern ist auch: Kirche im Dorf lassen.

Wie gesagt, mir gehts hier in meiner Anwendung primär darum, mich in die Schichtentrennung und Datenklassen (richtig) einzudenken und umzusetzen. Ich stell mir z.B. vor, was wäre wenn ich jetzt 'zig Millionen' Datensätze gespeichert HÄTTE, etc. Da würd ich bspw. besser nicht einfach das gesamte DS auf einmal via SELECT * FROM aus der Datenbank laden 😉
Daher auch die Überlegung mit Merge()

Aber super-coolen Tabellen-Designer hasteda - was ist das, wo ist der her?

M@crotron hat's bereits 'verraten'. Das Zauberwort heißt SQL Server Management Studio 😉

Zum "richtigen und perfekten" Proggen ist mir noch was eingefallen:
Erstmal gefällt mir dein Datenmodell ausgezeichnet, würde bei mir strukturell dasselbe bei rauskommen.

Danke 🙂

Nur zusammengesetzte PrimKeys lehne ich für mich ab (also ist mir bisher gelungen, ohne auszukommen)...

... hat sich ja denke ich durch die wunderbare Erklärung von LaTino erübrigt, oder spricht sonst etwas gegeben zusammengesetzen Primärschlüssel? 🤔

Dann kann man noch anne Benamung rumbosseln: tbl-Prefix ist ziemlich ungünstig

Naja gut, sind wirklich meistens Tabellen drin, in so einer Datenbank 👅

@JuyJuka

Das wichtigste ist, dass möglichst wenig nach passendem Wort such "Steuerung" aus der Datenschicht in der Logikschicht landet.
Ich gebe einfach alles (DataRow, DataTable, DataSet, etc.) was ich speichern will nach "unten" und gut ist.
Da her sollte es so wenig Methoden und Objekte in der "Signatur" der Datenschicht geben wie möglich

Danke für die Erklärung 🙂
Ich werde mir in anderen Projekten meine BusinessObjects auch mal mit komplett eigenen Klassen zusammenbasteln. Dann kann ich Interfaces mit Sicherheit hervorragend einbauen!

Ich möchte noch darauf hinweisen, das DataTable/DataRow/DataSet nicht die einzige Möglichkeit sind. Ich persönlich würde einen O/R Mapper DataSet/~Table/~Row vorziehen, da (meiner Meinung nach) diese Klassen gegen das OOP-Grundprinzip der Kapselung verstossen. (Aber dazu kann man mit der Forumssuche einige Diskusionen finden ... :::

Irgendwie wusste ich das das kommt G
Ne im ernst, ich interessiere mich für alle möglichen Technologien und hab mir auch schon viele Threads (und Blogs 😉) dieser Art durchgelesen, nur bei einer Sache muss ich ja anfangen 👅
Wenn mein Verständniss um der Umgang mit ADO.net kein Problem mehr ist, widme ich mich mal NHibernate und/oder seinen Geschwistern 🙂

@Rainbird

Übergabe "By Reference" ist nicht üblich und sollte auch nicht zum Einsatz kommen... Das kann zu unbeabsichtigtem Verhalten führen. Wenn z.B. in der Aufgerufenen Funktion eine "By Reference" übergebene DataSet-Variable auf null gesetzt wird, ist die Variable im Gültigkeitsbereich des Aufrufers ebenfalls null, da beide die Selbe Referenz verwenden

Deine Erklärung der Nachteile von 'by reference' sind mir (jetzt) auch schlüssig. Nur viel mir spontan kein besserer Weg ein.

Aber mal sehen ob ich lansam auf den richtigen Weg komme:
Die DataRows werden doch z.B. im DAL auf Veränderungen überprüft (RowState) und bei erfolgreichem Update() die AcceptChanges() aufgerufen, richtig?
Da ich aber eine Kopie meines DataSet-Objektes (by value) übergebe, muss ich nun die AcceptChanges() - Methode selber aufrufen.
Ich würde eine Methode benutzen, die mir die Anzahl der geänderten Zeilen zurückgibt. Diese werden dann mit den zu ändernden Zeilen des DataSets der BL verglichen und bei Erfolg ein AcceptChanges() aufgerufen.
Bei Mißerfolg - hmm 🤔
'Kommunikation' der Schichten über das RowUpdated() Event?

Generell hätte ich also zum Programmstart 1 DataSet, in welches ich mir zunächst nur Zutaten, Einheiten und Kategorien komplett reinlade.
Die Daten aus tblRezept und der Verknüpfungstabelle tblZutatRezept KÖNNTE man doch nach und nach dazuladen, also nur jedes Mal, wenn aus einer Rezeptübersicht ein Rezept komplett angezeigt werden soll, oder? (Stickwort: Merge()). Hierzu die Tabelle besser vorher leeren?

Also immer her mit den Verbesserungsvorschlägen! 🙂

Ps. Die Sache mit den verteilten Transaktionen und System.Transactions muss ich etwas nach hinten verschieben. Hab da bisher noch nie was mit gemacht, steht aber in meinem ado.net-Buch was von drin.

~ There's no knowledge that is not power~

5.299 Beiträge seit 2008
vor 14 Jahren

N'abend allerseits!
Wow, danke schonmal direkt der starken Resonanz hier 🙂
Also der Reihe nach 👅:

@ErfinderDesRades

Also son DataAdapter checkt ja selber, was zu updaten, inserten, deleten ist. Da werde ich eine Deibel tun, das ganze je nach Rowstate und was wieder auseinanderzuklamüsern.
Ich hab mir son DatasetAdapter gebastelt. Der kriegt mein Highlander-Dataset ("es kann nur eines geben"), und speichert, was zu speichern ist, und das wars dann auch schon mittm DAL
...
...
Äh - es geht um Kochrezepte?
Unter welchen Umständen bekommt man da einzelne Tabellen aus der DAL ins BL?
Geht sowas überhaupt - die Dinger sind doch mittnander verknüppert?
Hmm, ok irgendwie ist da was dran... also 'Gedanken-Planänderung':
-> Eine Zentrale Methode in dem DAL in der ich einfach mein DS reinwerfe quasi.
Welche Tables bzw. Rows eine Änderung besitzen, weiß das DS ja selber (RowState). Dazu weiter unten mehr...
Und ganz wichtiger Pattern ist auch: Kirche im Dorf lassen.
Wie gesagt, mir gehts hier in meiner Anwendung primär darum, mich in die Schichtentrennung und Datenklassen (richtig) einzudenken und umzusetzen.
Ich stell mir z.B. vor, was wäre wenn ich jetzt 'zig Millionen' Datensätze gespeichert...

Joa, da habichnich so dolle Erfahrungen mit gemacht. Ich hab mal gedacht, ich müsste den MVC-Pattern mal implementieren. Hat meine Solution auf doppelte Größe aufgeblasen, ist aber dadurch nicht wirklich übersichtlicher geworden.
Ja, und iwann lesich grad in diesem Forum: MVC und so ist auf Controls garnicht wirklich anwendbar. Weil in den Controls die Controller-Schicht ja quasi fest eingebaut ist, deswegen heißense ja auch "Controls".

Dann habichmirmal auf CodeProject mirn Beispiel zur n-tier-Architektur geladen. War entwedern schlechter Artikel, oders ist einfach nicht immer sinnvoll.
Jedenfalls waren da ca. 2000 Zeilen Code, für eine Funktionalität, die ich bestimmt in 300 Zeilen hingekriegt hätte.
Das ist dann doch nicht mehr sauber, auch wenns nach den Pattern-Regeln evtl. total blitzblank ist!
Also denkichmir, sone Monster-Technologie kann man sich so richtig erst aneignen, wenn man auch ein dafür passendes Monster-Problem zu lösen hat.
Andernfalls läufste Gefahr, voll die Wasserköpfe zu produzieren.

Leider findich den Artikel nicht mehr 🙁

Hey, aber vllt. kriegstes ja hin, son richtig schnuckeliges 3-tier-Kochbuch zu proggen!
Das könnteste dann doch inne Projekte einstellen, das tätichmir gerne mal angucken, da lernich bestimmt was.

Da würd ich bspw. besser nicht einfach das gesamte DS auf einmal via SELECT * FROM aus der Datenbank laden 😉
Daher auch die Überlegung mit Merge()

Generell hätte ich also zum Programmstart 1 DataSet, in welches ich mir zunächst nur Zutaten, Einheiten und Kategorien komplett reinlade.
Die Daten aus tblRezept und der Verknüpfungstabelle tblZutatRezept KÖNNTE man doch nach und nach dazuladen, also nur jedes Mal, wenn aus einer Rezeptübersicht ein Rezept komplett angezeigt werden soll, oder? (Stickwort: Merge()). Hierzu die Tabelle besser vorher leeren?

Also immer her mit den Verbesserungsvorschlägen! 🙂

Kommich nochma mittm DatasetAdapter . Der hat sog. "incrementelle Befüllung" als Feature.
Der merget aber nicht, sondern füllt fehlende Zeilen direkt in die vorhandene DataTable.
In dem Forum-Thread dazu wird auch der TableAdapterManager erwähnt, den lohntes sich wohl auch, mal zu probieren.
Ich hab mich ja mit meinem DatasetAdapter auf clientseitige Primkey-Generierung festgelegt, aber immer bisserl doofes Gefühl dabei, weil annere Leuts halt voll auf Serverseitig schwören.

Der frühe Apfel fängt den Wurm.

3.728 Beiträge seit 2005
vor 14 Jahren
Wo ist der Status

Die DataRows werden doch z.B. im DAL auf Veränderungen überprüft (RowState) und bei erfolgreichem Update() die AcceptChanges() aufgerufen, richtig?
Da ich aber eine Kopie meines DataSet-Objektes (by value) übergebe, muss ich nun die AcceptChanges() - Methode selber aufrufen.
Ich würde eine Methode benutzen, die mir die Anzahl der geänderten Zeilen zurückgibt. Diese werden dann mit den zu ändernden Zeilen des DataSets der BL verglichen und bei Erfolg ein AcceptChanges() aufgerufen.

Du hast das mit dem "By Value" noch immer nicht richtig verstanden. Das DataSet wird nicht im Speicher kopiert, wenn Du es "By Value" übergibst. Es wird nur die Referenz auf den Speicherbereich kopiert.
Wenn Deine Anwendung nicht in Tiers (Schichten auf mehrere OS-Prozesse verteilt) sondern nur in Layers (Alle Schichten im selben OS-Prozess) aufgeteilt ist, gibt es zu jeder Zeit nur EIN einziges DataSet im Speicher, mit dem alle Schichten arbeiten. Wenn Du eine n-Tier-Anwendung hast, gibt es auch nur auf dem Client DAUERHAFT ein DataSet, da BL und DAL statuslos sein sollen und nur pro Aufruf ein DataSet temporär im Speicher des jeweiligten OS-Prozesses erzeugt und nach Ende des Aufrufs wieder verworfen wird.

AccecptChanges musst Du eigentlich nie aufrufen, da das bereits der DataAdapter oder TableAdapter für Dich tut.

'Kommunikation' der Schichten über das RowUpdated() Event? Auf keinen Fall! BL und DAL sollten statuslos sein. Das DataSet wird vom Client bzw. der Präsentationsschicht gehalten und z.B. zum Speichern/Buchen etc. an die BL übergeben. Wenn Daten abgerufen werden, geben die entsprechenden Funktionen der BL ja DataSets/DataTables zurück. Deren Daten werden via Merge in Client-DataSet eingepflegt. Wenn man z.B. die Benutzeroberfläche aktualisieren möchte (d.h. alle Daten neu von der DB abrufen und anzeigen), kann es zur Sicherheit Sinn machen, vor dem Merge auch noch Clear aufzurufen. Eine Undo-Funktion kannst Du sehr leicht mittels RejectChanges realisieren.

andreas-82 Themenstarter:in
42 Beiträge seit 2006
vor 14 Jahren

Du hast das mit dem "By Value" noch immer nicht richtig verstanden. Das DataSet wird nicht im Speicher kopiert, wenn Du es "By Value" übergibst. Es wird nur die Referenz auf den Speicherbereich kopiert.

Versteh mich nicht falsch, ich wollte schon gerade ein Bsp. mit nem String hier posten, der einer Methode übergeben wird per value um zu zeigen, das dieser eine Kopie ist.
Vorher aber zum Glück noch dasselbe Bsp. mit nem DS, bzw. ner Klasse gemacht:
Man ist ja klar, das einzige was ne Kopie ist, ist die Objektreferenz.
Man wie peinlich X( - Amateur!

... räusper ... Schwamm drüber, das macht die ganze Sache auf jeden Fall wesentlich einfacher G

AccecptChanges musst Du eigentlich nie aufrufen, da das bereits der DataAdapter oder TableAdapter für Dich tut.

🙂 ...und zwar hab ich da auch in meiner BL was von, wie ich jetzt weiß G

Zitat von andreas-82:
'Kommunikation' der Schichten über das RowUpdated() Event?

Auf keinen Fall! BL und DAL sollten statuslos sein. Das DataSet wird vom Client bzw. der Präsentationsschicht gehalten und z.B. zum Speichern/Buchen etc. an die BL übergeben. Wenn Daten abgerufen werden, geben die entsprechenden Funktionen der BL ja DataSets/DataTables zurück.

Sehr schön, das kann ich mir dann auf jeden Fall merken:

  • Kein call by reference
  • Statuslosigkeit der BL und DAL

Danke nochmal für die Aufklärung 🙂

~ There's no knowledge that is not power~