Exemplarische Vorgehensweise: Behandeln einer Parallelitätsausnahme mit Hilfe von Visual J#

In dieser exemplarischen Vorgehensweise wird eine Windows-Anwendung erstellt, die einen Parallelitätsfehler verursacht, sowie eine Strategie zur Behandlung dieses Fehlers vorgestellt. Die Vorgehensweise simuliert zwei Benutzer, die gleichzeitig mit denselben Daten arbeiten. Das später erstellte Windows Form ermöglicht es Ihnen, von einem Formular aus in der Rolle beider Benutzer zu agieren.

In dieser exemplarischen Vorgehensweise werden die folgenden Aufgaben veranschaulicht:

  1. Benutzer 1 und Benutzer 2 füllen ihre jeweiligen Datasets mit den gleichen Daten.
  2. Benutzer 2 bearbeitet einen Datensatz, aktualisiert das Dataset und schreibt die Änderungen in die Datenquelle.
  3. Benutzer 1 bearbeitet denselben Datensatz, aktualisiert das Dataset und versucht ebenfalls, die Änderungen in die Datenquelle zu schreiben. Als Folge tritt ein Parallelitätsfehler auf.

Der Fehler wird abgefangen, und die verschiedenen Versionen des Datensatzes werden angezeigt, so dass der Benutzer entscheiden kann, was mit den noch nicht festgeschriebenen Änderungen von Benutzer 1 geschehen soll.

Hinweis   Die Verwendung eines Datasets ist nur eine Möglichkeit für den Datenzugriff und ist bei manchen Szenarios nicht optimal. Jedoch sind Datasets normalerweise die richtige Wahl für Windows Forms-Anwendungen. Diese exemplarische Vorgehensweise zeigt ein Szenario, in dem Datasets geeignet sind. Weitere Informationen finden Sie unter Empfehlungen zur Zugriffsstrategie auf Daten.

Voraussetzungen für diese exemplarische Vorgehensweise:

  • Zugriff auf die Pubs SQL Server-Beispieldatenbank mit der Berechtigung zum Durchführen von Aktualisierungen.

Erstellen eines neuen Projekts und einer neuen Datenverbindung

Im ersten Schritt der exemplarischen Vorgehensweise erstellen Sie eine neue Windows-Anwendung in Visual J#.

So erstellen Sie ein neues Projekt

  1. Zeigen Sie im Menü Datei auf Neu, und klicken Sie dann auf Projekt, um das Dialogfeld Neues Projekt zu öffnen.

  2. Wählen Sie im Bereich Projekttypen die Option Visual J#-Projekte und anschließend Windows-Anwendung aus.

  3. Geben Sie dem Projekt den Namen concurrency_walkthrough, und klicken Sie auf OK.

    Visual Studio fügt im Projektmappen-Explorer das Projekt concurrency_walkthrough hinzu und zeigt im Designer ein neues Windows Form an.

So erstellen Sie eine neue Datenverbindung

  1. Erstellen Sie im Server-Explorer eine neue Verbindung zur Pubs-Beispieldatenbank. Weitere Informationen finden Sie unter Hinzufügen neuer Datenverbindungen im Server-Explorer.

  2. Erweitern Sie im Server-Explorer die im vorherigen Schritt erstellte Verbindung.

  3. Erweitern Sie den Bereich Tabellen.

  4. Ziehen Sie die Tabelle authors auf Ihr Formular.

    Im Komponentenfach unterhalb des Formulars werden ein Connection-Objekt und ein DataAdapter-Objekt angezeigt.

Erstellen der Datasets

In diesem Abschnitt erstellen Sie die die beiden Datasets DsAuthors1 und DsAuthors2. Diese Datasets stellen die Daten dar, mit denen die beiden Benutzer gleichzeitig arbeiten. Als Nächstes fügen Sie dem Formular zwei DataGrid-Steuerelemente hinzu und binden sie an die Datasets. Schließlich werden dem Formular zwei Button-Steuerelemente hinzugefügt: eine Schaltfläche Update und eine Schaltfläche Reset. Mit der Schaltfläche Update wird ein Datensatz in DsAuthors1 geändert sowie der Versuch unternommen, die Änderung in die Datenbank zu schreiben. Die Schaltfläche Reset setzt die Datenbank auf den ursprünglichen Datensatz zurück, so dass Sie die exemplarische Vorgehensweise auch mehrmals ausführen können.

