Laden...

typed oder untyped Dataset

Erstellt von John Sanson vor 16 Jahren Letzter Beitrag vor 16 Jahren 22.636 Views
John Sanson Themenstarter:in
195 Beiträge seit 2006
vor 16 Jahren
typed oder untyped Dataset

Moin.

Ich arbeite zur zeit ein ASP.Net Buch und bin schon öfter auf die Begriffe typisiertes und untypisiertes DataSet gestoßen. Könnte ihr mir sagen was das genau bedeutet, wann mán welches benutzt und wo die unterschiede liegen.

danke

M
303 Beiträge seit 2006
vor 16 Jahren

Bei einem typisierten DataSet kannst werden deine Zellen durch Properties dargestellt, welche natürlich typsicher sind, und somit Fehler beim Programmieren vermeiden. Untypisierte, herkömmliche DataSets bieten dies nicht, hier kannst du auf die Zellen lediglich durch Collections zugreifen, welche dir object-Typen zurückliefern.

Wie du dir vorstellen kannst, ist für ein typisiertes DataSet einen ganzen Batzen Codegenerierung nötig, wobei wir auch schon bei den Nachteilen wären. Typisierte DataSets sind langsamer und die Codegenerierung ist hierbei nicht sonderlich sauber. Dafür bieten sie einen höheren Komfort beim Programmieren durch IntelliSense und typsicherheit.

Es gab hier in der Vergangenheit auch Threads zum Thema. Bemühe bitte die Forensuche.

3.825 Beiträge seit 2006
vor 16 Jahren

Hallo Marco,

jemand hier im Forum (ich glaube Rainbird) halt mal gesagt :

Man arbeitet zuerst mit Untyped Datasets, dann mit Typed Datasets, dann mit OR-Mappern, und dann wieder mit Untyped Datasets.

Ich habe Typed DS getestet und fand die echt die Katastrophe. MS selbst unterstützt im VS2003 eher untyped, im VS 2005 eher typed DS. Mal sehen was in VS2008 kommt 😉

Inzwischen arbeite ich weder noch sondern mit selbsterstellten Dataobjects die mir die Vorteile aus beiden Welten bringen. Und dazu noch schnell sind. Mit LINQ soll sowas ja auch für VS208 kommen.

Mein Tipp : Fang besser mit Untyped DS an, da bist Du flexibler.

Grüße Bernd

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

2.187 Beiträge seit 2005
vor 16 Jahren

Ich versuchs mal:

Instanzen der Klasse System.Datat.DataSet sind sogenannte untypisierte DataSets, diese enthlaten Instanzen von System.Data.DataTable und diese wiederum System.Data.DataColumn-Objekte und System.Data.DataRow-Objekte.
Somit ist ein DataSet die Abbildung einer Datenbank innerhalb von .NET Programmen.
Sie werden "untypisiert" genannt, da jede Spalte (System.Data.DataColumn) ihren Wert als object zurück gibt und der Typ nicht beachtet wird.

Typisierte DataSets hingegen sind Ableitungen von System.Data.DataSet, die Ableitungen von System.Data.DataTables und diese wiederum Ableitungen von System.Data.DataRow und System.Data.DataColumn enthalten.
Dadurch stehen die einzelnen Tabellen, Spalten und auch die Werte mit einem konkreten Typ zur Verfügung (z.B. string,int oder System.DateTime).

Zur Verwendung:
Ich (und ich spreche nur für mich) HASSE DataSets und zwar beide Arten. Sie sind für den Zugriff auf die Datenbank sehr gut, aber sollten in der eigentlichen Funktionsschicht nicht mehr auftauchen und erst recht nicht in der UI!
DataSets verschleiern jeglichen Zugriff auf die Daten und können vom Compiler nicht korrekt überprüft werden. Und die vorgetäuschte Sicherheit durch typisierte DataSets ist auch nuch Fack! Man kann immer noch wie willd auf ein typisiertes DataSet zugreifen, als wäre es untypisiert!
Schaf dir eine Datenbank-Zugriffsschit an, die aus den DataSets echte Bussines-Objekte macht und verwende die DataSets wirklich nur für das wofür Sie entwickelt wurden: Datenbankzugriff.

Gruß
Juy Juka

M
303 Beiträge seit 2006
vor 16 Jahren

Hallo Bernd,

ich habe die Frage nicht gestellt.

1.564 Beiträge seit 2007
vor 16 Jahren

Hallo John

Hier ein kleines Beispiel für eine typisierte DataTable:

      public Program()
      {
         MyDataTable dt = new MyDataTable();
         MyRow row = dt.NewRow();
         row.MyCol1 = "Hello World";
         row.MyCol2 = 123;
         dt.Rows.Add(row);
      }

      class MyDataTable : DataTable
      {
         public MyDataTable()
         {
            Columns.Add("MyCol1", typeof(string));
            Columns.Add("MyCol2", typeof(int));
         }

         public new MyRow NewRow()
         {
            return (MyRow)base.NewRow();
         }

         protected override DataRow NewRowFromBuilder(DataRowBuilder builder)
         {
            return new MyRow(builder);
         }
      }

      class MyRow : DataRow
      {
         public MyRow(DataRowBuilder builder)
            : base(builder)
         {
         }

         public string MyCol1
         {
            get { return (string)base["MyCol1"]; }
            set { base["MyCol1"] = value; }
         }
         private string myVar;

         public int MyCol2
         {
            get { return (int)base["MyCol2"]; }
            set { base["MyCol2"] = value; }
         }
      }

