Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
1 Einführung
2 Grundlagen der Sprachsyntax
3 Klassendesign
4 Weitere Datentypen
5 Multithreading
6 Collections und LINQ
7 Eingabe und Ausgabe
8 Anwendungen: Struktur und Installation
9 Code erstellen und debuggen
10 Einige Basisklassen
11 Windows-Anwendungen erstellen
12 Die wichtigsten Steuerelemente
13 Tastatur- und Mausereignisse
14 MDI-Anwendungen
15 Grafiken mit GDI+
16 Drucken
17 Entwickeln von Steuerelementen
18 Programmiertechniken
19 WPF – Grundlagen
20 Layoutcontainer
21 WPF-Steuerelemente
22 Konzepte von WPF
23 Datenbankverbindung mit ADO.NET
24 Datenbankabfragen mit ADO.NET
25 DataAdapter
26 Offline mit DataSet
27 Datenbanken aktualisieren
28 Stark typisierte DataSets
A Anhang: Einige Übersichten
Stichwort

Buch bestellen
Ihre Meinung?

Spacer
<< zurück
Visual Basic 2008 von Andreas Kuehnel, Stephan Leibbrandt
Das umfassende Handbuch
Buch: Visual Basic 2008

Visual Basic 2008
3., aktualisierte und erweiterte Auflage, geb., mit DVD
1.323 S., 49,90 Euro
Rheinwerk Computing
ISBN 978-3-8362-1171-0
Pfeil 27 Datenbanken aktualisieren
Pfeil 27.1 Aktualisieren mit CommandBuilder
Pfeil 27.1.1 Parallelitätskonflikt
Pfeil 27.1.2 Aktualisierungsbefehle des DbCommandBuilders
Pfeil 27.1.3 Aktualisierungsoptionen des DbCommandBuilders
Pfeil 27.1.4 Vor- und Nachteile des DbCommandBuilders
Pfeil 27.2 Manuell gesteuerte Aktualisierungen
Pfeil 27.2.1 Manuelles Aktualisieren mit dem DataAdapter
Pfeil 27.2.2 Aktualisieren mit ExecuteNonQuery
Pfeil 27.3 Benutzer über fehlgeschlagene Aktualisierungen informieren
Pfeil 27.4 Konfliktverursachende Datenzeilen bei der Datenbank abfragen
Pfeil 27.5 DataSet mit der Datenbank synchronisieren
Pfeil 27.5.1 UpdatedRowSource in DbCommand
Pfeil 27.6 Hierarchische Änderungen an die Datenbank übermitteln
Pfeil 27.6.1 Datenbank auslesen
Pfeil 27.6.2 Änderung
Pfeil 27.6.3 Bestellung einfügen
Pfeil 27.6.4 Bestelldetails einfügen
Pfeil 27.6.5 Wiederherstellen der Datenbank


Galileo Computing - Zum Seitenanfang

27.5 DataSet mit der Datenbank synchronisieren Zur nächsten ÜberschriftZur vorigen Überschrift

Bisher habe ich Ihnen gezeigt, wie Sie eine im DataSet geänderte Datenzeile in der Originaldatenbank aktualisieren können. Es gibt aber auch Situationen, in denen nach einer Aktualisierung von der Datenbank für die betroffene Datenzeile neue Werte abgefragt werden müssen. Wenn wir beispielsweise der Tabelle Products einen weiteren Artikel hinzufügen, interessiert uns, welchen Primärschlüssel die Datenbank für den neuen Artikel vergeben hat, da es sich um einen Autoinkrementwert handelt. Vielleicht hat Ihre Tabelle auch eine Timestamp-Spalte. Nach einer Änderung generiert die Datenbank einen neuen Wert, den Sie in Ihrer Datenzeile eintragen müssen.

Eine typische weitere Situation ergibt sich daraus, dass das UPDATE-Kommando neue Werte für einzelne Spalten – und nur für diese – spezifiziert. Der Rest der Zeile bleibt unberührt. Zum Beispiel ändert der Befehl

UPDATE Products SET ProductName = @Artikel WHERE ProductID = @ID

nur den Produktnamen. Auch wenn diese Aktualisierung erfolgreich ist, kann ein anderer Anwender die Spalte UnitPrice zwischenzeitlich editiert haben. Daher gibt die betreffende Datenzeile in Ihrem DataSet dann nicht den tatsächlichen Stand wieder.

Sie sehen, man kann sich eine Reihe von Situationen vorstellen, die es erforderlich machen, den Inhalt einer Datenzeile zu aktualisieren, nachdem eine Änderung übermittelt worden ist.


Galileo Computing - Zum Seitenanfang

27.5.1 UpdatedRowSource in DbCommand topZur vorigen Überschrift

