Ich höre immer wieder Leute sagen, Typisierte DataSets seien böse. Im Jahre 2003 wollte ich davon auch noch nichts wissen. Aber Typisierte DataSets, wie sie Visual Studio 2005 erzeugt sind eine feine Sache. Ich habe mich deshalb gefragt, warum Typisierte DataSets so verteufelt werden?
Höchstwahrscheinlich ist der DataSet-Designer von Visual Studio schuld. Wenn man als fauler Designer-Fan eine SQL Server Tabelle aus dem Server-Explorer per Drag&Drop in den DataSet-Designer zieht, passiert das hier:
Der Designer erstellt zwar automatisch eine schöne DataTable im DataSet, die alle Spalten der SQL Server Tabelle enthält, aber er hängt unten etwas dran (Fokus des bedrohlichen roten Wirbels im Bild). Es ist ein sogenannter TableAdapter. Zweck eines TableAdapters ist es, die DataTable, an der er hängt, mit Daten aus einer Datenbank zu füllen und Änderungen an der DataTable in einer Datenbank zu persistieren. Ein DataSet bzw. eine DataTable ist ein Container-Objekt, welches verwendet wird, um strukturierte Daten zwischen Komponenten auszutauschen. Man kann auch sagen ein DataSet ist eine Datenstruktur. So ein Container sollte passiv sein. Das heißt er sollte von "außen" befüllt werden und sich nicht selbts füllen. Der TableAdapter macht genau das Gegenteil. Er verwandelt das DataSet selbst in eine aktive Komponente. Und damit nicht genug. Ein Datenbankzugriff benötigt natürlich immer eine Verbindungszeichenfolge. Die hat der Designer heimlich, still und leise nebenbei als Anwendungseinstellung angelegt. So bekommt das Visual Studio-Projekt indem das DataSet liegt, plötzlich und automatisch eine Anwendungskonfigurationsdatei (App.config) untergeschoben:
<?
xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<connectionStrings>
<add name="WindowsApplication2.Properties.Settings.NTierExampleConnectionString"
connectionString="Data Source=AMILO;Initial Catalog=NTierExample;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
Spätestens jetzt haben bestimmt viele auf das kleine X rechts oben geklickt und sich nach einem OR-Mapping Werkzeug umgesehen.
Wenn der TableAdapter der Böse ist, liegt es klar auf Hand, was zu tun ist, um sinnvoll mit Typisierten DataSets arbeiten zu können. Richtig, kein Drag&Drop aus dem Server-Explorer! Stattdessen Tabellen, Spalten, Schlüssel und Beziehungen manuell im DataSet-Designer zusammenklicken. Das ist etwas mehr Arbeit, aber man erhält hinterher ein sauberes DataSet, welches von keiner Datenquelle abhängig ist. Füllen und Persistieren des DataSets kann so eine eigenständige Geschäftskomponente übernehmen. Dem DataSet ist es egal, von welcher Datenbank die Daten kommen und wohin Änderungen persistiert werden.
Es gibt noch einen Grund, warum DataSets im allgemeinen verpöhnt sind. Scheinbar denken viele Leute, Typisierte DataSets müsse man immer als Ganzes einsetzen. Also in etwa so:
// Neues Produkt Management-DataSet erzeugen
ProductManagementDataSet dataContainer = new ProductManagementDataSet();
Oft braucht man aber nur eine Tabelle und nicht drei auf eimal. Auch Beziehungen um zwischen Eltern- und Kind-Datensätzen zu springen, werden eher selten benötigt. Trotzdem arbeiten die meisten Tutorials, Bücher und Beispiele zu ADO.NET und DataSets immer mit ganzen DataSets. Dabei sind DataTables eigenständige Objekte, die auch dann alleine leben können, wenn sie in einem Typisierten DataSet definiert wurden. Wenn ich z.B. nur mit Produkten arbeiten will, mich aber Kategorieen und Preise gerade nicht interessieren, verwende ich auch nur die entsprechende DataTable und nicht das ganze DataSet:
// Neue Produkt-Tabelleninstanz erzeugen
ProductManagementDataSet.ProductsDataTable dataContainer = new ProductManagementDataSet.ProductsDataTable();
Jetzt beginnt das Argument "DataSets haben zu viel Overhead und sind unhandlich!" zu bröckeln. Ich neige dazu für jeden Bereich einer Geschäftsanwendung ein Typisiertes DataSet zu erstellen, welches die Datenstrukturen für diesen Bereich enthält. Diese DataSets werden in separate Assemblies gelegt. So können verschiedene Komponenten und Prozesse mit den selben Datenstrukturen arbeiten. Alles ist typsicher und hat den vollen Komfort von DataSets/DataTables.
Da Typisierte DataSets von System.Data.DataSet und deren Tabellen von System.Data.DataTable abgleitet sind, kann man mit Typisierten DataSet alles machen, was man mit den Basisklassen auch machen kann (z.B. DataView darauf anwenden, Filtern, Suchen, Zusammenführen, etc.). Was den Komfort angeht, sind DataSets/DataTables unschlagbar (Ich lasse mich natürlich gerne vom Gegenteil überzeugen)!
Wer Typisierten DataSets trotzdem noch nicht über den Weg traut, dem möchte ich gerne folgendes Architekturbeispiel von mir ans Herz legen: .NET Applikationsserver auf mycsharp.de. In diesem kleinen Projekt werden DataSets als Datentransfer-Objekte eingesetzt, die strukturierte Daten zwischen einem kleinen Applikationsserver und einem Windows.Forms-Client übertragen.
Fazit:
Typisierte DataSets sind cool.
Typisierte DataTables sind cool.
TableAdapter sind böse und damit der eigentliche Teufel , den man austreiben sollte.