So erstellen Sie zwei neue Datasets

  1. Wählen Sie das DataAdapter-Objekt aus.

  2. Wählen Sie im Menü Daten die Option Dataset generieren aus.

    Das Dialogfeld DataSet generieren wird angezeigt.

  3. Wählen Sie Neu aus, und geben Sie dem Dataset den Namen DsAuthors.

    Im Komponentenfach wird eine Instanz mit dem Namen DsAuthors1 angezeigt.

  4. Ziehen Sie von der Registerkarte Daten der Toolbox ein Dataset auf das Formular.

    Das Dialogfeld Dataset hinzufügen wird angezeigt.

  5. Bestätigen Sie die Auswahl von Typisiertes Dataset. Im Feld Name wird concurrency_walkthrough.DsAuthors angezeigt.

    Im Komponentenfach des Designers wird eine Instanz namens DsAuthors2 angezeigt.

Binden der Daten und Hinzufügen von Schaltflächen

Die Datenblätter dienen nur zum Anzeigen der Daten. In dieser exemplarischen Vorgehensweise sollten in den Datenblättern keine Daten bearbeitet werden.

So fügen Sie dem Formular zwei Datenraster hinzu

  1. Ziehen Sie von der Registerkarte Windows Forms der Toolbox ein DataGrid-Objekt auf die linke Seite des Formulars.
  2. Ziehen Sie von der Registerkarte Windows Forms der Toolbox ein DataGrid-Objekt auf die rechte Seite des Formulars.

So binden Sie die Datasets an die DataGrid-Steuerelemente

  1. Wählen Sie DataGrid1 aus, und legen Sie im Eigenschaftenfenster die folgenden Eigenschaften fest:

    Eigenschaft Einstellung
    DataSource DsAuthors1
    DataMember authors
    CaptionText DsAuthors1
  2. Wählen Sie DataGrid2 aus, und legen Sie im Eigenschaftenfenster die folgenden Eigenschaften fest:

    Eigenschaft Einstellung
    DataSource DsAuthors2
    DataMember Authors
    CaptionText DsAuthors2

So fügen Sie dem Formular die Schaltflächen "Update" und "Reset" hinzu

  1. Ziehen Sie von der Registerkarte Windows Forms der Toolbox ein Button-Steuerelement auf das Formular, und legen Sie es auf DataGrid1 ab.
  2. Geben Sie der Schaltfläche, während sie ausgewählt ist, im Eigenschaftenfenster den Namen btnUpdate, und setzen Sie die Text-Eigenschaft auf Update.
  3. Ziehen Sie von der Registerkarte Windows Forms der Toolbox ein zweites Button-Objekt auf das Formular, und legen Sie es auf DataGrid2 ab.
  4. Geben Sie der Schaltfläche, während sie ausgewählt ist, im Eigenschaftenfenster den Namen btnReset, und setzen Sie die Text-Eigenschaft auf Reset.

Zurücksetzen der Datenbank

Wenn Sie die exemplarische Vorgehensweise mehr als einmal ausführen möchten, müssen Sie dem Formular Code hinzufügen, durch den die Datenbank auf bekannte Werte zurückgesetzt wird.

So fügen Sie Code zum Zurücksetzen der Datenbank hinzu

  • Klicken Sie mit der rechten Maustaste auf das Formular, wählen Sie im Kontextmenü Code anzeigen aus, und fügen Sie dann folgenden Code über Form1 des Konstruktors ein:

    // Visual J#
    private void resetDatabase()
    {
       // Fill the dsAuthors dataset with data.
       sqlDataAdapter1.Fill(dsAuthors1);
       // Reset the au_fname in the first row to "John".
       dsAuthors1.get_authors().get_Item(0).set_au_fname("John");
       // Write the record back to the database.
       sqlDataAdapter1.Update(dsAuthors1);
       // Close the connection.
    sqlConnection1.Close();  
    }
    

Füllen der Datasets

In diesem Schritt werden beide Datasets mit denselben Daten gefüllt.

So fügen Sie Code zum Füllen der Datasets aus der Datenbank hinzu

  • Fügen Sie folgenden Code in den Code-Editor ein:

    // Visual J#
    private void filltheDataSets() 
    {
       sqlDataAdapter1.Fill(dsAuthors1);
       sqlDataAdapter1.Fill(dsAuthors2);
       sqlConnection1.Close(); 
    }
    

Simulieren von Änderungen durch Benutzer 2

