© Martin Korneffel,
Stuttgart 2003 +++ email: trac@n.zgs.de
+++ web: www.s-line.de/homepages/trac
ADO.NET
Inhalt
ADO.NET
Übersicht
ADO.NET-
Bibliotheken
Zugriff
über OleDB
Zugriff
auf SQL- Server
Zugriff
auf ODBC- Quellen
Datenbankprovider
- unabhängiger Code durch das Common- Model (NET 2.0)
DbProviderFactory
Auflistung
aller installierten Provider mittels DbProviderFactories
Besipiel
für Instanziierung providerspezifischer ADO.NET Typen mittels
ClassFactory
Abbildung
der Datenbank- Datentypen auf die .NET Datentypen
Datenaustausch
über DbParameter- Objekte
Automatische
Abblidung der Parametertypen auf Datenbanktypen
Explizite
Abbildung der .NET Datentypen auf die Datanbanktypen
Phasen
beim Zugriff auf Daten mittels ADO.NET
Verbindung
aufbauen
Connection
Strings
DbConnectionsStringBuilder
Schema
für den Umgang mit Verbindungszeichenfolgen
Zugriff
auf Daten mit DbCommand und DbDataReader
Beispiel
für ein Zugriff auf die Datenquelle mittels
XXXCommand/Datareader
Struktur
eines DataReaders
Behandeln
von Nullwerten
XXXDataReader-
Einschränkungen
MARS:
Programmieren verschachtelter DataReader mittels Multiple Active
Result Sets auf SQL Server 2005
Asynchrone
Ausführung von DataReader mittels BeginExecuteReader
Warten
auf die Fertigstellung über WaitHandles
Einlesen
großer Datenwerte (LOBs)
NonQuerys
über ein Command- Objekt abwickeln
SQL-
Kommando mit Parametern definieren
Parametern
Werte zuweisen und NonQuery ausführen
DataSet
DataSets
und Datenbank mit ADO.NET DataAdapter synchronisieren (NET 1.x)
Datasets
und Datenbank über TableAdapter synchronisieren (NET 2.0)
Objektmodell
von DataSet
Zugriff
auf Tabellen, Zeilen und Spalten in einem DataSet
Zeilen
einer Dataset- Tabelle hinzufügen
Zeilen
einer Dataset- Tabelle editieren
Zeilen
einer Dataset- Tabelle löschen
Beziehungen
zwischen Tabellen festlegen
Daten
aus DataSet in Tabelle zurückschreiben
Sortieren
und Filtern mittels DataViews über DataSet
Tabelle
aus DataSet als XML- Dokument ausgeben
XML-
Dokumente in DataSet einlesen
XML-
Typen
ADO.NET
und Steuerelemente
Datenquellen
an Eigenschaften von Steuerelementen binden
DataGrid
Transaktionen
Transaktionen
auf DataSets
Transaktionen
auf Datenbanken
Crystal
Reports
Zugriff
auf ODBC- Quellen
Erstellen
von Berichten über Datasets
ADO.NET
Übersicht
ADO.NET definiert einen einheitlichen Bauplan für
den Zugriff auf Datenquellen im .NET Framework.

ADO.NET-
Bibliotheken

Zugriff
über OleDB
OleDB dient hauptsächlich zum Zugriff auf
lokale Datenbanken (Access etc.)
Zugriff auf
SQL- Server
Namensraum System.Data.SqlClient und
System.Data.SqlTypes.
Über OleDB kann auch auf SQL- Server von MS
zugegriffen werden. Jedoch nutzt die allgemeine OleDB
Schnittstellenicht den Server nicht Optimal aus. Deshalb wurde ein
optimierter Satz von ADO.NET Klassen für den Zugriff auf SQL-
Server entwickelt.
Zugriff auf ODBC- Quellen
Microsoft liefert einen .NET Provider für
ODBC- Quellen. Damit sind defacto alle Datenbanken für .NET
verfügbar, da fast jede Datenbank über die Standardisierte
ODBC- Schnittstelle verfügt. Im Folgenden die Architektur für
den Zugriff auf MySQL Datenbankserver mittels ODBC- und .NET Provider
(siehe ODBC.NET)

Datenbankprovider
- unabhängiger Code durch das Common- Model (NET 2.0)
DbProviderFactory
In NET 2 .0 werden die
Datenbankprovider von der Zugriffslogik im Programm weiter entkoppelt
durch das sog. Common Model. Es handelt sich hierbei um eine
Klassenbibliothek im Namensraum System.Data.Common. Alle
Kernklassen von ADO.NET wie XXXCommand, XXConnection,
XXXDataReader usw. leiten sich von den hier definierten absrakten
Basisklassen ab. Wird die Zugriffslogik auf Operationen auf
Referenzen vom Typ dieser Basisklassen eingeschränkt, dann ist
sie nicht mehr an einen speziellen Provider wie ODBC oder
SqlNativeClient gebunden.

System.Common.DbProviderFactory
ist eine abstrakte Basisklasse für ClassFactorys. ClassFactorys
sind objekte, die selber wieder Instanzen von Objekten spezieller
Typen liefern. Für alle registrierten Provider wie ODBC oder
OLEDB gibt es spezielle ClassFactorys.

