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 Microsoft 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ärtsgerichteten 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 Microsoft SQL Server 2005 und 2008 verfügbar und dort im Standard deaktiviert.
Beispiel
- Konfiguration der app.config bzw. web.config
<configuration>
<!-- Connection String für Verbindung zur Datenbank -->
<connectionStrings>
<add name="MyConnectionString"
providerName="System.Data.SqlClient"
connectionString="Data Source=(localdb)\v11; InitialCatalog=MyDatabaseName" />
</connectionStrings>
<!-- Datenbank-Provider Factory -->
<!-- (meist in machine.config konfiguriert) -->
<system.data>
<DbProviderFactories>
<add name="SqlClient Data Provider"
invariant="System.Data.SqlClient"
description=".NET Framework Data Provider for SQL Server"
type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</DbProviderFactories>
</system.data>
</configuration>
- Herstellen einer Datenbankverbindung
var settings = ConfigurationManager.ConnectionStrings["MyConnectionString"];
var factory = DbProviderFactories.GetFactory(settings.ProviderName);
using (var connection = factory.CreateConnection())
{
if (connection == null) throw new Exception("Could not create a connection.");
connection.ConnectionString = settings.ConnectionString;
// use DbParameters to prevent SQL Injections
var age = factory.CreateParameter();
if (age == null) throw new Exception("Could not create parameter.");
age.ParameterName = "@age";
age.Value = 25;
// get a DbCommand with factory method
var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM dbo.People WHERE Age > @age";
command.CommandType = CommandType.Text;
command.Parameters.Add(age);
// open connection to database
await connection.OpenAsync();
// Variant 1: read query results
var dataReader = await command.ExecuteReaderAsync();
while (await dataReader.ReadAsync())
{
Console.WriteLine("Id: '{0}', FirstName: '{1}', MiddleName: '{2}', LastName: '{3}'",
dataReader["id"], dataReader["firstname"], dataReader["middlename"], dataReader["lastname"]);
}
dataReader.Close();
// Variant 2: use a data adapter to fill a data table
using (var dataAdapter = factory.CreateDataAdapter())
{
if (dataAdapter == null) throw new Exception("Could not create a data adapter.");
dataAdapter.SelectCommand = command;
var dataTable = new DataTable();
dataAdapter.FillSchema(dataTable, SchemaType.Source); // get table schema
dataAdapter.Fill(dataTable); // get data
using (var dataTableReader = new DataTableReader(dataTable))
{
while (await dataTableReader.ReadAsync())
{
Console.WriteLine("Id: '{0}', FirstName: '{1}', MiddleName: '{2}', LastName: '{3}'",
dataTableReader["id"], dataTableReader["firstname"], dataTableReader["middlename"], dataTableReader["lastname"]);
}
}
}
}
Literatur
- Andrew Troelsen: Pro C# 5.0 and the .Net 4.5 Framework. Springer, 2012, ISBN 978-1-4302-4233-8, S. 1560 (englisch).