Ich schließe mich speziell JuyJuka's Meinung an. DataSets/DataTables sind okay um sich Daten aus der Datenbank zu holen oder sie mittels Commands wieder zurückzuschreiben, aber um damit weiter zu arbeiten... no way! Business-Layer sind hier die Wahl. Nur mit solchen lassen sich komplexe Objektstrukturen kapseln und Sachen wie effektieves Caching oder optimierte Memory-Queries für Stammdaten realisieren (und noch vieles mehr...).

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

3.825 Beiträge seit 2006
vor 16 Jahren

Hallo Marco : Sorry, Dich hab ich als erstes gesehen !

😉

Juy : Ich hasse auch beide Datasets.

John : Für den Anfang kann ich Dir Untyped Datasets empfehlen, bei größeren Projekten benötigst Du unbedingt eine Zwischenschicht wie schon gesagt wurde.

Beispiel :


// Untyped Dataset
string name = (string)ds.Tables["Adressen"].Rows[0]["Name"];
// Typed Dataset
string name = dsAdressen.RowAdr[0].Name;

Grüße Bernd

Workshop : Datenbanken mit ADO.NET
Xamarin Mobile App : Finderwille Einsatz App
Unternehmenssoftware : Quasar-3

3.728 Beiträge seit 2005
vor 16 Jahren
typisiert oder nicht?

Typisierte DataSets verwende ich als Datencontainer für Daten, auf die CRUD-Operationen (Create, Read, Update, Delete) angewendet werden. Das sind in der regel Resultsets die alle Spalten einer bestimmten Tabelle enthalten. Typisierte DataSets sind einfach einzusetzen und vermeiden Tippfeheler von Spalten- oder Tabellennamen. Außerdem entfallen die lästigen Typenumwandlungen.

Untypisierte DataSets verwende ich wenn mehrere untypisierte DataTables auf einen Rutsch zurückgeben will. Das kommt ziemlich selten vor (Da für den Großteil der Anwendungsfälle typisierte DataSets eingesetzt werden).

Was hingegen häufig vorkommt sind Abfrageergebnisse, die nur einen Teil der Spalten einer Tabelle oder Spalten aus unterschiedlichen Tabellen enthalten (z.B. wenn man Such- und Filterfunktionen in seine Anwedung einbaut). Für diese Dinge benötigt man viel Flexibilität. Typisierte DataSets/DataTables sind zu starr. Also müssen für dynamische Nur-Lese-Abfragen untypisierte DataTables herhalten.

Es gibt auch hier keine Pauschalempfehlung. Keines ist besser als das andere. Die verschiedenen Klassen haben nur unterschiedliche Stärken und Schwächen. Kluge Leute setzen immer die Technologie für eine bestimmte Aufgabe innerhalb einer Anwendung ein, die dafür am besten passt. Da typisierte und untypisierte DataSets/DataTables auch noch sehr eng miteinander verwandt sind, wäre ein Entweder-Oder Unsinn.

2.187 Beiträge seit 2005
vor 16 Jahren

@Rainbird: Ich muss leider wiedersprechen.
Man sollte sich an eine einheitliche Linie in seinen Projekten halten. Wenn man verschiedene Technologien mischt, wird der Code sehr schwer lesbar und wart bar.
Außerdem kann man die einzelnen Teile der Software nicht mehr so schnell kombinieren und muss hässlichen zwischen-code schreiben, um sie wieder zusammen zu "frigeln".

Und das schliemste ist, wenn DataTable(s) (~Set(s), o.Ä.) zurück gegeben werden, da sich in einem dieser Objekte ziemlich viel unterschiedliches zeug befinden kann und man unzählige Laufzeitfehler abprüfen müsste und unter garantie irgend was vergisst. schauder Sowas hatten wir in unserer Software früher auch und es war die Hölle! (ohne Übertreibung oder sowas).

Gruß
Juy Juka

3.728 Beiträge seit 2005
vor 16 Jahren
mysteriös

Original von JuyJuka
Wenn man verschiedene Technologien mischt, wird der Code sehr schwer lesbar und wart bar. Außerdem kann man die einzelnen Teile der Software nicht mehr so schnell kombinieren und muss hässlichen zwischen-code schreiben, um sie wieder zusammen zu "frigeln".

Es sind keine unterschiedlichen Technologieen. Typisierte DataSets/DataTables sind ja von den untypisierten Standardklassen abgeleitet. Es handelt sich also lediglich um eine Erweiterung. Ob man Komponenten gut und schnell kombinieren kann, hat mit der Art der Datentransferobjekte überhaupt nichts zu tun, sondern die Schnittstellen der Komponenten. Typisierte DataSets sind da sogar sehr von Vorteil, da sie eine explizite serialisierbare Schnittstelle darstellen, die von verschiedenen Komponenten/Diensten gemeinsam zum Datenaustausch genutzt werden können. Deshalb sollten die Definitionen (XSD + Code) von typisierten DataSets in separate Schnittstellen-Assemblies gepackt werden.
Ich sehe auch keine Notwendigkeit irgendwelchen Zwischencode zu schreiben. Was steht denn in so einem Fickel-Zwischencode drin? Hast Du da ein Beispiel?

Original von JuyJuka
Und das schliemste ist, wenn DataTable(s) (~Set(s), o.Ä.) zurück gegeben werden, da sich in einem dieser Objekte ziemlich viel unterschiedliches zeug befinden kann und man unzählige Laufzeitfehler abprüfen müsste und unter garantie irgend was vergisst.

Was für Zeug? Meinst Du vielleicht die Originaldaten, bevor AcceptChanges aufgerufen wurde? Wieso muss man bei DataSets/DataTables unzähliche Laufzeitfehler abrpüfen? Sorry, aber da kann ich Dir nicht folgen. Was für Fehler?

2.187 Beiträge seit 2005
vor 16 Jahren
  1. ZwischenCode:
    Wenn man von einem Codeabschnitt, der untypisierte DataSets verwendet, einen Codeabschnitt (ich vermeide mit absicht das Wort Komponente) verwenden soll, muss man das typisierte DataSet erzeugen und aus dem untypisierten DataSet füllen. Das gleiche Problem hat man bei wechsen von Bussines-Objekten zu DataSets oder umgekehrt. (Ein Beispiel macht mir jetzt zuviel arbeit.)
    Außerdem ist der Frickel-Code nur ein Nachteil, dass man beim Warten überhaupt nicht zurecht kommt ist viel schlimmer.

  2. Wenn eine Methode ein DataSet zurück gibt, kan dieses DataSet fast jeden Aufbau haben. Mann müsste die Existenz von jeder Tabelle und jeder Spalte überprüfen. Mann müste den Typ jedes Werts in jeder Spalte prüfen.
    Mit typed DataSets ist alles schon viel besser aber immer noch sehr Anfällig, es könnten vom Entwickler viele Fehler gemacht werden, die echte Busines-Objekte abfangen würden.

Ich kann mir für DataSets nur eine Verwendung vorstellen: Komunikation mit externen Systemen (Webservices, Datenbanken, nicht .NET Komponenten).

Gruß
Juy Juka

3.728 Beiträge seit 2005
vor 16 Jahren
Da kann ich nicht zustimmen

Zu 1:

Es kommt in der Praxis nicht vor, dass man untypisierte DataSets in typisierte DataSets umwandeln muss, da typisierte und untypisierte DataSets für unterschiedliche Zwecke eingesetzt werden. Um. z.B. eine Übersichtsliste aller offenen Angebote abzurufen, verwendet man eine einfache DataTable. Um ein Angebot (welches man z.B. aus der Liste ausgewählt hat) mit seinen Positionen zu laden, verwendet man ein typisiertes DataSet. Es besteht also garkeine Notwendigkeit, solchen Zwischencode zu schreiben. Gut, das war jetzt nur ein Beispiel, aber ich bin mir fast sicher, dass Du kein konkretes Beispiel nennen kannst, bei dem man wirklich untypisierte in typisierte DataSets umwandeln muss.

Zu 2:

Es ist der große Vorteil von untypisierten DataSets/DataTables, dass der Aufbau völlig flexibel ist. Für dynamische Listen etc. ist das perfekt. Da müssen selbstdefinierte Objekte z.B. komplett passen, die sind nämlich immer starr.
Sollen Daten allerdings nicht nur gelesen, sondern auch geschrieben werden, ist eine starre Struktur besser. Ein Angebot, um bei dem Beispiel zu bleiben, hat immer die selben Eigenschaften und nicht plötzlich übernacht zwei Spalten mehr. Ob man nun selbstgetippte Objekte oder typisierte DataSets verwendet, ist letztendluch Geschmacksache. Fakt ist aber, dass DataSets/DataTables (vor allem in Kombination mit DataViews) viel mehr auf dem Kasten haben, als selbstgetippte Objekte. Ein paar Beispiele:*DataTable.Select ermöglicht eine parametrisierte Abfrage in SQL-Syntax auf die Daten im Hauptspeicher (Bei Objekten müsste man Schleifchen dafür schreiben) *DataSets/DataTables haben eine "Rückgängig" Funktionen (RejectChanges), die alle Änderungen verwirft und die ursprünglich abgerufenen Datensätze wiederherstellt (Wie machen das Deine Objekte?) *Typisierte DataTables können sehr einfach an Steuerelemente gebunden werden (z.B. ein DataGridView) und man kann auch neue Zeilen zufügen (ohne Code dafür schreiben zu müssen); Wie machen das die Objekte?

Mit typed DataSets ist alles schon viel besser aber immer noch sehr Anfällig, es könnten vom Entwickler viele Fehler gemacht werden, die echte Busines-Objekte abfangen würden.

Was genau ist Anfällig und wie machen das die Business-Objekte besser?

Bei Java-Entwicklern verstehe ich die Begeisterung für OR-Mapper. Die haben eben kein ADO.NET. Aber mit .NET ... ?

T
38 Beiträge seit 2007
vor 16 Jahren

Hallo Juy (und BerndFfm)
Du schreibst:
Schaf dir eine Datenbank-Zugriffsschit an, die aus den DataSets echte Bussines-Objekte macht und verwende die DataSets wirklich nur für das wofür Sie entwickelt wurden: Datenbankzugriff.

Sehr deiner Meinung was die saubere Schichtenarchitektur betrifft. ABer als .NET Newcomer laß mich fragen, wie Du das unter .NET machst. Wie und in welcher Form reichst Du dann die Daten an das Presentation tier hoch, um damit z.B. in einem DataGridView als DataSource zu arbeiten?

Ich hab mal ein Framework gesehen (Mere Mortals - siehe www.oakleafsd.com), die grundsätzlich von vornherein streng mit Schichtentrennung arbeiten und ein zentrales Business tier anlegen, wo alle BO's drin sind. Das eigene DataGridView dieses Frameworks verwendet eine BindingSource-Eigenschaft, wo man das BO angibt. In Laufzeit wird dann aber doch wieder ein DataSet in die DataSource geschrieben. (Es gibt auch noch ne andere Wahl, die hab ich noch nicht so ganz erforscht).

Wie würdest Du's machen? In welcher Form stellst Du die Daten dann der Oberfläche z.B. in Windows Forms zur Verfügung? Danke für Erklärung...