Update aktualisiert nicht nur die Datenbank, sondern prüft anschließend auch die Eigenschaft UpdatedRowSource des Command-Objekts, um festzustellen, wie nach einer erfolgreichen Aktualisierung eventuell von Command zurückgelieferte Werte auf die DataRow angewendet werden sollen.

Doch welche Werte werden zurückgeliefert? Wie Sie wissen, gehört der SQL Server zu der Gruppe von Datenbankservern, die mit Batch-Abfragen umgehen können. Als ich Ihnen die Klasse DataSet vorgestellt habe, setzten wir Batch-Abfragen ein, um das DataSet-Objekt in einem Schritt mit mehreren Tabellen zu füllen.

SELECT * FROM Products; 
SELECT * FROM Categories

Mit Batch-Abfragen können Sie nach der Aktualisierung zum Beispiel neue Daten abrufen:

UPDATE Products SET ProductName=@Name 
  WHERE ProductID=@ID AND ProductName=@OrgName; 
SELECT ProductID, ProductName, Unitprice, Discontinued 
  FROM Products WHERE ProductID = @ID;

Die Eigenschaft UpdatedRowSource des Command-Objekts legt fest, wie oder ob die vom Kommando zurückgelieferten Werte auf die DataRow angewendet werden sollen. Die Eigenschaft ist vom Typ der gleichnamigen Enumeration, die in Tabelle 27.4 beschrieben wird.


Tabelle 27.4 Konstanten der Enumeration »RowUpdatedSource«

Konstante Beschreibung

Both

DbCommand ruft neue Daten für die Zeile ab und verwendet dafür den ersten zurückgelieferten Datensatz und Ausgabeparameter. Dieses ist auch die Standardeinstellung.

FirstReturnedRecord

DbCommand liest neue Daten für die Zeile und verwendet dafür den ersten zurückgegebenen Datensatz.

None

DbCommand ruft keine neuen Daten für die Zeile ab.

OutputParameters

DbCommand liest neue Daten für die Zeile mit Ausgabeparametern.


Manuelle Synchronisation mit ExecuteReader

Sie können eine Batch-Abfrage selbst ausführen, indem Sie die Methode ExecuteReader des Command-Objekts aufrufen. Danach können Sie mit RecordsAffected des DataReaders ermitteln, wie viele Datenzeilen von der UPDATE-Abfrage betroffen sind. Handelt es sich um eine Datenzeile, rufen Sie die Read-Methode des Command-Objekts auf und können die neu ermittelten Werte in die betroffene DataRow eintragen. Zum Schluss rufen Sie AcceptChanges auf der DataRow auf, um die Version auf DataRowVersion.Original zu setzen.


'...\ADO\Aktualisierung\SynchronisationMitReader.vb