So fügen Sie Code ein, um Änderungen durch Benutzer 2 zu simulieren

  • Fügen Sie folgenden Code in den Code-Editor ein:

    // Visual J#
    private void user2changes() 
    {
       // Simulate a second user changing a record.
       dsAuthors2.get_authors().get_Item(0).set_au_fname("User 2");
       // Write it back to the database.
       sqlDataAdapter1.Update(dsAuthors2.GetChanges());
       // Refresh dsAuthors2 with the updated data.
       sqlDataAdapter1.Fill(dsAuthors2);
    // Close the connection..
    sqlConnection1.Close();
    }
    

Erstellen des Form_Load-Ereignishandlers

So fügen Sie dem Form_Load-Ereignis Code zum Initialisieren der exemplarischen Vorgehensweise hinzu

  1. Doppelklicken Sie in der Entwurfsansicht auf einen leeren Formularbereich, um den Form_Load-Ereignishandler automatisch zu erstellen.

  2. Fügen Sie im Ereignishandler folgenden Code hinzu:

    // Visual J#
    private void Form1_Load(Object sender, System.EventArgs e)
    {
       resetDatabase();
       filltheDataSets();
       user2changes();
    }
    
  3. Speichern Sie das Projekt.

Ausführen der Anwendung

  1. Drücken Sie F5, um die Anwendung auszuführen.

    Das Formular wird mit zwei Datenblättern angezeigt, die mit Daten aus der Tabelle authors in der Pubs-Datenbank gefüllt werden. Im Feld au_fname des ersten Datensatzes in DsAuthors1 sollte John stehen. Im Feld au_fname des ersten Datensatzes in DsAuthors2 sollte User 2 stehen.

    Das Formular sieht in etwa folgendermaßen aus:

  2. Wählen Sie im Menü Debuggen die Option Debug beenden aus.

Aktualisieren des Datasets und Schreiben der Änderungen in die Datenbank

Nun schreiben Sie Code, mit dessen Hilfe versucht wird, die Datenbank mit den Änderungen im Dataset DsAuthors1 zu aktualisieren. Wenn die Aktualisierung erfolgreich verläuft, wird die AcceptChanges-Methode des Datasets aufgerufen, und in einem Meldungsfeld wird eine Erfolgsmeldung angezeigt. Schlägt die Aktualisierung aus irgendeinem Grund fehl, wird der Fehler abgefangen, und in einem Meldungsfeld wird die Fehlermeldung angezeigt, wobei der Fehlerobjekttyp dem Titel des Meldungsfeldes entspricht. Da diese exemplarische Vorgehensweise darauf abzielt, einen Parallelitätsfehler zu verursachen, wird der Vollständigkeit halber das Feld mit der Erfolgsmeldung angezeigt.

Hinweis   Dieser Try...Catch-Block fängt alle Fehler ab. Im weiteren Verlauf der exemplarischen Vorgehensweise werden Sie eine weitere Catch-Anweisung hinzufügen, um den Parallelitätsfehler auf bestimmte Weise zu behandeln.

So aktualisieren Sie die Datenbank

  1. Rufen Sie die Update-Methode auf.
  2. Erstellen Sie einen Ausnahmehandler, durch den ein Meldungsfeld angezeigt wird.

Der Code muss wie folgt aussehen:

// Visual J#
private void updateDatabase() 
{
   try
   {
      // Update the database with the changes from dsAuthors1.
      sqlDataAdapter1.Update(dsAuthors1.GetChanges());
      dsAuthors1.AcceptChanges();
   sqlConnection1.Close();  
      MessageBox.Show("The update was successful.");
   }
   catch (System.Exception ex)
   {
      // Display information about update errors.
      MessageBox.Show(ex.get_Message(), ex.GetType().ToString());
   }
}

Als nächstes fügen Sie Code hinzu, durch den die Spalte au_fname im Dataset DsAuthors1 geändert wird. Der Code ruft dann die updateDatabase-Prozedur auf, um diese Änderung in die Datenbank zu schreiben. Da der Wert zuvor von Benutzer 2 geändert wurde, wird ein Parallelitätsfehler verursacht.

So aktualisieren Sie das Dataset "DsAuthors1"

  1. Doppelklicken Sie auf die Schaltfläche Update.

  2. Erstellen Sie den btnUpdate_Click-Ereignishandler:

    // Visual J#
    private void btnUpdate_Click(Object sender, System.EventArgs e)
    {
       // Change au_fname in the first row of dsAuthors1 to "User 1".
       dsAuthors1.get_authors().get_Item(0).set_au_fname("User 1");
       updateDatabase();
    }
    
  3. Speichern Sie das Projekt.

  4. Drucken Sie F5, um die Anwendung auszuführen.

  5. Klicken Sie auf die Schaltfläche Update, um das Feld au_fname des ersten Datensatzes in User 1 zu ändern.

    Der Parallelitätsfehler wird verursacht.

Behandeln von Parallelitätsfehlern

Wie Sie den Fehler behandeln, hängt von den jeweiligen Geschäftsregeln ab, nach denen die Anwendung abläuft. Die folgende Fehlerbehandlungsstrategie wird zur Veranschaulichung verwendet. Die Anwendung bietet dem Benutzer drei Versionen des Datensatzes zur Auswahl:

  • Den aktuellen Datensatz in der Datenbank
  • Den ursprünglichen Datensatz im Dataset
  • Die vorgeschlagenen Änderungen im Dataset

Der Benutzer kann dann entweder die Datenbank mit der vorgeschlagenen Änderung überschreiben oder die Aktualisierung verwerfen und das Dataset aktualisieren.

Erstellen eines benutzerdefinierten Fehlerhandlers

Um Fehler abzufangen, werden Aktualisierungen in der Regel in einem strukturierten Ausnahmehandler durchgeführt. Im oben verwendeten Code wurde ein try...catch-Block verwendet, um alle Fehler abzufangen – d. h., eine Struktur, die eine allgemeine catch-Anweisung für beliebige Fehler enthält.

Sie können auch bestimmte Fehler abfangen, so dass Sie in bestimmter Weise darauf reagieren können. Zur Veranschaulichung wird in dieser exemplarischen Vorgehensweise ein Ausnahmehandler für einen bestimmten Fehler hinzugefügt — nämlich für einen Parallelitätsfehler, den Sie mit dem DbConcurrencyException-Objekt überprüfen. In diesem Beispiel behandeln Sie diesen Fehler, indem Sie dem Benutzer Daten anzeigen.

So fügen Sie eine spezielle Behandlung für den DBConcurrencyException-Fehler hinzu

  1. Wenn die Anwendung noch ausgeführt wird, verlassen Sie den Ausführmodus, um zum Code-Editor zurückzukehren.

  2. Fügen Sie der updateDatabase-Methode über der vorhandenen catch-Anweisung eine zweite hinzu.

  3. Übergeben Sie das DBConcurrencyException-Objekt an die createMessage-Prozedur, die im nächsten Abschnitt erstellt wird.

    // Visual J#
    private void updateDatabase() 
    {
       try 
       {
          // Update the database with the changes from dsAuthors1.
          sqlDataAdapter1.Update(dsAuthors1.GetChanges());
          dsAuthors1.AcceptChanges();
          MessageBox.Show("The update was successful!");
       }
       catch (DBConcurrencyException dbcx)
       {
          createMessage(dbcx);
       }
       catch (System.Exception ex)
       {
          MessageBox.Show(ex.get_Message(), ex.GetType().ToString());
       }
    }
    

Anzeigen der Auswahloptionen für den Benutzer

Durch den soeben geschriebenen Code wird die createMessage-Prozedur aufgerufen, um Fehlerinformationen für den Benutzer anzuzeigen. In dieser exemplarischen Vorgehensweise wird ein Meldungsfeld verwendet, um dem Benutzer die verschiedenen Versionen des Datensatzes anzuzeigen. Er kann daraufhin auswählen, ob er den Datensatz mit den neuesten Änderungen überschreibt oder die Bearbeitung abbricht.

Hinweis   Der Einfachheit halber wird in dieser exemplarischen Vorgehensweise ein zweites Dataset (DsAuthors2) als Datenquelle verwendet, um den aktuellen Datensatz in der Datenbank abzurufen. In einer realen Anwendung würden Sie die tatsächliche Datenquelle erneut abfragen, um den aktuellen Wert des Datensatzes abzurufen, der den Fehler ausgelöst hat.

So erstellen Sie die createMessage-Prozedur

  • Erstellen Sie den Fehlerhandler, indem Sie dem Code-Editor folgenden Code hinzufügen:

    // Visual J#
    private void createMessage(DBConcurrencyException dbcx) 
    {
       // Declare variables to hold the row versions for display 
       // in the message box.
       System.String strInDs = "Original record in dsAuthors1:\n";
       System.String strInDB = "Current record in database:\n";
       System.String strProposed = "Proposed change:\n";
       System.String strPromptText = "Do you want to overwrite the current " + 
        "record in the database with the proposed change?\n";
       System.String strMessage;
       System.Windows.Forms.DialogResult response;
    
       // Loop through the column values.
       DataRow rowInDB = dsAuthors2.get_authors().FindByau_id(dbcx.get_Row().get_Item("Au_ID").ToString());
       for (int i = 0; i < dbcx.get_Row().get_ItemArray().length; i++)
       {
          strInDs = strInDs + dbcx.get_Row().get_Item(i, DataRowVersion.Original) + "\n";
          strInDB = strInDB + rowInDB.get_Item(i, DataRowVersion.Current) + "\n";
          strProposed = strProposed + dbcx.get_Row().get_Item(i, DataRowVersion.Current) + "\n";
    
       }
    
       // Create the message box text string.
       strMessage = strInDs + "\n" + strInDB + "\n" + strProposed + "\n" + strPromptText;
       // Display the message box.
       response = MessageBox.Show(strMessage, dbcx.get_Message(),MessageBoxButtons.YesNo);
       processResponse(response);
    }
    

Verarbeiten der Eingabe des Benutzers

Sie benötigen außerdem Code, um die Reaktion des Benutzers auf das Meldungsfeld zu verarbeiten. Der Benutzer kann entscheiden, ob der aktuelle Datensatz in der Datenbank mit der vorgeschlagenen Änderung überschrieben wird oder nicht. Wenn der Benutzer die Änderung beibehalten möchte, wird die Merge-Methode von DsAuthors1 aufgerufen, wobei das preserveChanges-Argument den Wert true hat. Dadurch werden die ursprünglichen Versionen der Datenzeilen in DsAuthors2 mit den aktuellen Versionen der Datenzeilen in DsAuthors1 zusammengeführt. Der Aktualisierungsversuch verläuft somit erfolgreich, da die ursprünglichen Versionen des Datensatzes nun mit denen der Datenbank übereinstimmen.

So verarbeiten Sie die Benutzereingabe aus dem Meldungsfeld

  • Fügen Sie dem Code-Editor folgenden Code hinzu:

    // Visual J#
    private void processResponse(System.Windows.Forms.DialogResult response)
    {
       // Execute the appropriate code depending on the button selected 
       // in the message box.
       if(response == System.Windows.Forms.DialogResult.Yes)
       {
          dsAuthors1.Merge(dsAuthors2, true);
          sqlDataAdapter1.Update(dsAuthors1);
          dsAuthors1.AcceptChanges();
       }
       else if (response == System.Windows.Forms.DialogResult.No)
       {
          dsAuthors1.Merge(dsAuthors2);
       }
    }
    

Zurücksetzen der Daten

Um das Formular zurückzusetzen, wird die Form1_Load-Methode geändert, so dass sie ausgeführt wird, wenn der Benutzer auf die Schaltfläche Reset klickt.

**Hinweis   **Eine Methode kann als Ereignishandler für mehrere Ereignisse fungieren, die mehreren Objekten zugehörig sind. Weitere Informationen finden Sie unter Verbinden mehrerer Ereignisse mit einem einzelnen Ereignishandler in Windows Forms.

So erstellen Sie einen Ereignishandler für die btnReset-Schaltfläche

  1. Öffnen Sie das Formular im Designer.

  2. Klicken Sie auf die Schaltfläche Reset.

  3. Klicken Sie auf der Symbolleiste des Eigenschaftenfensters auf die Schaltfläche Ereignisse.

  4. Suchen Sie das Click-Ereignis, und klicken Sie dann auf den Pfeil, um alle Methoden anzuzeigen, die auf das Ereignis reagieren können.

    In diesem Fall wird Form1_Load angezeigt, da es die korrekte Signatur für ein button.click-Ereignis besitzt.

  5. Wählen Sie Form1_Load aus.

    Der Code zum Verknüpfen der Form1_Load-Methode mit dem btnReset.Click-Ereignis wird automatisch erstellt. Form1_Load reagiert nun sowohl auf Form1.Load als auch auf btnReset.Click.

    **Tipp   **Um den erstellten Code anzuzeigen, doppelklicken Sie auf das Formular und erweitern den abgeblendeten Bereich "Vom Windows-Formular-Designer generierter Code".

Ausführen der Anwendung

  1. Drucken Sie F5, um die Anwendung auszuführen.

  2. Klicken Sie auf die Schaltfläche Aktualisieren.

    Der Parallelitätsfehler wird ausgelöst und eine Meldung wird angezeigt.

Siehe auch

Hinzufügen neuer Datenverbindungen im Server-Explorer | Aktualisieren der Datenbank mit einem DataAdapter und dem DataSet | Dataset-Aktualisierungen in Visual Studio .NET | ADO.NET-Datasets | Parallelitätssteuerung in ADO.NET | Debuggen von verwaltetem Code | Weitergabe von Anwendungen | Exemplarische Vorgehensweisen zur Arbeit mit Daten