(zur Einordnung: Bin Profi in Visual FoxPro und Anfänger in C#, der für alle Hilfen dankbar ist. VFP: eine in vielen Bereichen unschlagbare Sprache, allerdings vieles sehr anders. 1. Hybrid (neben oo auch noch prozedurale Elemente), 2. Late Binding, 3. Weak Typing.)

1.564 Beiträge seit 2007
vor 16 Jahren

Hallo Triumph of Truth

Um eine Saubere Anbindung an das DataGridView zu bekommen...

Deine Liste (die DataSource) sollte folgende Interfaces implementieren:
* IList (welch Wunder... 😉)
* IBindingList
Durch dieses Interface stellt du unter anderem die Möglichkeiten für Sortiren und Suchen zur Verfügung.
Zusätzlich enthält das Interface ein Event welches das DataGridView informiert wenn sich die Liste geändert hat
* ITypedList
Dieses Interface benötigst du dann, wenn das DataGridView auch in der Lage sein soll die Spalten dynamisch aufzubauen, wenn keine Objekte in der Liste sind.
Wenn die Liste Objekte enthällt holt sich das DataGridView die Informationen über die möglichen Felder aus dem TypeDescriptor des ersten Objekts (falls dieses Interface nicht vorhanden ist).

Die Daten-Objekete sollten folgende Interfaces implementieren:
* IEditableObject
Darüber informiert das DataGridView z.B. ein Objekt wenn es durch "Escape" wieder zurückgesetzt werden soll.

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

T
38 Beiträge seit 2007
vor 16 Jahren

Aha. Das heißt erst mal vom Grundansatz (richtig verstanden?) ich baue mir meine eigene Datenklasse, die diese Interfaces implementiert, hänge sie per Aggregation an mein BO dran und mache sie dann programmatisch zur DataSource des DataGridView - so etwa richtig gesagt?
Oder mache mein BO Basisklasse direkt zu Datenspeicher und gebe dem BO selbst diese Interfaces?

(zur Einordnung: Bin Profi in Visual FoxPro und Anfänger in C#, der für alle Hilfen dankbar ist. VFP: eine in vielen Bereichen unschlagbare Sprache, allerdings vieles sehr anders. 1. Hybrid (neben oo auch noch prozedurale Elemente), 2. Late Binding, 3. Weak Typing.)

T
38 Beiträge seit 2007
vor 16 Jahren

Noch ne Anmerkung / Frage dazu:
Kann mir nicht vorstellen, daß Du sowas dann jedes mal neu zusammen bastelst. Machst Du Dir also eine Basisklasse mit diesen Fähigkeiten und verwendest sie dann zur DAtenbindung an der Oberfläche?

(zur Einordnung: Bin Profi in Visual FoxPro und Anfänger in C#, der für alle Hilfen dankbar ist. VFP: eine in vielen Bereichen unschlagbare Sprache, allerdings vieles sehr anders. 1. Hybrid (neben oo auch noch prozedurale Elemente), 2. Late Binding, 3. Weak Typing.)

1.564 Beiträge seit 2007
vor 16 Jahren

Hallo

Wenn deine List-Source die genannten Interfaces implementiert kannst du sie direkt als DataSource an das DataGridView anbinden:
myDataGridView.DataSource = myListSource;

Ich habe natürlich Basis-Objekte (eine BoList und ein BoObject) welches die genannten Interfaces implementiert. Wenn du das auch machen willst mußt du noch was beachten:
Dar dein basis BoObject natürlich nicht die Felder (Spalten) der eigentlichen Objekte kennt/hat mußt du einen TypeDescriptionProvider, einen TypeDescriptor und einen PropertyDescriptor schreiben, damit das Grid über Reflection auf die Felder zugreifen kann.

Die Datenbindung ist dann, wie gesagt, einfach über die DataSource-Property des Grids.

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

T
38 Beiträge seit 2007
vor 16 Jahren

Aha Aha, jetzt wird mir einiges klarer...
"wenn du das auch tun willst"...
naja - scheint ja so der einzig saubere Weg zu sein. Daß ich das DataSet nicht direkt an der Oberfläche verwendet ist mir schon plausibel.

Aber da gibt's doch bestimmt X Frameworks, die sowas sauber zuendeprogrammiert haben, oder? (obwohl ich mir vorstellen könnte, daß Frameworks so "sauber" häufig auch gar nicht sind).

(zur Einordnung: Bin Profi in Visual FoxPro und Anfänger in C#, der für alle Hilfen dankbar ist. VFP: eine in vielen Bereichen unschlagbare Sprache, allerdings vieles sehr anders. 1. Hybrid (neben oo auch noch prozedurale Elemente), 2. Late Binding, 3. Weak Typing.)

F
10.010 Beiträge seit 2004
vor 16 Jahren

@Rainbird:

Die begeisterung für ORMapper kommt meistens dann auf, wenn du dich mit
mehr als einem SQL-Dialekt herumschlagen musst.

Wenn dir der ORMapper dann noch das Anlegen/Updaten der TabellenSchemata
vereinfacht, und Trigger/Validation bietet, wird es ziemlich schnell komfortable.

Dann noch einen "einfachen" generator für vorhandene Tabellen und du wirst
sehen, auch Du wirst dann manchmal denken, das das hilfreich ist 😉

1.564 Beiträge seit 2007
vor 16 Jahren

@Triumph of Truth

obwohl ich mir vorstellen könnte, daß Frameworks so "sauber" häufig auch gar nicht sind

Ich habe eben auch fast keine BO-Library gefunden die das richtig implementiert oder die Libraries waren unzahlbar.

Deswegen habe ich mir mittlerweile eine eigene geschrieben die zusätzlich noch einige Sachen mehr macht (z.B. Objekt-Orientierte Queries, Caching, Client-Transaktionen, Serialisierung, ...)

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

2.187 Beiträge seit 2005
vor 16 Jahren

Hallo @All,

Bei uns bin ich nicht für den Übergang von der Funktionsschicht zur Presentationsschicht zuständig. Daher hab ich keine Ahnung von DataBinding lach und am Hinterkopf kratz.
Aber dank der Informationen von Oxygen werd ich mein Framework (ein OR-Maper) erweitern.

@Rainbird: DataSets (egal ob typisiert oder nicht) bieten immer vollkommen freien Zugriff auf alle Felder mit Indexern. Man kann sogar Spalten hinzufügen und entfernen!

Punkt 1: Sollten wir abhacken, ich wollte eigentlich nur sagen: Mann sollte unabhänig von der gewählten Technik bei dieser einen bleiben, damit es lesbar und wartbar bleibt.

DataTable.Select ermöglicht eine parametrisierte Abfrage in SQL-Syntax auf die Daten im Hauptspeicher (Bei Objekten müsste man Schleifchen dafür schreiben)

SQL-Abfragen als string im Quellcode (oder in Resourcen oder sonst wo) finde ich absolut inakzeptabel! Alleine darüber könnte man einen eigenen Thread eröffnen!

DataSets/DataTables haben eine "Rückgängig" Funktionen (RejectChanges), die alle Änderungen verwirft und die ursprünglich abgerufenen Datensätze wiederherstellt (Wie machen das Deine Objekte?)

Zufällig hat mein Framework auch so eine Funktion (void Cancel()). lach

Typisierte DataTables können sehr einfach an Steuerelemente gebunden werden (z.B. ein DataGridView) und man kann auch neue Zeilen zufügen (ohne Code dafür schreiben zu müssen); Wie machen das die Objekte?

Mit DataBinding von Microsoft, wobei ich da nicht so bewandert bin, da ein Kollege für die Anbindung an die UI verantwortlich ist.

Ich finde ADO.NET ganz ok, aber nicht zur Verwendung als Business schicht. Dazu kann man als Verwender zuviele Fehler machen und die Kapselung ist in ADO.NET viel zu schwacht (was in andern Fällen ein Vorteil ist).

@Triumph of Truth: Du hast gefragt, wie unser UI-Schicht Daten/Objekte abruft. Dazu existiert in meinem Framework eine Namespace "ExtendetSelect", dessen Klassen aus Typ-Definitonen SQL-Abfragen erzeugt, diese auf der Datenbank ausführt und Objkte erzeugt.
Beispiel:


Aisys.ExtendetSelect.Select<Lagerplatz> select = Aisys.ExtendetSelect.SelectFactory.Create<Lagerplatz>();
select.Bedinung = new Aisys.ExtendetSelect.PropertyBedingung<Lagerplatz>("Status"); // Es gibt leider keinen Operator, daher muss das Property als string angegeben werden *kotz*
select.Objekt.Status = Status.Aktiv;
Lagerplatz[] ergebnisAusDerDatenbank = select.Execute();

Gruß
Juy Juka

3.728 Beiträge seit 2005
vor 16 Jahren
ADO.NET oder ADO.NOT?

Original von JuyJuka
@Rainbird: DataSets (egal ob typisiert oder nicht) bieten immer vollkommen freien Zugriff auf alle Felder mit Indexern. Man kann sogar Spalten hinzufügen und entfernen!

...

Ich finde ADO.NET ganz ok, aber nicht zur Verwendung als Business schicht. Dazu kann man als Verwender zuviele Fehler machen und die Kapselung ist in ADO.NET viel zu schwacht (was in andern Fällen ein Vorteil ist).

Ich glaube Du hast da was nicht richtig verstanden. ADO.NET ist eine Datenzugriffstechnologie. Man kann ADO.NET deshalb nicht als Geschäftsschicht verwenden.
Es macht nichts, dass man typisierten DataTables Spalten zufügen und entfernen kann. Wenn jemand eine Spalte entfernt und diese DataTable zur Weiterverarbeitung an die Geschäftsschicht sendet, wird er eine Ausnahme bekommen (davon ausgehend, dass die Geschäftssicht eingehende Daten validiert).
Außerdem kann man über Reflection auch auf sämtliche Felder (auch private) und Eigenschaften eines Objekts zugreifen.

Grundsätzlich sollte man auch zwischen statuslosen Objekten der Geschäftsschicht und Datenübertragungsobjekten (DTOs) unterscheiden. Bei den beiden Begriffen kann man sehr leicht aneinander vorbeireden. Mir ist noch nicht ganz klar, ob wir wenns um Business-Objects gehts, das selbe meinen (Business-Objects ist ein schwammiger Begriff).

Natürlich kann ein eigenes Datenzugriffs-Framework eine tolle Sache sein. Man muss aber auch den Aufwand sehen. Nicht alle Projekte sind so groß, dass man einen Entwickler dafür abstellen kann, der nur für die Datenzugriffskomponente zuständig ist.
Du solltest Dich auch dafür interessieren, was in der Präsentationsschicht mit Deinen Objekten gemacht wird (z.B. ob Datenbindung verwendet werden soll). Wie kannst Du ein effizientes Framework entwickeln, wenn Du nicht weisst, was die Kollegen damit anstellen?

Noch eine Frage zu Deinem Projekt. Handelt es sich dabei um eine verteilte Anwendung (bei der ein Applikationsserver zu Einsatz kommt, oder die Komponenten in verschiedenen Prozessen laufen)?

2.187 Beiträge seit 2005
vor 16 Jahren

Hi,

Die Aufteilung in "Statuslose Objekte" und "Datenübertragungsobjekte" halte ich für keinen guten Ansatz. Wenn man seine Bussinesschicht so aufteilt, sind DataSets natürlich genial. Aber in der OOP (oder so wie ich OOP gelernt habe) bilden Daten und Funktionen eine (≤Bethonung) Einheit. (DTO's kommen nur bei Netzwerk und Außer-.NET-Kommunikation zum einsatz.)

Meine Datenzugriffschicht ist nicht nur für ein Projekt entwickelt worden, sondern wird in allen .NET-Projekten unserer Firma benutzt. Daher ist es nicht wirklich ein Aufwand.

Unsere Anwendungen sind im Moment FatClients (UI und Businessschicht auf dem Client). Eine zusätzliche Abstraktionsschicht (Factory bzw. Abstract Factory) ist noch in Planung, um einen Wechsel zwischen FatClient und n-Tier Architektur zu ermöglichen, da wir je nach Kunden variieren können sollten.

Gruß
Juy Juka

3.728 Beiträge seit 2005
vor 16 Jahren
n-Tier

Datenstrukturen und die Datenzugriffslogik in einer Klasse zu haben, ist für Datenbank gestützte Business-Anwendungen kein guter Weg.

Warum?
Dieser Ansatz skaliert nicht gut und ist nicht gut verteilbar. Außerdem ist es so nicht möglich, Komponenten voneinander zu entkoppeln (Was bei jedem großen Projekt angestebt werden sollte). Jede Komponente die solche OOP-Datenklassen benutzt, hat automatisch Zugriff auf die Datenzugriffslogik. In einer verteilten mehrschichtigen Anwendung ist das undenkbar. Der Prozess (EXE) der Präsentationsschicht darf nicht selbständig auf die Datenbank zugreifen (Auch nicht über ein spezielles Framework), sondern muss dazu die Geschäftsschicht (die in einem anderen Prozess oder auch auf einem anderen Computer lebt) bemühen (z.B. CustomerDataSet GetCustomer(Guid customerID)). Wenn Datenstrukturen und Datenzugriffslogik aber in einer Klasse liegen, lässt sich die Datenzugriffslogik nicht auf einen Serverprozess auslagern, da sie mit den Datenstrukturen (Properties etc.) verheiratet ist.

Deshalb sollte es die Datenzugriffslogik von den DTOs getrennt sein. DTOs ist dann einfach passive Objekte die für die einzelnen Tabellenspalten Eigenschaften bereitstellen und Serialisierbar sind. Die Geschäftsschicht kann solche DTOs zurückgeben oder entgegennehemen und sie mit Hilfe der Datenzugriffslogik persistieren. Die Datenstrukturen liegen in einer separaten Assembly, die sowohl den Server- als auch den Clientkomponenten bekannt ist.
Es gibt zahlreiche OR-Mapping-Frameworks (z.B. nHibernate) die das nach diesem Prinzip tun. Oder eben ADO.NET (Die Entsprechung für DTO ist dabei DataSet/DataTable).

Wenn man die Datenstrukturen und Datenzugriffslogik gleich trennt, braucht man auch keine zusätzliche Abstraktionsschicht. Das macht die Anwendung nur unnötig kompliziert. Eine solche Abstraktionsschicht versucht eigentlich nur einen Design-Fehler zu korrigieren bzw. eine Datenzugriffsschicht die für Fat-Clients entworfen wurde n-Tier-tauglich zu machen.

Ich habe zu Zeiten von VB6 selbst solche OOP-Dateklassen wie Du gebaut und bin damit kräftig auf die Nase gefallen. Datenstrukturen sind Schnittstellen und Schnittstellen müssen neben der Implementierung stehen und nicht damit verwurstelt sein.

2.187 Beiträge seit 2005
vor 16 Jahren

Original von Rainbird
Eine solche Abstraktionsschicht versucht eigentlich nur einen Design-Fehler zu korrigieren bzw. eine Datenzugriffsschicht die für Fat-Clients entworfen wurde n-Tier-tauglich zu machen.

Da hast du recht, die zusätzliche Schicht, macht das Fat-Client-Framework n-Tier-tauglich und ist echt eine unnötige Sache. Ich sollte mich mal mit meinen Kollegen beraten, wie ich das aus meinem Framework raus bekomme, so dass es für die UI unwichtig wird, wo die Business-Schicht leuft.

Die Verbindung von Daten und Funktion - mit der du anscheinend schlechte Erfahrungen gemacht hast - ist eins der Grundprinzipe der Obejktorientierung. Oder bin ich seit 3Jahren auf dem Holzweg! Panik!
Ich bin diese Verbindung mit absicht eingegangen. Wir haben eine 1.Version unserer Anwendung die mit DTOs und "Funktionsklassen" arbeitet. Diese Version ist absolut unwartbar, die Datenstruktur wird durch die untypisierten DataSets vollkommen verschleiert, Funktionsklassen greifen wild auf bis zu 10 verschiedene DTOs (bzw. Tabellen) zu und die ganze Architektur ist auf Bulk-Verarbeitung (oder wie das heißt), also die Verarbeitung von vielen Datensätzen gleichzeitig, ausgelegt (Was in der Anwendung nicht vor kommt, höchstens mal 15 Datensätze auf einmal).

Ich glaube, dass eine Einheit aus Daten und Funktion besser austauschbar ist DTOs und Funktionsklassen. Sollten verschiedene Datenquellen zum Einsatz kommen, sollten das von der Factory abgefangen werden.

Datenstrukturen sind Schnittstellen und Schnittstellen müssen neben der Implementierung stehen und nicht damit verwurstelt sein. Die Aussage versteh ich im Moment nicht ganz. Könntest du das mal anders ausdrücken?

Danke für diese sehr anregende Diskusion.
Ich habe durch euch fest gestellt, dass mein Framework bereits eine Stelle besitzt an der man eine für die UI unsichtbare Erweiterung einbauen kann um auch entfernte Objekte zu verwenden und dank Oxygen kann ich eine Unterstützung für DataBinding einbauen.

Gruß
Juy Juka

3.728 Beiträge seit 2005
vor 16 Jahren

Original von JuyJuka
Die Verbindung von Daten und Funktion - mit der du anscheinend schlechte Erfahrungen gemacht hast - ist eins der Grundprinzipe der Obejktorientierung. Oder bin ich seit 3Jahren auf dem Holzweg! Panik!

Für viele Dinge ist Objektorientierung auch super (z.B. für Grafische Benutzeroberflächen wie Windows.Forms). Aber für datenbankgestützte Geschäftsanwendungen ist sie eher schädlich als nützlich. Ich würde fast schon sagen, OOP in dierser Form ist Gift für solche Anwendungen. Ich war auch einige Jahre auf dem Holzweg.

Meine objektorientierten Datenklassen haben damals etwa so ausgesehen (Für das bessere Verständnis schreibe ich das Beispiel in C# als Schnittstelle und nicht als VB6-Klassenmodul):


// Schnittstelle der Datenklasse für Angebote
public interface ISalesOffer
{
    void Load(int offerID);
    
    void Save();

    void Create();
    
    // Gibt die Auflistung der Angebotspositionen zurück.
    SalesOfferItemCollection Items
    {
        get;
    }

    int OfferId
    {
        get;
        set;
    }

    string OfferNo
    {
        get;
        set;
    }

    DateTime OfferDate
    {
        get;
        set;
    }

    // Datenklasse Company enthält Informationen über den Kunden.
    Company Customer
    {
        get;
        set;
    }

    OfferStateEnum State
    {
        get;
        set;
    }

    string CreatedBy
    {
        get;
        set;
    }

    DateTime CreatedOn
    {
        get;
        set;
    }
}

So habe ich damals ein "schönes" filigranes Objektmodell gebaut. Hinterher war alles ein unzertrennlicher Klumpen. Fazit: Alles Murks!

Original von JuyJuka
Ich glaube, dass eine Einheit aus Daten und Funktion besser austauschbar ist DTOs und Funktionsklassen. Sollten verschiedene Datenquellen zum Einsatz kommen, sollten das von der Factory abgefangen werden.

Es ist eher nicht so gut austauschbar, da man die Datenzugriffslogik nicht unabhängig von den Strukturen austauschen kann. Wenn ich z.B. verschiedene Datenbanksysteme (Oracle, SQL Server, MySQL, ...) unterstützen will, muss ich für jedes Datenbanksystem eine eigene Datenzugriffskomponente schreiben. Meine Datenstrukturen ändern sich aber nicht. Dem Client ist es egal, ob die Daten aus einer SQL-Server-Tabelle oder einer Oracle-Tabelle kommen. Wenn die verschiedenen Datenzugriffskomponenten alle eine Schnittstelle implementieren, kann ich per Konfiguration (z.B. App.config) einfach die Datenzugriffskomponente einstellen, welche für die aktuelle Installation benötigt wird.
Bei Deinem Ansatz müsste ich für alle Datenklassen mit all ihren vielen Properties Schnittstellen schreiben und diese für jedes Datenbanksystem komplett implementieren, damit die Factory einheitliche Schnittstellen zurückgeben kann.
Typisierte DataSets sind neutral und können von jeder beliebigen Datenquelle aus befüllt werden. Trotzdem stellen sie eine explizite Schnittstelle dar und ermöglichen die heiß ersehnte Intellisense beim codieren.

Original von JuyJuka
Die Aussage versteh ich im Moment nicht ganz. Könntest du das mal anders ausdrücken?

DTOs oder typisierte DataSets sind Schnittstellen . In Verbindung mit WCF würde man sagen: Es sind DataContracts (Contract = Vertrag). Genau wie XSD (XML-Schema-Definition) beschreibt, wie ein bestimmtes XML-Dokument aussieht, beschreiben DTOs oder typisierte DataSets wie bestimmte Datensätze aussehen.
Wenn nun verschiedene Prozesse/Module/Komponenten miteinander kommunizieren, müssen alle beteiligten ihre Daten in einer einheitlichen festgelegten Form übermitteln. Das bedeutet, dass jede(r) Prozess/Modul/Komponente die Assembly referenzieren muss, in der die Datenstrukturen definiert sind. Üblicherweise regelt ein(e) Prozess/Modul/Komponente den Zugriff auf die Datenbank und die anderen Prozesse/Module/Komponenten agieren als Clients. Diese Clients sollen nicht in der Lage sein, selbst direkt mit der Datenbank zu kommunizieren, aber sie benötigen trotzdem die Datenstrukturen (also die Schnittstellen). Deshalb darf die Implementierung der Datenzugriffslogik nicht mit der Definition der Datenstrukturen verwoben sein. Datenzugriffslogik und Datenstruktur-Definitionen (Schnittstellen) müssen auf jeden Fall in getrennten Assemblies liegen. Nur Prozesse/Module/Komponenten, die wirklich mit der Datenbank reden müssen, referenzieren die Datenzugriffs-Assembly und die Datenstruktur-Definitions-Assembly. Alle anderen refernzieren nur die Datenstruktur-Definitions-Assembly. Die Implementierung des Datenzugriffs steht in einer anderen Assembly als die Datenstruktur-Definitionen. Man kann also sagen: Die Schnittstellen stehen neben der Implementierung (in einer anderen Assembly und nicht in der Selben).

1.564 Beiträge seit 2007
vor 16 Jahren

Hallo Rainbird

Aber für datenbankgestützte Geschäftsanwendungen ist sie eher schädlich als nützlich. Ich würde fast schon sagen, OOP in dierser Form ist Gift für solche Anwendungen. Ich war auch einige Jahre auf dem Holzweg.

Sorry, aber das kann ich nicht unterschreiben. Ich arbeite jetzt seit 10 Jahren im Bereich datenbankbasierter Anwendungen. Ich habe die exakt entgegengesetzte Erfahrung gemacht. Ich fing an mit schlichteren Zwischenschichten und bin mittlerweile zu 100% OOP übergegangen. Seitdem habe ich viele Probleme nicht mehr die ich vorher hatte.

Ich dieses Thema beinhaltet allgemein sehr viele Glaubensfragen. Ich möchte nicht sagen, dass mein Weg der richtige ist oder irgendein anderer besser oder schlechter. Ich habe schon sehr viele unterschiedliche Design-Patterns in diesem Bereich gesehen und kann nur sagen man kann jedes Design gut oder weniger gut verwenden.

Grüße
Flo

Blog: Things about Software Architecture, .NET development and SQL Server
Twitter
Google+

Je mehr ich weiß, desto mehr weiß ich was ich noch nicht weiß.

2.187 Beiträge seit 2005
vor 16 Jahren

Hallo,

Original von Rainbird
Hinterher war alles ein unzertrennlicher Klumpen.

Diesem Problem steh ich auch gegenüber, hab jetzt aber dank unserer Diskusion - glaube ich - eine Lösung.

Original von Rainbird
Es ist eher nicht so gut austauschbar, da man die Datenzugriffslogik nicht unabhängig von den Strukturen austauschen kann. Wenn ich z.B. verschiedene Datenbanksysteme (Oracle, SQL Server, MySQL, ...) (Ich quote jetzt nicht den ganzen Absatzt.)
Da hattest du wohl einfach Pech mit deinem Designe, bei meinem Designe habe ich A) eine datenbankunabhänige Datenbankanbindung und B) eine generische (≤ Funktioniert für alle Klassen) Mappingschicht.
Das heißt: Ich muss mir über die Datenquelle (-bank) keine Gedanken machen und kann Sie jeder Zeit wechseln (über App.config) und wenn sich mein Datenmodel ändert, muss ich nur die Bussines-Schicht anpassen, da die Mappingschicht generisch ist.
!! Zwischen Datenbankanbindung und Mappingschicht verwende ich auch DataSets, da sie wie gesagt neutral und flexibel sind. !!

Original von Rainbird
Nur Prozesse/Module/Komponenten, die wirklich mit der Datenbank reden müssen, referenzieren die Datenzugriffs-Assembly und die Datenstruktur-Definitions-Assembly. Alle anderen refernzieren nur die Datenstruktur-Definitions-Assembly.

Genau das ist mit Business-Klassen - aus Daten und Funktionen - auch möglich, das spricht nicht gegen dieses Vorgehen.

Original von Oxygen
Ich dieses Thema beinhaltet allgemein sehr viele Glaubensfragen.

Stimmt wohl und beide Ansätze - "Integrierte Daten und Funktionen" und "DTOs und Funktionsklassen" - wurden entwickelt, um zu abstrahieren, zu trennen und mit dem OOP-Gedanken.

Ich werde bei "Integrierte Daten und Funktionen" bleiben, da nach meinem Empfinden "DTOs" gegen ein Grundprinzip der OOP verstossen. Das ist jetzt keine Empfehlung, sondern meine Meinung.

Gruß
Juy Juka

3.728 Beiträge seit 2005
vor 16 Jahren

Original von JuyJuka
Genau das ist mit Business-Klassen - aus Daten und Funktionen - auch möglich, das spricht nicht gegen dieses Vorgehen.

Was machen Deine Business-Klassen genau und wie sind sie aufgebaut?
Sind sie statuslos?

S
8.746 Beiträge seit 2005
vor 16 Jahren

Dieser Load/Save-Kram ist m.E. ein großer Killer. Auch in OO-DBs. Persistenz ist ein Aspekt und keine Schnittstelle.

Mappingschichten find ich toll, nur würde ich nie auf die Idee kommen, eine selbst zu schreiben...

M
110 Beiträge seit 2007
vor 16 Jahren

Hallo Svenson,

man muss ja auch nicht alles schreiben, was es schon gibt. Es sein denn, es gibt noch keine Lösung zu dem, was man braucht. O/R Mapper gibt es wie Sand am Meer. Okay ich haben auch einen geschrieben, aber das liegt nur daran, da keiner die Funktionalitäten bieten konnte, die ich gerne hätte.

Man kann ja schliesslich auch nicht alles selber machen. Ich vertrete die Meinung: Was soll ich mich in etwas einarbeiten, was andere schon längst gelöst haben und es bestimmt besser ist, als das was ich machen könnte. Man kann nicht alles wissen und sich überall auskennen.

Gruss

Mirko

Mappen statt hacken mit Invist , dem .NET O/R Mapper - Code Generator