Softwareentwickler wünschen sich einen typisierten Datenzugriff, allerdings ohne die Schwerfälligkeit des DataSet. Bei den vielen Lösungen, die sie in der .NET-Welt finden, ist die Grundidee immer die gleiche: Man definiert .NET-Klassen, deren Aufbau dem Tabellenschema einer Datenbank entspricht. Nach dem Laden der Daten mit dem DataReader kopiert der Programmierer die Datensätze zeilenweise in Instanzen der .NET-Klassen um:
Kunde k = new Kunde();
k.ID = Convert.ToInt64(DataReader["id"])
k.Ort = Convert.ToString(Ort["id"])
Das sorgt (gerade bei vielen oder breiten Tabellen) für viel Arbeit und ist nicht einmal die "halbe Miete", denn in der Regel will man ja Änderungen speichern. Dabei ist nicht nur zwischen Insert, Update und Delete zu unterscheiden, sondern auch das Problem der Änderungsverfolgung zu lösen. Außerdem gilt es, viele weitere Fälle zu beachten wie Primär- und Fremdschlüssel, Navigationsbeziehungen, Autowertspalten und Spalten mit Standardwerten sowie Transaktionen.
Mit hinreichenden Verallgemeinerungen der Anforderungen auf beliebige Datenbankschemata ist man bei einem vollwertigen objektrelationalen Mapper (ORM) angekommen. Microsoft hat sich aus dem Thema (zu) lange herausgehalten. In .NET 2.0 Beta gab es einen Prototypen, der es nicht zur Marktreife schaffte. Seit .NET 3.5 gibt es LINQ-to-SQL und das ADO.NET Entity Framework (zur Frage, warum es zwei gibt, siehe "Verwirrung um objektrelationale Mapper" im fünften Teil dieser Serie). In der Zwischenzeit wurden ORM-Werkzeuge wie Sand am Meer entwickelt, sowohl kommerzielle als auch nicht kommerzielle. Das bekannteste ORM-Werkzeug für .NET außerhalb der von Microsoft angebotenen Lösungen ist sicherlich NHibernate. Über die Frage, ob typisierte DataSets ein ORM sind oder nicht, lässt sich, in Abhängigkeit von den Anforderungen, die man an einem ORM hat, streiten.
Die Unterschiede im Leistungsumfang und in der Geschwindigkeit der am Markt verfügbaren ORM-Werkzeuge sind groß. Bei der Evaluierung eines Tools sind viele Punkte zu beachten, wobei einige wichtige genannt seien:
Einen Vergleich der beiden Microsoft-ORMs mit NHibernate und einigen kommerziellen Produkten pflegt der Autor auf seiner Website. Die Auswahl ist sicherlich subjektiv geprägt durch das, was ihm normalerweise bei seinen Kunden begegnet. Ein vollständiger Vergleich aller Produkte am Markt ist kaum zu leisten, zumal es ständig neue Versionen gibt.
Insgesamt sollte man im Hinterkopf haben, dass es in einigen Szenarien gar keinen Sinn ergibt, einen ORM einzusetzen. Ein Beispiel für den ORM-Ausschluss sind zeitkritische Aktionen, etwa Maschinensteuerungen, bei denen jede Millisekunde zählen kann. Abbildung 1 und 2 zeigen, dass OR-Mapper tendenziell sogar noch langsamer sind als das typisierte DataSet. Hier ist in .NET zu DataReader beziehungsweise zu direktem SQL mit Command-Objekten raten.
Geschwindigkeitsvergleich von unterschiedlichen Datenzugriffslösungen am Beispiel des Einlesens von 10.000 Datensätzen mit einem SQL-Befehl aus einer Tabelle (Abb. 1).
Geschwindigkeitsvergleich von unterschiedlichen Datenzugriffslösungen am Beispiel des Aktualisierens von 1000 Datensätzen mit 1000 eigenen SQL-Befehlen (Abb. 2).
Auch Massendatenänderungen programmiert man üblicherweise nicht mit ORMs. Wenn man zum
Beispiel für 100 Produkte den Preis um 10 Prozent erhöhen möchte, sollte man direkt einen SQL-Befehl, der die Preisspalte ändert, zur Datenbank schicken. ORM-Einsatz würde heißen, die Produktobjekte erst unnötigerweise ins RAM zu laden, dort zu ändern und schließlich zurückzuspeichern. Ebenso ein K.O.-Kriterium für ORMs sind Datenmodelle, die sich ohne Neukompilierung der Anwendung ändern lassen sollen, beispielsweise durch den Kunden. Typisierte Objekte, die das ORM-Zentrum bilden, sind gar nicht umsetzbar. In dem Fall leisten die untypisierten Container DataReader und DataSet gute Dienste.
Für ORMs sprechen hingegen Projektanforderungen wie Datenbankunabhängigkeit, Kommunikation mit anderen Plattformen (dort gibt es keine DataSets), Persistenztransparenz, Leichtgewichtigkeit und "sauberer", überschaubarer Code. Wenn man mit dem Objektmodell beginnen will und die Datenbank auf der Basis erzeugen möchte, ist objektrelationales Mapping das Instrument der Wahl. Vererbungsunterstützung hat man nur mit ORMs.
Es ist nicht ausgeschlossen, sondern sinnvoll, dass Softwarearchitekten eine hybride Strategie implementieren, bei denen zwei Datenzugriffslösungen in einer Anwendung existieren, zum Beispiel ORM im Standard und der DataReader für die zeitkritischen Aktionen.
Dr. Holger Schwichtenberg
bietet mit seinem Unternehmen www.IT-Visions.de Beratung und Schulungen im .NET-Umfeld. Er hält Vorträge auf Fachkonferenzen und ist Autor zahlreicher Fachbücher.
Themenforum: .NET