Auflistung
aller installierten Provider mittels DbProviderFactories
Die installierten Provider werden in der
machine.config aufgelistet:
<system.data>
<DbProviderFactories>
<add name="Odbc Data Provider" invariant="System.Data.Odbc" description=".Net Framework Data Provider for Odbc"
type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<add name="OleDb Data Provider" invariant="System.Data.OleDb" description=".Net Framework Data Provider for OleDb"
type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<add name="OracleClient Data Provider" invariant="System.Data.OracleClient" description=".Net Framework Data Provider for Oracle"
type="System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
<add name="SqlClient Data Provider" invariant="System.Data.SqlClient" description=".Net Framework Data Provider for SqlServer"
type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" />
<add name="SQL Server CE Data Provider" invariant="Microsoft.SqlServerCe.Client"
description=".NET Framework Data Provider for Microsoft SQL Server 2005 Mobile Edition"
type="Microsoft.SqlServerCe.Client.SqlCeClientFactory, Microsoft.SqlServerCe.Client, Version=9.0.242.0,
Culture=neutral, PublicKeyToken=89845dcd8080cc91" />
</DbProviderFactories>
</system.data>
Mit den statischen Methoden der Klasse
System.Common.DbProviderFactories können Einträge
aus machine.config als ClassFactorys aufgelistet, und mit
GetFactory(Invarianter-Providername) kann eine spezielle
ClassFactory instanziiert werden.
Besipiel für Instanziierung providerspezifischer ADO.NET Typen
mittels ClassFactory
// Instanziieren einer ClassFactory
System.Data.Common.DbProviderFactory dbFactory = System.Data.Common.DbProviderFactories.GetFactory("System.Data.SqlClient");
// Erzeugen Providerspezifischer ADO.NET Objekte, auf denen mit allg. Basisklassenreferenzen verwiesen wird
System.Data.Common.DbConnection conn = dbFactory.CreateConnection();
// Verbindungsobjekt instanziieren
conn.ConnectionString = connStrBld.ConnectionString;
// Kommandoobjekt instanziieren
cmd = dbFactory.CreateCommand();
cmd.Connection = conn;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "data.AddRecord";
// Kommandoparameter instanziieren
DbParameter pRecord = cfactory.CreateParameter();
pRecord.ParameterName = "@record";
pRecord.DbType = DbType.Xml;
pRecord.Direction = ParameterDirection.Input;
cmd.Parameters.Add(pRecord);
Abbildung der Datenbank- Datentypen auf die .NET Datentypen
Datenaustausch über DbParameter- Objekte
Die Datenbankbefehle werden mit Objekten
parametriert, die von der Klasse DbParameter abgeleitet sind. Diese
kapseln die Konvertierung von NET in Datenbankspezifische Typen,
indem jeder spezielle Datenbankprovider eine von DbParameter
abgeleitete Klasse implementiert.

Im
folgenden ist die Klassenhirarchie von DbParameter dargestellt:

Automatische Abblidung der Parametertypen auf Datenbanktypen
Jeder
Datenbank hat ihre speziellen Datentypen. Beim Zugriff auf die Daten
über .NET Programme müssen die .NET Typen in die
Datenbanktypen konvertiert werden. Diese Aufgabe übernehmen die
speziellen Implementierungen der DbParameter Klasse in den einzelnen
Datenbankprovidern.
Die
Konvertierung kann völlig transparent erfolgen. Dabei wird der
Typ der Value- Eigenschaft eines Parameters bestimmt. Gemäß
dieser Typinformation erfolgt dann zur Laufzeit die Konvertierung in
den Datenbanktypen. Der Entwickler kann in diesen Prozess eingreifen,
indem er der Eigenschaft DbType einen Wert aus der Enumeration
System.Data.DbType zuweist. Hierdurch wird die automatisierte
Konvertierung etwas verfeinert.
Die
folgende Tabelle stammd aus der MSDN 2005 beschreibt die
Abbildungsvorschrift von .NET Typen für verschiedene
Datanbankprovider:
.NET Framework-Typ
|
System.Data.DbType
|
SqlDbType
|
OleDbType
|
OdbcType
|
OracleType
|
bool
|
Boolean
|
Bit
|
Boolean
|
Bit
|
Byte
|
byte
|
Byte
|
TinyInt
|
UnsignedTinyInt
|
TinyInt
|
Byte
|
byte[]
|
Binary
|
VarBinary. . Diese implizite
Konvertierung schlägt fehl, wenn das Bytearray die
maximale Länge eines VarBinary übersteigt,
d. h. 4000 Bytes. Legen Sie für Bytearrays mit mehr
als 8000 Bytes explizit den SqlDbType fest.
|
VarBinary
|
Binary
|
Raw
|
char
|
|
Das Herleiten von einem SqlDbType von
char wird nicht unterstützt.
|
Char
|
Char
|
Byte
|
DateTime
|
DateTime
|
DateTime
|
DBTimeStamp
|
DateTime
|
DateTime
|
Decimal
|
Decimal
|
Decimal
|
Decimal
|
Numeric
|
Number
|
double
|
Double
|
Float
|
Double
|
Double
|
Double
|
float
|
Single
|
Real
|
Single
|
Real
|
Float
|
Guid
|
Guid
|
UniqueIdentifier
|
Guid
|
UniqueIdentifier
|
Raw
|
Int16
|
Int16
|
SmallInt
|
SmallInt
|
SmallInt
|
Int16
|
Int32
|
Int32
|
Int
|
Int
|
Int
|
Int32
|
Int64
|
Int64
|
BitInt
|
BigInt
|
BigInt
|
Number
|
object
|
Object
|
Variant
|
Variant
|
Das Herleiten von einem OdbcType von
Object wird nicht unterstützt.
|
Blob
|
string
|
String
|
NVarChar. Diese implizite
Konvertierung schlägt fehl, wenn die Zeichenfolge die
maximale Länge eines NVarChar übersteigt,
d. h. 4000 Zeichen. Legen Sie für Zeichenfolgen mit
mehr als 4000 Zeichen den SqlDbType explizit fest.
|
VarWChar
|
NVarChar
|
NVarChar
|
TimeSpan
|
Time
|
Das Herleiten von einem SqlDbType von
TimeSpan wird nicht unterstützt.
|
DBTime
|
Time
|
DateTime
|
UInt16
|
UInt16
|
Das Herleiten von einem SqlDbType von
UInt16 wird nicht unterstützt.
|
UnsignedSmallInt
|
Int
|
UInt16
|
UInt32
|
UInt32
|
Das Herleiten von einem SqlDbType von
UInt32 wird nicht unterstützt.
|
UnsignedInt
|
BigInt
|
UInt32
|
UInt64
|
UInt64
|
Das Herleiten von einem SqlDbType von
UInt64 wird nicht unterstützt.
|
UnsignedBigInt
|
Numeric
|
Number
|
|
AnsiString
|
VarChar
|
VarChar
|
VarChar
|
VarChar
|
|
AnsiStringFixedLength
|
Char
|
Char
|
Char
|
Char
|
|
Currency
|
Money
|
Currency
|
Das Herleiten von einem OdbcType von
Currency wird nicht unterstützt.
|
Number
|
|
Date
|
Das Herleiten von einem SqlType von
Date wird nicht unterstützt.
|
DBDate
|
Date
|
DateTime
|
|
SByte
|
Das Herleiten von einem SqlType von
SByte wird nicht unterstützt.
|
TinyInt
|
Das Herleiten von einem OdbcType von
SByte wird nicht unterstützt.
|
SByte
|
|
StringFixedLength
|
NChar
|
WChar
|
NChar
|
NChar
|
|
Time
|
Das Herleiten von einem SqlType von
Time wird nicht unterstützt.
|
DBTime
|
Time
|
DateTime
|
|
VarNumeric
|
Das Herleiten von einem SqlDbType von
VarNumeric wird nicht unterstützt.
|
VarNumeric
|
Das Herleiten von einem OdbcType von
VarNumeric wird nicht unterstützt.
|
Number
|
Explizite Abbildung der .NET Datentypen auf die Datanbanktypen
Die Datenbankprovider können Klassen
bereitstellen, mittels derer explizit die Konvertierung zwischen .NET
und Datenbanktypen durchgeführt werden kann.
Ein Beispiel hierfür ist Provider für
den MS- SQL Server. Dieser stellt unter dem dem Namensraum
System.Data.SqlTypes Konvertierungsklassen bereit. Z.B. kann
ein Konvertierung zwischen Datumstypen wie folgt erfolgen: Der Typ
SqlDateTime wird vom Provider bereitgestellt. Mittels eines
Konstruktoren können Zeitangaben aus .NET Zeitangaben für
SQL Server gewonnen werden. Umgekehrt werden mittels
Typkonvertierungsoperatoren aus SqlDateTime .NET DateTime- Werte
wieder zurückgewonnen:
using System.Data.SqlTypes;
// Aktuelles Datum in einen SQL Server DateTime- Wert konvertieren
SqlDateTime sqlTime = new SqlDateTime(DateTime.Now);
:
// Ein .NET- Datum aus einem SQL Server DateTime zurückgewinnen
DateTime netTime = (DateTime) sqlTime;
Phasen beim Zugriff auf Daten mittels ADO.NET
Die Verbindung und Interaktion mit einer
Datenquelle verteilt sich auf mehrere Phasen:
Verbindung aufbauen mittels XXXConnection
– Objekt herstellen
Datenquelle über Kommandos (z.B. SQL)
steuern. Dazu wird Kommando in einem XXXCommand- Objekt
definiert, dieses über XXXConection Objekt mit der Datenbank
verbunden und dann ausgeführt
Ergebnisse können über einen
Serverseitigen Cursor ausgelesen werden (XXXDataReader), oder
in einen Clientseitigen Cash geladen werden (DataSet).
Verbindungen aufbauen
Verbindungen werden über ein Objekt vom Typ
OleDbConnection aufgebaut.
Imports System.IO
Imports System.Xml.Serialization
Module m1Serialisierung
Public Sub Main()
' Verbindung mit Datenbank aufnehmen
Dim con As New SqlConnection
Try
With con
.ConnectionString = "Data Source=192.168.10.2;Database=geoinfo;User ID=sa;Password=sql92"
.Open()
' Verbindungsdaten ausgeben
Console.WriteLine("Datenquelle = {0}", .DataSource)
Console.WriteLine("Version = {0}", .ServerVersion)
Console.WriteLine("Akt. Datenbank = {0}", .Database)
Console.WriteLine("Verbindungszustand = {0}", .State)
End With
Catch ex As Odbc.OdbcException
Console.WriteLine("Es ist ein ODBC Fehler aufgetreten")
Console.WriteLine("{0}", ex.Message)
Catch ex As Exception
Console.WriteLine("Es ist ein allg. Fehler aufgetreten")
Console.WriteLine("{0}", ex.Message)
Finally
con.Close()
End Try
end module
Connection Strings
Attribut
|
Beschreibung
|
Beispiel
|
Provider
|
Treiber, über den die Verbindung
aufgebaut wird
|
"SQLDB"
|
Data Source
|
Name oder Netzwerkadresse der Serverinstanz
|
"192.168.10.2"
|
Database
|
Name der Datenbank
|
"geoinfo"
|
Integrated Security
|
Soll Windowsauthentifizierung durchgeführt
werden ? Mögliche Werte: true oder false
|
"false"
|
User ID
|
SQL- Serverkonto
|
"sa"
|
Password
|
Passwort für SQL- Serverkonto
|
"sql92"
|
Beim Zugriff über den SQL- Provider muß
auf die Angabe des Providers verzichtet werden.
Werden ODBC- Treiber benutzt, dann kann auf dem
System ein DSN (Data Source Name) eingerichtet
werden. Anstelle eines langen Connection- Strings reicht dann die
Angebe des DSN wie folgt:
' DSN sei geoinfo
con.ConnectionString = "DSN=geoinfo;"
DbConnectionsStringBuilder
Ab NET 2.0 gibt es die Klasse
DbConnectionsStringBuilder. Sie ist eine Basisklasse, die das
erstellen und bearbeiten von Verbindungszeichenfolgen vereinfacht.
Ableitungen existieren für die im NET mitgelieferten Datenbank-
Provider, welche sicherstellen, daß nur Providerspezifische
Verbindungszeichenfolgen gebildet werden können.

Schema
für den Umgang mit Verbindungszeichenfolgen
1) Speichern der Verbindungzeichenfolge in der
Konfigurationsdatei
Datenbankserver können auf verschiedene Art
und Weise auf einem System installiert und Administriert werden.
Dementsprechend individuell fallen auch die notwendigen
Verbindungszeichenfolgen zur Kontaktaufnahme mit dem Server aus.
Deshalb sollten Verbindungszeichenfolgen in Konfigurationsdateien
gesichert werden, damit der Administrator sie an die jeweilige
Serverumgebung anpassen kann. Beispiel:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
</configSections>
<connectionStrings>
<add name="AdoNetAsyncReader.Properties.Settings.dmsMinConnString"
connectionString="Data Source=.\Sqlexpress;Initial Catalog=dmsmin;Integrated Security=True"
providerName="System.Data.SqlClient" />
</connectionStrings>
</configuration>
2) Anpassen der Verbindungszeichenfolge mit einem
ConnectionStringBuilder
Für die verschiedenen Datenbankprovider (z.B.
SQLServer oder Oracle) stellt ADO.NET spezifische
ConnectionStringBuilder- Klassen bereit, die die speziellen
Einstellungen der Verbindungen kapseln. So verfügen Verbindungen
zum Sql Datenbankserver über die Eigenschaft
MultipleActiveResultSet und
Verbindungen über den ODBC- Treiber über die Eigenschaft
DSN. Beispiel:
static void Main(string[] args)
{
// Verbindungszeichenfolge einlesen
SqlConnectionStringBuilder connBld = new SqlConnectionStringBuilder(
Properties.Settings.Default.dmsMinConnString);
// Verbindungszeichenfolge modifizieren
connBld.IntegratedSecurity = false;
connBld.UserID = "sa";
connBld.Password = "€uro08";
// Verbindung einrichten
using (SqlConnection conn = new SqlConnection(connBld.ConnectionString))
{
conn.Open();
// ...
}
}
Zugriff auf Daten mit DbCommand und DbDataReader
Wurden Select Abfragen an eine Datenbank
gesendet, so ist das Ergebnis in der Regel eine Menge von
Tabellenzeilen. Die Methode ExecuteReader liefert ein Objekt vom Typ
XXXDataReader zurück. Jeder Zugriff auf eine Zeile im Resultset
über XXXDataReader führt zu einem Abruf von der
Datenquelle:

Struktur
eines DataReaders

Beispiel für ein Zugriff auf die Datenquelle mittels
XXXCommand/Datareader
Im folgenden Quelltext wird die Beispieldatenbank
GeoInfo auf einem MySQL- Server geöffnet, und
anschließend die Tabelle Kennzeichen abgefragt.
Module Module1
Sub Main()
Dim con As New Odbc.OdbcConnection("DSN=geoinfo")
Try
con.Open()
Dim cmd As New Odbc.OdbcCommand
cmd.Connection = con
cmd.CommandText = "select * from kennzeichen where land like 'D%'"
Dim reader As Odbc.OdbcDataReader
reader = cmd.ExecuteReader()
If reader.HasRows Then
Console.WriteLine("Resultset nicht leer !")
Console.WriteLine("Anz der Spalten{0}", reader.FieldCount)
Dim i As Integer
For i = 0 To reader.FieldCount - 1
Console.Write("{0}" + vbTab, reader.GetName(i))
Next
Console.Write(vbCrLf)
' Ausgabe der Zeilen
While reader.Read
For i = 0 To reader.FieldCount - 1
Console.Write("{0}" + vbTab, reader.GetString(i))
Next
Console.Write(vbCrLf)
End While
reader.Close()
End If
Catch exc As Odbc.OdbcException
Console.WriteLine("Fehler:")
Console.WriteLine(exc.Message)
Catch exc As Exception
Console.WriteLine("Fehler")
Console.WriteLine(exc.Message)
Finally
con.Close()
End Try
Console.ReadLine()
End Sub
End Module
c# Beispiel:
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Configuration;
namespace AdoNetDataReaderArtikelDb
{
class Program
{
static void Main(string[] args)
{
try
{
DbProviderFactory dbFactory = DbProviderFactories.GetFactory("System.Data.SqlClient");
//DbProviderFactory dbFactory = DbProviderFactories.GetFactory("System.Data.OleDb");
//DbProviderFactory dbFactory = System.Data.SqlClient.SqlClientFactory.Instance();
// Zugriff auf die Connection- Strings in der App.Config- Datei
ConnectionStringSettingsCollection connSettColl = ConfigurationManager.ConnectionStrings;
ConnectionStringSettings MySetting = connSettColl["ArtikelDB"];
DbConnection conn = dbFactory.CreateConnection();
// Eine Verindungszeichenfolge mittels Builder aufbauen
DbConnectionStringBuilder dbConnBld = dbFactory.CreateConnectionStringBuilder();
//dbConnBld.Add("Provider", "SQLDB");
dbConnBld.Add("Data Source", "R2004");
dbConnBld.Add("Database", "DBArtikel");
dbConnBld.Add("Integrated Security", "True");
//MySetting = null;
if (MySetting != null)
conn.ConnectionString = MySetting.ConnectionString;
else
//conn.ConnectionString = "Data Source=R2004;Initial Catalog=DBArtikel;Integrated Security=True";
conn.ConnectionString = dbConnBld.ConnectionString;
DbCommand cmd = dbFactory.CreateCommand();
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "dbo.ListeAlleArtikelZumLieferanten";
cmd.Connection = conn;
DbParameter param1 = dbFactory.CreateParameter();
param1.ParameterName = "@lieferant";
param1.DbType = DbType.AnsiString;
param1.Size = 1000;
param1.Direction = ParameterDirection.Input;
cmd.Parameters.Add(param1);
// Optimierung: Kommando wird auf dem DBMS vorkompiliert
cmd.Prepare();
// Kommando ausführen
cmd.Parameters["@lieferant"].Value = "BMW";
conn.Open();
//DbDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);
System.Data.SqlClient.SqlDataReader reader = (System.Data.SqlClient.SqlDataReader)cmd.ExecuteReader(CommandBehavior.CloseConnection);
int artNr = 0;
string ProduktName = "";
int vorrat = 0;
System.Data.SqlTypes.SqlMoney preis;
while (reader.Read())
{
artNr = reader.GetInt32(0);
ProduktName = (string)reader["ProduktName"];
vorrat = (int)reader["vorrat"];
preis = reader.GetSqlMoney(4);
string outStr = string.Format("ArtNr={0:N0}\tP:{1}\tV:{2:N0}\tPreis:{3:N2}", artNr, ProduktName, vorrat, (double)preis.Value);
Console.WriteLine(outStr);
}
}
catch (DbException ex)
{
Console.WriteLine("Fehler: " + ex.Message);
Console.WriteLine("Source: " + ex.Source);
}
Console.ReadLine();
}
}
}
Behandeln von
Nullwerten
Datenbanken bieten einen besonderen Spaltenwert
an, der keinem gültigen Wert eines Datentyps entspricht. Dies
ist der Null- Wert. Durch ihn wird eine nichtinitialisierte
Datenbankspalte beschrieben.
Der Nullwert einer Datenbankspalte ist nicht das
gleiche wie eine Null- Referenz in C#. Nullwerte aus Datenbanken
werden durch die Klasse System.DBNull dargestellt. Beispiel:
SqlCommand cmdMaxId = new SqlCommand();
cmdMaxId.Connection = conDMS;
cmdMaxId.CommandText = "select max(dir_id) from dirs";
// Im Falle einer Leeren Tabelle liefert die Abfrage einen Nullwert zurück
object obj = cmdMaxId.ExecuteScalar();
if (obj != System.DBNull.Value)
DirId = (int) obj + 10000;
else
DirId = 1;
XXXDataReader-
Einschränkungen
Datensätze können nicht gezielt
angesprungen werden
Die Ergebnismenge kann nicht rückwärts
durchlaufen werden
Daten in der Datenbank könne nicht
geändert werden
MARS: Programmieren verschachtelter DataReader mittels Multiple
Active Result Sets auf SQL Server 2005
Allgemein kann über eine Verbindung nur ein
Resultset auf dem Server abgerufen werden. Sollen aber zu allen
Datensätzen einer Elterntabelle alle zugehörigen Datensätze
in der Kindtabelle ausgegeben werden, dann müssen die Resultsets
der Eltern, wie auch der Kindtabelle über eine Verbindung
gesteuert werden. MARS ermöglicht dies auf SQL- Server 2005. Das
Feature wird über die Verbindungszeichenfolge eingestellt.
Beispiel:
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
namespace AdoNetMars
{
class Program
{
static void Main(string[] args)
{
try
{
// Durch MARS (Multiple Active Result Sets) kann mit verschachtelten
// Readern auf dem SQL- Server 2005 gearbeitet werden
DbConnectionStringBuilder connBld = new DbConnectionStringBuilder();
connBld.Add("Data Source", @".\SQLEXPRESS");
connBld.Add("Initial Catalog", "DBArtikel");
connBld.Add("Integrated Security", "True");
// Wenn folgende Zeile auskommentiert ist, dann können verschachtelte DataReader nicht programmiert werden
//connBld.Add("MultipleActiveResultSets", "True");
using (SqlConnection conn = new SqlConnection(connBld.ConnectionString))
{
// Iteration über alle Lieferanten
SqlCommand cmdLf = new SqlCommand("select * from dbo.Lieferanten order by name", conn);
cmdLf.CommandType = CommandType.Text;
conn.Open();
using (SqlDataReader readerLf = cmdLf.ExecuteReader())
{
// Speziell beim SqlReader kann geprüft werden, ob ein nichtleeres Resultset zurückgegeben wurde
if (readerLf.HasRows)
{
// Ausgabe des Tabellenkopfes
Console.WriteLine("{0,5} {1,5} {2,6} {3,10}", "Lieferant", "artNr", "vorrat", "preis");
while (readerLf.Read())
{
string LieferantName = (string)readerLf["name"];
int LieferantenNr = (int)readerLf["lfnr"];
// Erzeugen eines zweiten Resultsets speziell zum Lieferanten
SqlCommand cmdArt = new SqlCommand("select * from dbo.Artikel where lfnr = @lfnr", conn);
cmdArt.CommandType = CommandType.Text;
cmdArt.Parameters.AddWithValue("@lfnr", LieferantenNr);
using (SqlDataReader readerArt = cmdArt.ExecuteReader())
{
if (readerArt.HasRows)
{
while (readerArt.Read())
{
int artNr = (int)readerArt["artnr"];
int vorrat = (int)readerArt["vorrat"];
System.Data.SqlTypes.SqlMoney preis = readerArt.GetSqlMoney(4);
Console.WriteLine("{0,5} {1,5:D} {2,6:D} {3,10:N}", LieferantName, artNr, vorrat, (double)preis.Value);
}
}
else
{
Console.WriteLine("Zum Lieferanten {0:D} existieren keine Artikel", LieferantenNr);
}
}// using readerArt
}
}
else
{
Console.WriteLine("Die Lieferantentabelle ist leer");
}
} // using readerLf
} // using conn
}
catch (SqlException ex)
{
Console.WriteLine("SQL- Fehler: Schweregrad {0:D}, Fehler {1:D}: {2}", ex.Number, ex.State, ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("Allg Fehler: {0}", ex.Message);
}
}
}
}
Asynchrone Ausführung von DataReader mittels BeginExecuteReader
Abfragen, deren
Ausführung länger dauern als die üblichen
Reaktionszeiten von usern (< 1 Sec) können auch asynchron
gestartet werden. Dies geschieht durch die schon von den Delagates
bekannten Mwethodenpaare Begin..., End....
static void Main(string[] args)
{
// Verbindungszeichenfolge einlesen
SqlConnectionStringBuilder connBld = new SqlConnectionStringBuilder(
Properties.Settings.Default.dmsMinConnString);
// Verbindungszeichenfolge modifizieren: Asynchrone Verarbeitung aktivieren
connBld.AsynchronousProcessing = true;
// Verbindung einrichten
using (SqlConnection conn = new SqlConnection(connBld.ConnectionString))
{
conn.Open();
// Folgende Abfrage benötigt die meiste Zeit bei der Ausführung auf dem Server
string sql = "waitfor delay '0:00:20:00'; select [path] from data.files where taxonomy_of_content like 'picture/%'";
// Folgende Abfrage benötigt viel Zeit auf dem Server und nochmehr Zeit beim Abruf der Datensätze
//string sql = "select data.PathOf(hierarchy_id) from data.files where taxonomy_of_content like 'picture/%'";
SqlCommand cmd = new SqlCommand(sql, conn);
// Aufbau des Readers asynchron starten
IAsyncResult res = cmd.BeginExecuteReader();
while (!res.IsCompleted)
{
Console.Write("Daten noch nicht eingelesen. {0:T}\r", DateTime.Now);
System.Threading.Thread.Sleep(1000);
}
SqlDataReader reader = cmd.EndExecuteReader(res);
Console.WriteLine("Alle Datensätze eingelesen");
Console.WriteLine("Ergebnis");
int line = 0;
while (reader.Read())
{
Console.WriteLine("{0,5:D}: {1}", line++, reader.GetString(0));
}
}
}
Warten auf die Fertigstellung über WaitHandles
// Verbindungszeichenfolge einlesen
SqlConnectionStringBuilder connBld = new SqlConnectionStringBuilder(Properties.Settings.Default.dmsMinConnString);
// Verbindungszeichenfolge modifizieren: Asynchrone Verarbeitung aktivieren
connBld.AsynchronousProcessing = true;
connBld.MultipleActiveResultSets = true;
//-------------------------------------------------------------------------
// Wait- Handles
string sql1 = "waitfor delay '0:00:10:00'; select 'Query A' as Col";
string sql2 = "waitfor delay '0:00:15:00'; select 'Query B' as Col";
cmd.CommandText = sql1;
SqlCommand cmd2 = new SqlCommand(sql2, conn);
res = cmd.BeginExecuteReader();
IAsyncResult res2 = cmd2.BeginExecuteReader();
System.Threading.WaitHandle[] waitHandleList = { res.AsyncWaitHandle, res2.AsyncWaitHandle };
for (int countWaits = 0; countWaits < waitHandleList.Length; countWaits++)
{
int ix = System.Threading.WaitHandle.WaitAny(waitHandleList, 60000, false);
switch (ix)
{
case 0:
using(SqlDataReader reader = cmd.EndExecuteReader(res)) {
if(reader.Read()) {
Console.WriteLine("{0:T}: {1}", DateTime.Now, reader.GetString(0));
}
}
break;
case 1:
using(SqlDataReader reader = cmd2.EndExecuteReader(res2)) {
if(reader.Read()) {
Console.WriteLine("{0:T}: {1}", DateTime.Now, reader.GetString(0));
}
}
break;
case System.Threading.WaitHandle.WaitTimeout:
Console.WriteLine("Timeout");
break;
}
}
}
Einlesen
großer Datenwerte (LOBs)
static void Main(string[] args)
{
// Verbindungszeichenfolge einlesen
SqlConnectionStringBuilder connBld = new SqlConnectionStringBuilder(Properties.Settings.Default.dmsMinConnString);
// Verbindungszeichenfolge modifizieren
// Verbindung einrichten
using (SqlConnection conn = new SqlConnection(connBld.ConnectionString))
{
conn.Open();
string sql = "select img from dbo.FotosView where path = @path";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.AddWithValue("@path", @"F:\TRAC19\trac\projekt\lernen-dot-net\Bildergalerie\Diverse\Mann-im-Reinraum.jpg");
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
if (reader.Read())
{
if (!reader.IsDBNull(0))
{
SqlBytes bytes = reader.GetSqlBytes(0);
using (System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(bytes.Stream))
{
bmp.Save("Bild.gif", System.Drawing.Imaging.ImageFormat.Gif);
}
} else {
Console.WriteLine("Bildspalte ist leer");
}
} else {
Console.WriteLine("Kein Datensatz gefunden");
}
}
}
}
NonQuerys über ein Command- Objekt abwickeln
SQL- Kommando mit Parametern definieren
Parametern Werte zuweisen und NonQuery ausführen
DateTime TimeAppStart = DateTime.Now;
try
{
// Verbindung öffnen
this.sqlConLogbuch.Open();
sqlCmdInsertAppStart.Parameters["@AppName"].Value = "moc5-asp-anwendung";
sqlCmdInsertAppStart.Parameters["@obj"].Value = "global.asax";
sqlCmdInsertAppStart.Parameters["@t"].Value = new SqlDateTime(TimeAppStart);
sqlCmdInsertAppStart.Parameters["@info"].Value = "Anwendung startet";
int anz_rows = sqlCmdInsertAppStart.ExecuteNonQuery();
} catch (Exception ex) {
throw new ApplicationException("Datenbankfehler: " + ex.Message);
}
DataSet
Arbeiten mit den Command- und Datareader- Objekten
entspricht einer interaktiven Sitzung am Datenbankserver.
Insbesondere bei hochbelasteten Systemen und vielschichtigen
Architekturen kann dies eine negative Performance bedeuten, muß
doch jedesmal gewartet werden, bis der Server antwortet. Die Lösung
für dieses Problem ist da DataSet. Ein DataSet ist ein
Datenbankcache auf dem lokalen System. Mittels eines Adapters wird es
mit einer Teilmenge von Daten aus dem fernen Server gefüllt, die
wiederum durch ein XXXCommand- Objekt definiert sind. Anschließend
können die Daten lokal manipuliert werden, ohne das Antworten
vom Server abgewartet werden müssen. Das Dataset protokolliert
alle Änderungen. Durch ein Update können die Änderungen
schließlich wieder an den Server zurückgesendet werden.

