© Martin Korneffel, Stuttgart 2003 +++ email: trac@n.zgs.de +++ web: www.s-line.de/homepages/trac

ADO.NET

Inhalt

  1. ADO.NET Übersicht

  2. ADO.NET- Bibliotheken

    1. Zugriff über OleDB

    2. Zugriff auf SQL- Server

    3. Zugriff auf ODBC- Quellen

  3. Datenbankprovider - unabhängiger Code durch das Common- Model (NET 2.0)

    1. DbProviderFactory

    2. Auflistung aller installierten Provider mittels DbProviderFactories

    3. Besipiel für Instanziierung providerspezifischer ADO.NET Typen mittels ClassFactory

  4. Abbildung der Datenbank- Datentypen auf die .NET Datentypen

    1. Datenaustausch über DbParameter- Objekte

    2. Automatische Abblidung der Parametertypen auf Datenbanktypen

    3. Explizite Abbildung der .NET Datentypen auf die Datanbanktypen

  5. Phasen beim Zugriff auf Daten mittels ADO.NET

  6. Verbindung aufbauen

    1. Connection Strings

      1. DbConnectionsStringBuilder

    2. Schema für den Umgang mit Verbindungszeichenfolgen

  7. Zugriff auf Daten mit DbCommand und DbDataReader

    1. Beispiel für ein Zugriff auf die Datenquelle mittels XXXCommand/Datareader

    2. Struktur eines DataReaders

    3. Behandeln von Nullwerten

    4. XXXDataReader- Einschränkungen

    5. MARS: Programmieren verschachtelter DataReader mittels Multiple Active Result Sets auf SQL Server 2005

    6. Asynchrone Ausführung von DataReader mittels BeginExecuteReader

      1. Warten auf die Fertigstellung über WaitHandles

    7. Einlesen großer Datenwerte (LOBs)

  8. NonQuerys über ein Command- Objekt abwickeln

    1. SQL- Kommando mit Parametern definieren

    2. Parametern Werte zuweisen und NonQuery ausführen

  9. DataSet

    1. DataSets und Datenbank mit ADO.NET DataAdapter synchronisieren (NET 1.x)

    2. Datasets und Datenbank über TableAdapter synchronisieren (NET 2.0)

    3. Objektmodell von DataSet

    4. Zugriff auf Tabellen, Zeilen und Spalten in einem DataSet

    5. Zeilen einer Dataset- Tabelle hinzufügen

    6. Zeilen einer Dataset- Tabelle editieren

    7. Zeilen einer Dataset- Tabelle löschen

    8. Beziehungen zwischen Tabellen festlegen

    9. Daten aus DataSet in Tabelle zurückschreiben

  10. Sortieren und Filtern mittels DataViews über DataSet

  11. Tabelle aus DataSet als XML- Dokument ausgeben

  12. XML- Dokumente in DataSet einlesen

    1. XML- Typen

  13. ADO.NET und Steuerelemente

    1. Datenquellen an Eigenschaften von Steuerelementen binden

    2. DataGrid

  14. Transaktionen

    1. Transaktionen auf DataSets

    2. Transaktionen auf Datenbanken

  15. Crystal Reports

    1. Zugriff auf ODBC- Quellen

    2. 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:

  1. Verbindung aufbauen mittels XXXConnection – Objekt herstellen

  2. 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

  3. 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

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: