ADO.NET
ADO.NET ist ein Teil der von Microsoft entwickelten .NET-Plattform. Es handelt sich um eine Sammlung von Klassen, die den Zugriff auf relationale Datenbanken gewährleisten.
ADO.NET gilt als Nachfolger der ActiveX Data Objects (ADO), hat aber nichts mit der ActiveX Technologie zu tun. In der Tat ist es um zahlreiche Funktionen erweitert worden, so dass man von einer Neuentwicklung sprechen kann.
Aufgaben
Aufgabe der Klassen (die sich im Namensraum System.Data befinden) ist die Datenbankanbindung und Datenhaltung im Arbeitsspeicher. Dazu existieren Klassen, die Verbindung zu einer Datenbank (Microsoft SQL Server, Oracle etc.) herstellen (sogenannte Connection-Klassen), Klassen, die Tabellen im Arbeitsspeicher repräsentieren, und es ermöglichen, mit ihnen zu arbeiten (sogenannte DataTables) und Klassen, die für gesamte Datenbanken im Arbeitsspeicher stehen (sogenannte DataSets).
Andere Klassen regeln die Anbindung an eine Datenbank. Für die Anbindung an die physische Datenbank existieren sogenannte DataProvider. Die Datenbanken können auf XML-Format abgebildet werden, weshalb es Klassen zum direkten Zugriff auf XML im Namensraum System.Xml gibt.
Architektur von ADO.NET
Das Hauptanliegen von ADO.NET besteht darin, die Datenbeschaffung von der Bereitstellung und Anzeige der Daten vollständig zu trennen. Um dieses Ziel zu erreichen, spaltet sich ADO.NET in die drei Hauptkomponenten DataSet, Datenprovider und die Klassen der Datenbindung auf. Letztere stellen allerdings keinen integralen Bestandteil von ADO.NET dar, stattdessen dienen sie der Anbindung der Steuerelemente an ein DataSet.
Datenprovider
Der Datenprovider ist die Schnittstelle zu einer Datenbank. Er muss fachliche Informationen über die Datenbank besitzen, d. h., er muss die Datenbank kennen. Für unterschiedliche Datenbanken existieren individuelle Datenprovider. Im .NET Framework sind die Datenprovider MS SQL Server und OLE DB standardmäßig enthalten. Auch für viele Open-Source-Datenbanken, wie z. B. MySQL, existieren .NET-Datenprovider.
Die vier Kernkomponenten der .NET-Datenprovider sind:
- Connection: Stellt eine Verbindung her, die der Kommunikation mit einer Datenquelle dient. Seit .NET 2.0 ist es möglich bei der Verbindung umfangreiche Metadaten zur Datenbank zu laden.
- Command: Führt Anweisungen, gespeicherte Prozeduren und Aktionsabfragen aus. Dazu gehören unter anderem SELECT-, UPDATE- oder DELETE-Kommandos.
- DataAdapter: Der DataAdapter füllt ein DataSet mit Daten und gleicht Aktualisierungen mit der Datenquelle ab. Er fungiert als Brücke zwischen der Datenquelle und einem DataSet-Objekt.
- DataReader: Es handelt sich um einen vorwärts gerichteten Datensatzleser, der nur einen lesenden Zugriff auf die Daten gestattet. Eine Navigation durch die Datensätze ist dabei nicht möglich, da diese sequentiell abgearbeitet werden.
DataSet

Ein DataSet repräsentiert die speicherresidente, relationale Datenbank in der eigentlichen Anwendung. Es handelt sich dabei um ein Speicherabbild der eigentlichen Datenbank. Ein DataSet wird immer dann eingesetzt, wenn Daten mehrmals benötigt und von der Anwendung geändert werden. In diesem Fall werden die Daten über den DataAdapter im DataSet gespeichert, wo sie der Anwendung zur weiteren Verwendung zur Verfügung stehen.
Die wichtigsten Klassen des DataSet sind:
- DataSet: Diese Klasse repräsentiert ein logisches Schema. Das Datenbankschema verwaltet Beziehungen zwischen den in der Klasse enthaltenen Tabellen und sorgt dafür, dass die Datenbank relational ist.
- DataTable: Das DataTable-Objekt stellt eine Datenbanktabelle dar. Es handelt sich um einen Datenspeicher mit Datensätzen und Spalten.
- DataRow: Die DataRow-Klasse repräsentiert einen konkreten Datensatz in einer DataTable. Eine DataRow-Instanz ist stets an ein DataTable-Objekt gebunden.
- DataView: Dient zum Filtern (z. B. WHERE) und Sortieren (z. B. ORDER BY) von Daten in einer DataTable. Über eine DataView kann eine spezielle Sicht auf die Daten einer Tabelle angelegt werden.
Einschränkungen
In ADO.NET 1.x konnte zu bestimmten Zeitpunkten pro Verbindung nur ein Datenbankbefehl aktiv sein, beispielsweise ein DataReader lesend auf die Datenbank zugreifen. Versuche eines parallelen Zugriffs waren nicht möglich und führten zu Fehlermeldungen. Diese Architektur kann als Single Active Result Sets (SARS) bezeichnet werden.
ADO.NET 2.0 unterstützt hingegen Multiple Active Result Sets (MARS), also die Mehrfachverwendung einer Verbindung. MARS ist für den SQL Server 2005 und 2008 verfügbar und dort im Standard deaktiviert.
ADO.NET Entity Framework

Code First | Model First | |
---|---|---|
Keine Datenbank vorhanden | Bestehende Klassen werden mit Annotationen (Table , Column ) ausgezeichnet, welche die Abbildung auf eine Datenbank steuern. Darauf aufbauend werden vom DbContext die Datenbank und die Datenbank-Tabellen modelliert und beim Aufruf der SaveChanges() -Methode erstellt.
|
Die Entity Klassen werden mit einem grafischen Designer modelliert. Das Modell wird einerseits mit Hilfe des Text Template Transformation Toolkit (T4) und der zugehörigen T4-Skriptsprache in Entity-Klassen umgewandelt. Zudem erlaubt es der Designer ein SQL-Skript zu erstellen, mit dem die Datenbank erstellt wird. |
Verwendung einer bestehenden Datenbank | Die Entity-Klassen können entsprechend der vorgegebenen Datenbank manuell erstellt, modelliert und ausgezeichnet werden. Dies ist jedoch sehr arbeitsintensiv. | Mit Hilfe eines Assistenten wird die Datenbank abgefragt und entsprechend der Datenbankstruktur ein passendes Modell erstellt. Dieses wird mit einem T4-Skript in die entsprechenden Klassen umgewandelt. |
DbContext API[EF4 1] (EF5) | EF4 | Aufgabe |
---|---|---|
DbContext | ObjectContext | Stellt eine Verbindung mit der Datenbank dar. Stellt Methoden für Abfragen (Query), Änderungsverfolgung (Tracking) und Speichern (Save) bereit. |
DbQuery | ObjectQuery | Stellt Methoden für das Hinzufügen (Add), Anhängen (Attach) und Entfernen (Remove) von Entitäten bereit. |
DbSet | ObjectSet | Erbt von von DbQuery/ObjectQuery und stellt die entsprechenden Methoden für Entity-Typen bereit. |
Change Tracker API | ObjectContext.ObjectStateManager | Bietet Methoden um Änderungen verfolgen zu können. Hierzu gehört das Abfragen des ursprünglichen und des derzeitigen Zustands von Entitäten. |
Validation API | — | Automatische Validierung der Daten im DataLayer. |
Code First Model Building | — | Reflektiert über Code basierte Klassen um für diese passende Datenbankobjekte und die zugehörigen Metadaten im Arbeitsspeicher und der Datenbank zu erstellen. |
|
Inserting Entities
using(MyDbEntities context = new MyDbEntities())
{
// Erstelle zwei Personen-Entities
Person person = new Person(){
FirstName = "William",
LastName = "Adama",
DateOfBirth = DateTime.Now
};
Person person = new Person(){
FirstName = "Laura",
LastName = "Roslin",
DateOfBirth = DateTime.Now
};
// Erstelle ein Adressen-Entity
Address address = new Address(){
Street = "Market Street 70",
City = "Philadelphia",
State = "PA",
Zip = "12345"
};
// Erste Variante
context.Persons.Add(person1);
// Zweite Variante
// mit dem Kontext verlinken und als Hinzugefügt markieren.
context.Entry(person2).State = EntityState.Added;
// Dritte Variante
// Das Entity wird an ein breits vom Kontext beobachtetes Entity gehängt
person1.Address.Add(address);
// Speichere Änderungen am Kontext in der Datenbank
context.SaveChanges();
}
Abfrage von Daten
Abfragen aller Daten aus einem Datensatz:
using(MyDbEntities context = new MyDbEntities())
{
IEnumerator<Person> persons = context.Persons; // entspricht SELECT * FROM [Persons]
//// wenn LINQ-Ausdrücke (zB. Joins) benötigt werden:
// IEnumerator<Person> persons = from p in context.Persons select p;
foreach(Person person in persons)
{
// führt zusätzliche SQL-Abfragen an die Adresses-Tabelle
// mit einem entsprechenden JOIN aus
foreach(Adress address in person.Adresses)
{
// ...
}
}
}
Um zu verhindern, dass die selbe Datenbankabfrage mehrfach ausgeführt wird, kann die ToList()
-Methode verwendet werden:
using(MyDbEntities context = new MyDbEntities())
{
var persons = context.Persons;
// Datenbankabfrage wird ausgeführt und als Liste zurückgegeben
var allPersons = persons.ToList();
// Keine weitere Datenbankabfragen durch Verwendung der Liste
foreach(Person person in allPersons) { /* ... */ }
foreach(Person person in allPersons) { /* ... */ }
}
Sucht ein bestimmtes Objekt in der Datenbank:
Person person = context.Persons.SingleOrDefault(p => p.PersonId == personId);
Oder mit LINQ:
Person person = (select p from context.Persons where p.PersonId == personId select p).SingleOrDefault();
Bei Db-Objekten steht zudem die Find()
-Methode bereit. Diese sucht das Objekt zuerst im Arbeitsspeicher und macht eine Datenbankabfrage falls das Objekt nicht im Speicher gefunden wird:
Person person = context.Persons.Find(personId);
Lazy, Eager und Explicit Loading
Das Entity Framework verwendet im Normalfall das lazy loading, bei dem Daten aus der Datenbank dann geladen werden, wenn die Daten abgefragt werden:
// lazy loading
// nur die Personen werden abgefragt und geladen
var persons = context.Peoples;
Falls weitere Daten mit geladen werden müssen, wird das eager loading verwendet:
// eager loading
// Adressen werden bereits bei der Abfrage der Personen geladen
var persons = context.Peoples.Include("Adresses");
// LINQ-to-Entities Beispiel für eager loading
var persons = from p in context.Peoples.Include("Adresses")
where p.FirstName == fname
select p;
Das explizite Laden (explicit loading) der Daten ist ähnlich dem lazy loading, erlaubt jedoch das Laden der Navigationseigenschaften (navigation properties).
// explicit loading
var persons = context.Peoples; // wie lazy loading; Adressen werden nicht mit geladen
foreach(var person in persons)
{
person.Adresses.Load(); // explicit loading; navigation properties für Adressen werden geladen
forach(var adress in person.Adresses)
{
// ...
}
}
Update Entities
Delete Entities
Löschen eines Entities im Entity Framework 4:
using(MyDbEntities context = new MyDbEntities())
{
// Abfrage eines Entities aus der Datenbank
Person person = (select p in context.Pesons
where p.Id == id
select p).SingleOrDefault();
if(person != null)
{
context.Persons.DeleteObject(person);
context.SaveChanges();
}
}
Löschen eines Entities im Entity Framework 5:
using(MyDbEntities context = new MyDbEntities())
{
// Abfrage eines Entities aus der Datenbank
Person person = (select p in context.Persons
where p.Id == id
select p).SingleOrDefault();
if(person != null)
{
context.Entry(person).State = EntityState.Deleted; // Entity zur Löschung markieren
context.SaveChanges(); // Entity in der Datenbank löschen
}
}
Vorkompilierte Abfragen
Datenbankanfragen werden vom Entity Framework in für die Datenbankschnittstelle passende Anfragen kompiliert. Dieser Vorgang kostet jedoch Zeit, weshalb kompilierte Anfragen – sofern diese erneut benötigt werden – nicht verworfen, sondern in einem Objekt gespeichert und später wiederverwendet werden sollten.
Um bei der ersten Verwendung einer Abfrage mit dem Entity Framework 4 Zeit zu sparen, können Abfragen vorkompiliert werden.
static Func<MyDbEntities, int, ObjectQuery<Person>> QueryContactById =
CompiledQuery.Compile<MyDbEntities, int, IQueryable<Person>>(
(context, personId) =>
from p in context.Persons
where p.Id== personId
select p);
Im Entity Framework 5 werden Abfragen automatisch bei der Erstellung vorkompiliert.
Transaktionen
using (MyDbEntities context = new MyDbEntities())
{
using (TransactionScope scope = new TransactionScope())
{
// Der TransactionScope sucht den neuesten Context auf dem Stacktrace und verlinkt sich automatisch mit diesem
try
{
// Bearbeitung von Entities
context.SaveChanges(); // speichern der Änderungen in der Datenbank
scope.Complete(); // Transaktion wird abgeschlossen
}
catch (InvalidOperationException e)
{
// Transaktion fehlgeschlagen
}
} // scope.Dispose()
} // context.Dispose()
Literatur
- Andrew Troelsen: Pro C# 5.0 and the .Net 4.5 Framework. Springer, 2012, ISBN 978-1-4302-4233-8, S. 1560 (englisch).
- Julia Lerman, Rowan Miller: Programming Entity Framework: DbContext; Querying, Changing, and Validating Your Data with Entity Framework. O'Reilly Media, 2012, ISBN 978-1-4493-1296-1, S. 258 (englisch).
- Julia Lerman, Rowan Miller: Programming Entity Framework: Code First; Creating and Configuring Data Models from Your Classes. O'Reilly Media, 2012, ISBN 978-1-4493-1294-7, S. 194 (englisch).
Weblinks
- .NET Framework-Entwicklerhandbuch ADO.NET (Visual Studio 2005) im MSDN
- .NET Framework-Entwicklerhandbuch ADO.NET (Visual Studio 2008) im MSDN
- Daten 2.0: Verfügbarmachen und Nutzen von Daten in einer Welt der Webdienste im MSDN-Magazine
- Liste von ADO.NET Datenprovidern. Abgerufen am 11. November 2012 (englisch).
- Liste von ADO.NET Entity Framework Providern. Abgerufen am 11. November 2012 (englisch).
- Performance Considerations for Entity Framework 5. Abgerufen am 11. November 2012 (englisch).
- Get Started with Entity Framework (EF). In: Data Developer Center. Microsoft, abgerufen am 12. November 2012 (englisch).