DataSets und Datenbank mit ADO.NET DataAdapter synchronisieren
(NET 1.x)
Ein Tabelle aus einem DataSet wird mit einem
Adapter- Objekt gefüllt. Man könnte den Adapter auch wie
einen Zuweisungsoperator von Datenbank an Tabelle im DataSet ansehen.

Um
ein DataSet mit Daten aus einer Datenbank zu füllen, reicht die
Referenz auf ein XXXCommand- Objekt mit einer gültigen select...
Anweisung. Beispiel:
Dim con As New Odbc.OdbcConnection("DSN=geoinfo")
Try
con.Open()
Dim cmd As New Odbc.OdbcCommand(con)
Dim ds As New DataSet
Dim adapter As New Odbc.OdbcDataAdapter
' Dataset mit 2 Tabellen füllen
With adapter
.SelectCommand = cmd
.Fill(ds, "kennzeichen")
cmd.CommandText = "select * from laender"
.Fill(ds, "laender")
End With
:
Um die Orginaldatensätze mit den geänderten Daten im
Dataset zu synchronisieren, müssen korrekte XXXCommand- Objekte
für Update, Delete und Insert gebildet werden:
Dim con As New OdbcConnection("DSN=adr1")
Dim cmdSelect As New OdbcCommand("select name, plz from adressen")
Dim cmdUpdate As New OdbcCommand("update adressen set name = ?, plz = ? where name = ?")
Dim cmdDelete As New OdbcCommand("delete from adressen where name = ?")
Dim cmdInsert As New OdbcCommand("insert into adressen (name, plz) values(?, ?)")
Dim adapter As New OdbcDataAdapter
Dim ds As New DataSet
Sub Main()
Try
'TODO: DML Kommandos definieren und an adapter zuweisen
cmdSelect.Connection = con
adapter.SelectCommand = cmdSelect
With cmdUpdate
.Connection = con
.Parameters.Add("@name", OdbcType.VarChar, 255, "name")
.Parameters.Add("@plz", OdbcType.Int, 4, "plz")
.Parameters.Add("@name_2", OdbcType.VarChar, 255, "name")
End With
adapter.UpdateCommand = cmdUpdate
With cmdDelete
.Connection = con
.Parameters.Add("@name", OdbcType.VarChar, 255, "name")
End With
adapter.DeleteCommand = cmdDelete
With cmdInsert
.Connection = con
.Parameters.Add("@name", OdbcType.VarChar, 255, "name")
.Parameters.Add("@plz", OdbcType.Int, 4, "plz")
End With
adapter.InsertCommand = cmdInsert
' DataSet ändern
:
' Dataset zurückschreiben
adapter.Update(ds, "adressen")
Catch ex As Exception
Console.WriteLine("Fehler: {0}", ex.Message)
End Try
end Sub
Datasets und Datenbank über TableAdapter synchronisieren
(NET 2.0)
In Visual- Studio 2005
wurde der DataDesigner umgestaltet. Die IDE kann jetzt sog.
TableAdapter Klassen aus typisierten DataTable- Klassen erzuegen,
welche Datenbankverbindungs- und DataAdapterobjekt sowie die
zugehörigen Kommandoobjekte zusammenfasst und streng typisierte
Update- und Fill- Methoden bereitstellt.
Ein Tableadapter wird
generiert, wenn aus dem Serverexplorer ein Tabellenobjekt in den
Dataset- Designer gezogen wird. Für bereits existierende
DataTable und Dataset- Klassen kann ein TabelAdapter neu erstellt
werden, indem aus der Toolbox des DataSet- Designers ein TabelAdapter
Objekt gezogen wird.
Die TableAdapter-
Klassen sind in einem separaten Namespace mit der Struktur
Projektnamespace.[Tabellenname]TableAdapters eingeschlossen.
Sie haben folgende allg. Struktur:

Objektmodell
von DataSet

Zugriff auf Tabellen, Zeilen und Spalten in einem DataSet
Durch Navigation über das Objektmodell kann
auf Zeilen und Spalten zugegriffen werden:
Dim con As New Odbc.OdbcConnection("DSN=vbadressen")
Try
con.Open()
Dim cmd As New Odbc.OdbcCommand("select name, plz from adressen", con)
Dim adp As New Odbc.OdbcDataAdapter(cmd)
Dim ds As New DataSet
adp.Fill(ds, "adressen")
' Ausgabe aller aktuellen Zeilen
Dim tab As DataTable
Dim i As Integer
For Each tab In ds.Tables
' Tabellenname ausgeben
Console.WriteLine("Tab: {0}", tab.TableName)
' Tabellenkopf ausgeben
Dim col As DataColumn
For Each col In tab.Columns
Console.Write(col.ColumnName + vbTab)
Next
Console.WriteLine("")
Dim row As DataRow
For Each row In tab.Rows
For i = 0 To tab.Columns.Count - 1
Console.Write(row(i).ToString())
Next
Console.WriteLine("")
Next
Next
con.Close()
Catch ex As Exception
Console.WriteLine("Allg. Fehler")
Console.WriteLine("{0}", ex.Message)
End Try
Zeilen einer Dataset- Tabelle hinzufügen
Zeilen einer Dataset- Tabelle editieren
Zeilen einer Dataset- Tabelle löschen
Beziehungen zwischen Tabellen festlegen
Sind in einem DataSet mehrere Tabellen geladen,
dann können zwischen diesen 1:n- Beziehungen (Relations)
definiert werden. Alle 1:N Beziehungen werden in der Collection
Relations von DataSet aufbewahrt. Jeder Eintrag in dieser
Collection ist vom Typ DataRelation, der folgenden Aufbau
besitzt:

Im
folgenden Beispiel wird die Beziehung zwischen den Tabellen
kennzeichen und energieverbrauch aus der
Übungsdatenbank geoinfo definiert und für die
Berechnung des Gesamtenergieverbrauchs von Deutschland ausgenutzt.
Achtung: Die
Beziehungen in Datasets können erst dann ihre Wirkung entfalten,
wenn die Parent, als auch die Child- Tabelle mit Daten aus der
Datenbank geladen wurden !
Dim con As New OdbcConnection("DSN=geoinfo")
Try
con.Open()
Dim cmd As New OdbcCommand
cmd.Connection = con
Dim adapter As New OdbcDataAdapter
cmd.CommandText = "select * from kennzeichen where land = 'Deutschland'"
adapter.SelectCommand = cmd
' Tabellen kennzeichen und energieverbrauch in ds füllen
Dim ds As New DataSet
adapter.Fill(ds, "kennzeichen")
cmd.CommandText = "select * from energieverbrauch"
adapter.SelectCommand = cmd
adapter.Fill(ds, "energieverbrauch")
' Referenzen auf die Tabellen anlegen
tabKz = ds.Tables("kennzeichen")
tabEv = ds.Tables("energieverbrauch")
' Beziehung definieren
Dim parentCol, childCol As DataColumn
parentCol = tabKz.Columns("kz")
childCol = tabEv.Columns("kz_land")
Dim relKzEv As New DataRelation("ev_von_land", parentCol, childCol)
ds.Relations.Add(relKzEv)
' Aufsummieren des Energieverbrauches von Deutschland
If tabKz.Rows.Count > 0 Then
Dim parentRow As DataRow
parentRow = tabKz.Rows(0)
Dim childRow As DataRow
Dim summe As Double
For Each childRow In parentRow.GetChildRows("ev_von_land")
summe += childRow("verbrauch")
Next
Console.WriteLine("{0} hatte eines Gesamtverbrauch von {1}", parentRow("land"), summe)
End If
Console.ReadLine()
Catch ex As Exception
Console.WriteLine("Allg. Fehler: {0}", ex.Message)
End Try
Daten aus DataSet in Tabelle zurückschreiben
Wurden in einem Dataset Daten geändert, so
können diese durch den Adapter wieder zurückgeschrieben
werden. Dazu dient die Methode adapter.Update(...). Die
Aktualisierung auf dem Datenbankserver erfolgt wiederum über
Kommandos (z.B. SQL). Die Kommandos müssen in den Eigenschaften
UpdateCommand, InsertCommand und DeleteCommand des
Adapters hinterlegt sein. Diese können manuell oder automatisch
erstellt worden sein.
Zur automatischen
Erstellung wird die Klasse XXXCommandBuilder verwendet. Eine Instanz
eines CommandBuilders muß erzeugt werden, und beim
Instanziieren ist der Adapter im Konstruktor zu übergeben. Im
Adapter muß zuvor das SelectCommand definiert worden sein. Der
Commandbuilder erstellt dann automatisch die die fehlenden Kommandos.
Imports System.Data.Odbc
Module Module1
Dim con As New OdbcConnection("DSN=adr1")
Dim cmdSelect As New OdbcCommand("select name, plz from adressen")
Dim cmdUpdate As New OdbcCommand("update adressen set name = ?, plz = ? where name = ?")
Dim cmdDelete As New OdbcCommand("delete from adressen where name = ?")
Dim cmdInsert As New OdbcCommand("insert into adressen (name, plz) values(?, ?)")
Dim adapter As New OdbcDataAdapter
Dim ds As New DataSet
Sub Main()
Try
' DML Kommandos definieren und an adapter zuweisen
cmdSelect.Connection = con
adapter.SelectCommand = cmdSelect
With cmdUpdate
.Connection = con
.Parameters.Add("@name", OdbcType.VarChar, 255, "name")
.Parameters.Add("@plz", OdbcType.Int, 4, "plz")
.Parameters.Add("@name_2", OdbcType.VarChar, 255, "name")
End With
adapter.UpdateCommand = cmdUpdate
With cmdDelete
.Connection = con
.Parameters.Add("@name", OdbcType.VarChar, 255, "name")
End With
adapter.DeleteCommand = cmdDelete
With cmdInsert
.Connection = con
.Parameters.Add("@name", OdbcType.VarChar, 255, "name")
.Parameters.Add("@plz", OdbcType.Int, 4, "plz")
End With
adapter.InsertCommand = cmdInsert
' DataSet füllen
adapter.Fill(ds, "adressen")
' 2 Zeile hinzufügen
Dim newRow As DataRow = ds.Tables("adressen").NewRow
newRow("name") = "new1 " + Now.ToString
newRow("plz") = 99
ds.Tables("adressen").Rows.Add(newRow)
Dim newRow2 As DataRow = ds.Tables("adressen").NewRow
newRow2("name") = "new2 " + Now.ToString
newRow2("plz") = 100
ds.Tables("adressen").Rows.Add(newRow2)
' 2. Zeile ändern
ds.Tables("adressen").Rows(1)("plz") = 1000
' 1. Zeile löschen
ds.Tables("adressen").Rows(0).Delete()
' Dataset zurückschreiben
adapter.Update(ds, "adressen")
Catch ex As Exception
Console.WriteLine("Fehler: {0}", ex.Message)
End Try
Console.WriteLine("Daten in mysql/adr1/adressen geändert")
Console.ReadLine()
End Sub
End Module
Sortieren und Filtern mittels DataViews über DataSet
So, wie in einer SQL- Select- Anweisung Datensätzt
über where – Klauseln gefiltert, und über
Order By – Klauseln sortiert werden können, sind
auch Tabellen in einem DataSet über ein DataView Objekt filter
und sortierbar. Den sort und RowFilter Eigenschaften
können Ausdrücke zugewiesen werden, die weitgehend denen
aus SQL entsprechen.