Option Strict On 
Imports System.Data.Common, System.Data.SqlClient 
Namespace ADO 
  Module SynchronisationMitReader 
    Sub Test() 
      Dim con As DbConnection = New SqlConnection() 
      con.ConnectionString = "Data Source=(local);" & _ 
        "Initial Catalog=Northwind;Integrated Security=sspi" 
      Dim cmd As DbCommand = New SqlCommand() 
      cmd.CommandText = "SELECT ProductID, ProductName, " & _ 
        "UnitPrice FROM Products" 
      cmd.Connection = con 
      Dim ds As New DataSet() 
      Dim da As DbDataAdapter = New SqlDataAdapter() 
      da.SelectCommand = cmd 
      da.Fill(ds)

      ' Datenzeilen editieren 
      Dim row As DataRow = ds.Tables(0).Rows(0) 
      row("ProductName") = "Früchtetee"

      ' externe Änderung simulieren 
      cmd.CommandText = _ 
        "UPDATE Products SET UnitPrice=16.00 WHERE ProductID=1" 
      con.Open() : cmd.ExecuteNonQuery() : con.Close()

      ' Änderungen durchführen 
      Dim orig As DataRowVersion = DataRowVersion.Original 
      ds.Tables(0).Select("ProductID=1")(0)("ProductName") = "Kräutertee" 
      Dim update As DbCommand = CreateUpdateCommand(con) 
      update.Parameters("@Name").Value = row("ProductName") 
      update.Parameters("@ID").Value = row("ProductID")

      con.Open() 
      Dim rd As DbDataReader = update.ExecuteReader() 
      If rd.RecordsAffected = 1 Then 
        While rd.Read() 
          row("ProductName") = rd("ProductName") 
          row("UnitPrice") = rd("UnitPrice") 
        End While 
      End If 
      con.Close()

      Console.WriteLine("""Orig"" : {0,-20}{1}", row(1, orig), row(2, orig)) 
      Console.WriteLine("Aktuell: {0,-20}{1}", row(1), row(2)) 
      row.AcceptChanges() 
      Console.WriteLine("""Orig"" : {0,-20}{1}", row(1, orig), row(2, orig)) 
      Console.ReadLine() 
    End Sub

    Function CreateUpdateCommand(ByVal con As DbConnection) As DbCommand 
      Dim cmd As DbCommand = New SqlCommand() 
      cmd.CommandText = "UPDATE Products SET ProductName=@Name " & _ 
        "WHERE ProductID=@ID" & ";" & _ 
        "SELECT ProductID, ProductName, Unitprice " & _ 
        "FROM Products WHERE ProductID = @ID" 
      cmd.Connection = con

      ' die Parameter der Parameters-Auflistung hinzufügen 
      Dim col As DbParameterCollection = cmd.Parameters 
      col.Add(New SqlParameter("@Name", SqlDbType.VarChar, 40, "ProductName")) 
      col.Add(New SqlParameter("@ID", SqlDbType.Int, 4, "ProductID")) 
      Return cmd 
    End Function 
  End Module 
End Namespace

In der ersten Datenzeile der Products-Tabelle wird der Produktname lokal geändert. Danach wird die Spalte UnitPrice des ersten Datensatzes ohne Mitwirkung des DataAdapters geändert. Diese Spalte ist zwar vom DataAdapter bezogen worden, hat sich aber im Code der Anwendung nicht verändert. In der Konsolenausgabe sehen Sie, dass sowohl der neue Produktname als auch der neue Einzelpreis der Datenzeile angezeigt wird.

"Orig" : Chai                18.0000 
Aktuell: Kräutertee          16.0000 
"Orig" : Kräutertee          16.0000

Automatische Synchronisation mit DataAdapter

Das Beispiel lässt sich deutlich vereinfachen, wenn Sie es dem DataAdapter überlassen, die Batch-Abfrage auszuführen. Da die Einstellung des Commands UpdatedRowSource.Both lautet, rufen Sie wie gehabt die Methode Update auf. Den Rest erledigt der DataAdapter.


'...\ADO\Aktualisierung\SynchronisationMitAdapter.vb

      ... 
      Dim update As DbCommand = CreateUpdateCommand(con) 
      da.UpdateCommand = update 
      da.Update(ds) 
      Console.WriteLine("""Orig"" : {0,-20}{1}", row(1, orig), row(2, orig)) 
      ...

Neue Autoinkrementwerte abrufen

Wenn Sie Ihrer DataTable, die auf einem autoinkrementellen Primärschlüsselwert basiert, eine neue Datenzeile hinzufügen, bekommt diese neue lokale Datenzeile einen temporären Primärschlüssel. Dieser dient nur dazu, die neue Datenzeile in der DataTable eindeutig identifizieren zu können. Aktualisieren Sie später die Datenbank mit einem INSERT INTO-Befehl, vergibt die Datenbank automatisch einen gänzlich neuen Primärschlüssel.

Nach der Aktualisierung sollten Sie diesen neuen endgültigen Primärschlüssel von der Datenbank beziehen. Tun Sie dies nicht, haben Sie Schwierigkeiten, die Datenzeile in der Datenbank eindeutig zu identifizieren, da Ihnen der neu vergebene Primärschlüssel nicht bekannt ist. Spätestens beim nächsten UPDATE-Kommando ist dies sehr ärgerlich.

Die Funktion SCOPE_IDENTITY() des SQL Servers gibt den letzten in der Sitzung automatisch generierten Identitätswert zurück, der in eine Spalte einer beliebigen Tabelle eingefügt wurde. Alternativ können Sie @@IDENTITY verwenden. Wir erhalten den Identitätswert, indem wir nach der Aktualisierungszeichenfolge des Commands die Funktion aufrufen.

INSERT INTO Products (ProductName, UnitPrice, Discontinued) 
  VALUES(@Name, @Preis, @Conti); 
SELECT @ID = SCOPE_IDENTITY()

Im folgenden Beispielprogramm wird der Products-Tabelle eine Datenzeile hinzugefügt und anschließend an der Konsole angezeigt. Beachten Sie bitte, dass in der Methode CreateInsertCommand der Parameter @ID als Ausgabeparameter spezifiziert ist.


'...\ADO\Aktualisierung\AutoInkrement.vb

Option Strict On 
Imports System.Data.Common, System.Data.SqlClient 
Namespace ADO 
  Module AutoInkrement 
    Sub UpdatedRowSource() 
      Dim con As DbConnection = New SqlConnection() 
      con.ConnectionString = "Data Source=(local);" & _ 
        "Initial Catalog=Northwind;Integrated Security=sspi" 
      Dim cmd As DbCommand = New SqlCommand() 
      cmd.CommandText = "SELECT ProductID, ProductName, " & _ 
        "UnitPrice, Discontinued FROM Products" 
      cmd.Connection = con 
      Dim ds As New DataSet() 
      Dim da As DbDataAdapter = New SqlDataAdapter() 
      da.SelectCommand = cmd 
      da.Fill(ds)

      ' Datenzeile hinzufügen 
      Dim neu As DataRow = ds.Tables(0).NewRow() 
      neu("ProductName") = "Camembert" 
      neu("UnitPrice") = 11.3 
      neu("Discontinued") = 0 
      ds.Tables(0).Rows.Add(neu)

      ' Datenbank aktualisieren 
      Dim insert As DbCommand = CreateInsertCommand(con) 
      insert.UpdatedRowSource = UpdateRowSource.OutputParameters 
      da.InsertCommand = insert 
      da.Update(ds) 
      Console.WriteLine("Neuer Primärschlüssel: {0}", neu("ProductID"))

      Console.ReadLine() 
    End Sub

    Function CreateInsertCommand(ByVal con As DbConnection) As DbCommand 
      Dim cmd As DbCommand = New SqlCommand() 
      cmd.CommandText = "INSERT INTO Products " & _ 
        "(ProductName,UnitPrice,Discontinued) " & _ 
        "Values(@Name,@Preis,@Conti)" & ";" & _ 
        "SELECT @ID = SCOPE_IDENTITY()" 
      cmd.Connection = con

      ' die Parameter der Parameters-Auflistung hinzufügen 
      Dim col As DbParameterCollection = cmd.Parameters 
      col.Add(New SqlParameter("@Name", SqlDbType.VarChar, 40, "ProductName")) 
      col.Add(New SqlParameter("@Preis", SqlDbType.Money, 8, "UnitPrice")) 
      col.Add(New SqlParameter("@Conti", SqlDbType.Bit, 1, "Discontinued")) 
      Dim param As DbParameter = _ 
        New SqlParameter("@ID", SqlDbType.Int, 0, "ProductID") 
      cmd.Parameters.Add(param) 
      param.Direction = ParameterDirection.Output 
      Return cmd 
    End Function 
    ... 
  End Module 
End Namespace

Bei jedem Programmlauf bekommen Sie einen neuen Wert:

Neuer Primärschlüssel: 86

Alternativ zu diesem Beispiel können Sie auch das Ereignis RowUpdated des DataAdapters dazu benutzen, den neuen Primärschlüssel abzurufen. Überprüfen Sie im Ereignishandler zuerst, in welchem Zustand sich die Aktualisierung befindet und ob zuvor ein INSERT abgesetzt worden ist. Dann erzeugen Sie ein passendes Command-Objekt und rufen dessen Methode ExecuteScalar auf, um den neuen Primärschlüssel abzufragen.

Im vorhergehenden Beispiel wurde der betreffenden Datenzeile im DataSet der neue Primärschlüssel zugewiesen, weil wir das Command-Objekt als Batch-Abfrage konstruiert hatten. Rufen wir im Ereignishandler von RowUpdated den Schlüsselwert ab, liegt keine Batch-Abfrage vor, und wir müssen den Schlüsselwert, der von der Spalte ProductID bezogen wurde, unserer Datenzeile im DataSet zuweisen.


'...\ADO\Aktualisierung\AutoInkrement.vb

Option Strict On 
Imports System.Data.Common, System.Data.SqlClient 
Namespace ADO 
  Module AutoInkrement 
    ... 
    Private con As DbConnection = New SqlConnection() 
    Sub Ereignis() ... 
    Sub Änderung(ByVal sender As Object, ByVal ev As RowUpdatedEventArgs) 
      If ev.Status = UpdateStatus.Continue AndAlso _ 
        ev.StatementType = StatementType.Insert Then 
        Dim cmd As DbCommand = New SqlCommand() 
        cmd.CommandText = "SELECT @@Identity" : cmd.Connection = con 
        ev.Row("ProductID") = cmd.ExecuteScalar() 
      End If 
    End Sub 
  End Module 
End Namespace


Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen. >> Zum Feedback-Formular
<< zurück
  Zum Katalog
Zum Katalog: Visual Basic 2008
Visual Basic 2008
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Visual Basic 2012






 Visual Basic 2012


Zum Katalog: Schrödinger programmiert C++






 Schrödinger
 programmiert C++


Zum Katalog: IT-Handbuch für Fachinformatiker






 IT-Handbuch für
 Fachinformatiker


Zum Katalog: Professionell entwickeln mit Visual C# 2012






 Professionell
 entwickeln mit
 Visual C# 2012


Zum Katalog: Windows Presentation Foundation






 Windows Presentation
 Foundation


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Rheinwerk Verlag GmbH 2009
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de