Im
folgenden Beispiel wird die Tabelle kennzeichen aus der
Übungsdatenbank geoinfo vollständig in ein
DataSet eingelesen. Dieses wird anschließend gefiltert mittels
einer DataView nach allen Ländern, die mit D beginnen.
Dim con As New OdbcConnection("DSN=geoinfo")
Try
con.Open()
Dim cmd As New OdbcCommand
cmd.Connection = con
Dim adapter As New OdbcDataAdapter
cmd.CommandText = "select * from kennzeichen"
adapter.SelectCommand = cmd
' Tabellen kennzeichen und energieverbrauch in ds füllen
Dim ds As New DataSet
adapter.Fill(ds, "kennzeichen")
Dim view As New DataView(ds.Tables("kennzeichen"))
view.RowFilter = "kz like 'D%'"
Dim rowview As DataRowView
For Each rowview In view
Console.Write("{0,-12}{1,-12}", rowview(0).ToString(), rowview(1).ToString())
Console.WriteLine("")
Next
Console.ReadLine()
Catch ex As Exception
Console.WriteLine("Allg. Fehler: {0}", ex.Message)
Finally
con.Close()
End Try
Tabelle aus DataSet als XML- Dokument ausgeben
Dim con As New OdbcConnection("DSN=geoinfo")
Try
con.Open()
Dim cmd As New OdbcCommand
cmd.Connection = con
Dim adapter As New OdbcDataAdapter
cmd.CommandText = "select * from kennzeichen"
adapter.SelectCommand = cmd
' Tabellen kennzeichen und energieverbrauch in ds füllen
Dim ds As New DataSet
adapter.Fill(ds, "kennzeichen")
' Öffnen einer Datei und schreiben als xml
Dim datXml As New FileStream("d:\trac\projekt\lernen-dot-net\kz.xml", FileMode.Create)
'Dim datXsd As New FileStream("d:\trac\projekt\lernen-dot-net\kz.xsd", FileMode.Create)
ds.WriteXml(datXml, XmlWriteMode.WriteSchema)
datXml.Close()
Console.WriteLine("Schema geschrieben")
Console.ReadLine()
Catch ex As Exception
Console.WriteLine("Allg. Fehler: {0}", ex.Message)
Finally
con.Close()
End Try
XML-
Dokumente in DataSet einlesen
Dim ds As New DataSet
' Öffnen einer Datei und einlesen in ein DataSet
Dim datXml As New FileStream("d:\trac\projekt\lernen-dot-net\kz.xml", FileMode.Open)
ds.ReadXml(datXml, XmlReadMode.Auto)
datXml.Close()
XML- Typen
ADO.NET und
Steuerelemente
Datenquellen an Eigenschaften von Steuerelementen binden
Alle Steuerelemente sind von der Klasse Control
abgeleitet, und erben von dieser die Collection
ControlBindingCollection. Diese ist eine Menge aus
System.Windows.Forms.Binding Objekten. Jedes Binding Objekt
beschreibt eine Verknüpfung zwischen einer
Steueelementeigenschaft und einer Datenquelle. Solange

DataGrid

Transaktionen
Transaktionen
auf DataSets

Transaktionen
auf DataTables und DataRows
Zustände einer DataRow

Zugriff
auf Zustandsabhängige Versionen einer Datarow

Transaktionen
auf Datenbanken

Crystal
Reports
Das Standard- Tool in Visual Studio zum erstellen
von Datenbankberichten ist Crystal Reports.
Zugriff auf ODBC- Quellen
Beispiel: Anlegen eines Berichts, der die
Speicherplatzbelegung pro Unterverzeichnis graphisch darstellt
Erstellen von Berichten über Datasets
DataSets können in Visual Studio
automatisiert aus Datenbankdefinitionen oder XML- Schema- Dateien
gewonnen werden. Mittels des Berichtsassistenten von Crystal Reports
kann aus einem DataSet wiederum ein Bericht erzeugt werden. Die
folgende Zeichnung veranschaulicht diesen Prozess:
