Freigeben über


Behandeln einer Parallelitäts ausnahme in .NET Framework-Datenbankanwendungen

Hinweis

Datasets und verwandte Klassen sind ältere .NET-Technologien aus den frühen 2000er Jahren, die es Anwendungen ermöglichen, mit Daten im Arbeitsspeicher zu arbeiten, während die Anwendungen von der Datenbank getrennt sind. Die Technologien sind besonders nützlich für Anwendungen, die es Benutzern ermöglichen, Daten zu ändern und die Änderungen wieder in der Datenbank zu speichern. Obwohl sich Datasets als sehr erfolgreiche Technologie erwiesen haben, empfehlen wir, dass neue .NET-Anwendungen Entity Framework Core verwenden. Entity Framework bietet eine natürlichere Möglichkeit, mit tabellarischen Daten als Objektmodelle zu arbeiten, und verfügt über eine einfachere Programmierschnittstelle.

Parallelitätsausnahmen (System.Data.DBConcurrencyException) werden ausgelöst, wenn zwei Benutzer gleichzeitig versuchen, dieselben Daten in einer Datenbank zu ändern. In dieser exemplarischen Vorgehensweise erstellen Sie eine Windows-Anwendung, die veranschaulicht, wie Sie eine DBConcurrencyException abfangen, die Zeile suchen, die den Fehler verursacht hat, und eine Strategie für den Umgang mit dem Fehler lernen.

Diese exemplarische Vorgehensweise enthält folgende Vorgänge:

  1. Erstellen eines neuen Windows Forms App (.NET Framework)-Projekts.

  2. Erstellen Sie ein neues Dataset basierend auf der Tabelle der Northwind-Kunden.

  3. Erstellen eines Formulars mit einer DataGridView für die Anzeige der Daten.

  4. Füllen Sie ein Dataset mit Daten aus der Tabelle „Customers“ in der Datenbank „Northwind“.

  5. Verwenden Sie das Feature Tabellendaten anzeigen in Server-Explorer, um auf die Daten der Kundentabelle zuzugreifen und einen Datensatz zu ändern.

  6. Ändern Sie dann einen Wert im gleichen Datensatz, aktualisieren Sie das Dataset und versuchen Sie, die Änderungen in die Datenbank zu schreiben. Dieser Vorgang führt zu einem Parallelitätsfehler.

  7. Fangen Sie den Fehler ab, und zeigen Sie anschließend die verschiedenen Versionen des Datensatzes an, um festzulegen, ob die Aktualisierung der Datenbank fortgesetzt oder abgebrochen werden soll.

Voraussetzungen

In dieser exemplarischen Vorgehensweise werden SQL Server Express LocalDB und die Northwind-Beispieldatenbank verwendet.

  1. Wenn Sie nicht über SQL Server Express LocalDB verfügen, installieren Sie diese Komponente entweder über die SQL Server Express-Downloadseite oder über den Visual Studio-Installer. Im Visual Studio-Installer können Sie SQL Server Express LocalDB als Teil der Workload Datenspeicherung und -verarbeitung oder als einzelne Komponente installieren.

  2. Installieren Sie die Northwind-Beispieldatenbank, indem Sie die folgenden Schritte ausführen:

    1. Öffnen Sie in Visual Studio das Fenster SQL Server-Objekt-Explorer. (Der SQL Server-Objekt-Explorer wird als Teil der Workload für die Datenspeicherung und -verarbeitung im Visual Studio-Installer installiert.) Erweitern Sie den Knoten SQL Server. Klicken Sie mit der rechten Maustaste auf Ihre LocalDB-Instanz, und wählen Sie Neue Abfrage aus.

      Ein Abfrage-Editor-Fenster wird geöffnet.

    2. Kopieren Sie das Northwind-Transact-SQL-Skript in die Zwischenablage. Dieses T-SQL-Skript erstellt die Northwind-Datenbank von Grund auf neu und füllt sie mit Daten auf.

    3. Fügen Sie das T-SQL-Skript in den Abfrage-Editor ein, und klicken Sie dann auf die Schaltfläche Ausführen.

      Nach kurzer Zeit wird die Ausführung der Abfrage abgeschlossen, und die Northwind-Datenbank wird erstellt.

Erstellen eines neuen Projekts

Beginnen Sie mit dem Erstellen einer neuen Windows Forms-Anwendung:

  1. Wählen Sie in Visual Studio im Menü Datei die Optionen Neu>Projekt aus.

  2. Erweitern Sie entweder Visual C# oder Visual Basic im linken Bereich, und wählen Sie dann Windows Desktop aus.

  3. Wählen Sie im mittleren Bereich den Projekttyp Windows Forms-App aus.

  4. Geben Sie dem Projekt den Namen ConcurrencyWalkthrough, und wählen Sie dann OK aus.

    Das Projekt ConcurrencyWalkthrough wird erstellt und dem Projektmappen-Explorer hinzugefügt, woraufhin ein neues Formular im Designer geöffnet wird.

Erstellen des Northwind-Datasets

Erstellen Sie als nächstes ein Dataset mit dem Namen NorthwindDataSet:

  1. Wählen Sie im Menü Daten die Option Neue Datenquelle hinzufügen aus.

    Der Assistent zum Konfigurieren von Datenquellen wird geöffnet.

  2. Wählen Sie auf dem Bildschirm Datenquellentyp auswählen die Option Datenbank aus.

    Assistent für die Datenquellenkonfiguration in Visual Studio

  3. Wählen Sie eine Verbindung mit der Northwind-Beispieldatenbank aus der Liste der verfügbaren Verbindungen aus. Wenn die Verbindung in der Liste der Verbindungen nicht verfügbar ist, wählen Sie Neue Verbindung aus.

    Hinweis

    Beim Herstellen einer Verbindung zu einer lokalen Datenbankdatei wählen Sie Nein aus, wenn Sie gefragt werden, ob die Datei Ihrem Projekt hinzugefügt werden soll.

  4. Wählen Sie auf der Seite Verbindungszeichenfolge in der Anwendungskonfigurationsdatei speichern die Option Weiter aus.

  5. Erweitern Sie den Knoten Tabellen, und wählen Sie die Tabelle Customers aus. Der Standardname für das Dataset sollte NorthwindDataSet sein.

  6. Wählen Sie Fertig stellen aus, um das Dataset zum Projekt hinzuzufügen.

Erstellen eines datengebundenen DataGridView-Steuerelements

In diesem Abschnitt erstellen Sie ein System.Windows.Forms.DataGridView, indem Sie das Customers-Objekt aus dem Datenquellenfenster auf das Windows-Formular ziehen.

  1. Wählen Sie zum Öffnen des Fensters Datenquellen im Menü Daten die Option Datenquellen anzeigen aus.

  2. Erweitern Sie im Fenster Datenquellen den Knoten NorthwindDataSet, und wählen Sie dann die Tabelle Customers aus.

  3. Wählen Sie den Pfeil nach unten auf dem Tabellenknoten und dann DataGridView in der Dropdownliste aus.

  4. Ziehen Sie die Tabelle auf einen leeren Bereich des Formulars.

    Ein DataGridView-Steuerelement namens CustomersDataGridView und ein BindingNavigator mit dem Namen CustomersBindingNavigator werden dem Formular hinzugefügt, das an die BindingSource gebunden ist. Dies ist wiederum an die Tabelle „Customers“ im NorthwindDataSet gebunden.

Testen des Formulars

Sie können das Formular jetzt testen, um sicherzustellen, dass das Verhalten bisher wie erwartet ausfällt:

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

    Das Formular wird mit einem DataGridView-Steuerelement angezeigt, das mit Daten aus der Tabelle „Customers“ gefüllt ist.

  2. Klicken Sie im Menü Debuggen auf Stop Debugging (Debuggen beenden).

Behandeln von Parallelitätsfehlern

Wie Sie Fehler behandeln, hängt von den jeweiligen Geschäftsregeln ab, die für Ihre Anwendung gelten. Für diese exemplarische Vorgehensweise verwenden wir die folgende Strategie als Beispiel für die Behandlung des Parallelitätsfehlers.

Die Anwendung bietet dem Benutzer drei Versionen des Datensatzes zur Auswahl:

  • Den aktuellen Datensatz in der Datenbank

  • Der ursprüngliche Datensatz, der in das Dataset geladen wird

  • Die vorgeschlagenen Änderungen im Dataset

Der Benutzer kann dann die Datenbank mit der vorgeschlagenen Version überschreiben oder aber die Aktualisierung abbrechen und das Dataset mit den neuen Werten aus der Datenbank aktualisieren.

So aktivieren Sie die Behandlung von Parallelitätsfehlern

  1. Erstellen Sie einen benutzerdefinierten Fehlerhandler.

  2. Zeigen Sie die Auswahloptionen für den Benutzer an.

  3. Verarbeiten Sie die Eingabe des Benutzers.

  4. Senden Sie die Aktualisierung erneut, oder setzen Sie die Daten im Dataset zurück.

Hinzufügen von Code zum Behandeln der Parallelitätsausnahme

Wenn bei einem Aktualisierungsversuch eine Ausnahme ausgelöst wird, sind die Informationen, die zusammen mit der ausgelösten Ausnahme angezeigt werden, in der Regel sehr hilfreich. In diesem Abschnitt fügen Sie Code hinzu, der versucht, die Datenbank zu aktualisieren. Sie behandeln auch jede DBConcurrencyException, die ausgelöst werden kann, sowie alle anderen Ausnahmen.

Hinweis

Die Methoden CreateMessage und ProcessDialogResults werden später in der exemplarischen Vorgehensweise hinzugefügt.

  1. Fügen Sie unter der Form1_Load-Methode folgenden Code hinzu:

    private void UpdateDatabase()
    {
        try
        {
            this.customersTableAdapter.Update(this.northwindDataSet.Customers);
            MessageBox.Show("Update successful");
        }
        catch (DBConcurrencyException dbcx)
        {
            DialogResult response = MessageBox.Show(CreateMessage((NorthwindDataSet.CustomersRow)
                (dbcx.Row)), "Concurrency Exception", MessageBoxButtons.YesNo);
    
            ProcessDialogResult(response);
        }
        catch (Exception ex)
        {
            MessageBox.Show("An error was thrown while attempting to update the database.");
        }
    }
    

  1. Ersetzen Sie die CustomersBindingNavigatorSaveItem_Click-Methode, um die UpdateDatabase-Methode aufzurufen, sodass sich Folgendes ergibt:

    private void customersBindingNavigatorSaveItem_Click(object sender, EventArgs e)
    {
        UpdateDatabase();
    }
    

Anzeigen der Auswahloptionen für den Benutzer

Durch den soeben geschriebenen Code wird die CreateMessage-Prozedur aufgerufen, um Fehlerinformationen für den Benutzer anzuzeigen. Für diese exemplarische Vorgehensweise verwenden Sie ein Meldungsfeld, um die verschiedenen Versionen des Datensatzes für den Benutzer anzuzeigen. Dadurch kann der Benutzer auswählen, ob der Datensatz mit den Änderungen überschrieben oder die Bearbeitung abgebrochen werden soll. Wenn der Benutzer durch Klicken auf eine Schaltfläche eine Option im Meldungsfeld ausgewählt hat, wird die Antwort an die ProcessDialogResult-Methode übergeben.

Erstellen Sie die Meldung, indem Sie dem Code-Editor folgenden Code hinzufügen. Geben Sie diesen Code unter der UpdateDatabase-Methode ein:

private string CreateMessage(NorthwindDataSet.CustomersRow cr)
{
    return
        "Database: " + GetRowData(GetCurrentRowInDB(cr), DataRowVersion.Default) + "\n" +
        "Original: " + GetRowData(cr, DataRowVersion.Original) + "\n" +
        "Proposed: " + GetRowData(cr, DataRowVersion.Current) + "\n" +
        "Do you still want to update the database with the proposed value?";
}


//--------------------------------------------------------------------------
// This method loads a temporary table with current records from the database
// and returns the current values from the row that caused the exception.
//--------------------------------------------------------------------------
private NorthwindDataSet.CustomersDataTable tempCustomersDataTable = 
    new NorthwindDataSet.CustomersDataTable();

private NorthwindDataSet.CustomersRow GetCurrentRowInDB(NorthwindDataSet.CustomersRow RowWithError)
{
    this.customersTableAdapter.Fill(tempCustomersDataTable);

    NorthwindDataSet.CustomersRow currentRowInDb = 
        tempCustomersDataTable.FindByCustomerID(RowWithError.CustomerID);

    return currentRowInDb;
}


//--------------------------------------------------------------------------
// This method takes a CustomersRow and RowVersion 
// and returns a string of column values to display to the user.
//--------------------------------------------------------------------------
private string GetRowData(NorthwindDataSet.CustomersRow custRow, DataRowVersion RowVersion)
{
    string rowData = "";

    for (int i = 0; i < custRow.ItemArray.Length ; i++ )
    {
        rowData = rowData + custRow[i, RowVersion].ToString() + " ";
    }
    return rowData;
}

Verarbeiten der Antwort des Benutzers

Sie benötigen außerdem Code, um die Reaktion des Benutzers auf das Meldungsfeld zu verarbeiten. Der Benutzer kann entweder den aktuellen Datensatz in der Datenbank mit der vorgeschlagenen Änderung überschreiben oder die lokalen Änderungen verwerfen und die Datentabelle mit dem aktuell in der Datenbank vorhandenen Datensatz aktualisieren. Wenn der Benutzer Ja auswählt, wird die Merge-Methode aufgerufen, wobei das Argument preserveChanges auf true festgelegt ist. Der Aktualisierungsversuch verläuft somit erfolgreich, da die ursprüngliche Version des Datensatzes nun mit dem Datensatz in der Datenbank übereinstimmt.

Fügen Sie den folgenden Code unter dem im vorherigen Abschnitt hinzugefügten Code hinzu:

// This method takes the DialogResult selected by the user and updates the database 
// with the new values or cancels the update and resets the Customers table 
// (in the dataset) with the values currently in the database.

private void ProcessDialogResult(DialogResult response)
{
    switch (response)
    {
        case DialogResult.Yes:
            northwindDataSet.Merge(tempCustomersDataTable, true, MissingSchemaAction.Ignore);
            UpdateDatabase();
            break;

        case DialogResult.No:
            northwindDataSet.Merge(tempCustomersDataTable);
            MessageBox.Show("Update cancelled");
            break;
    }
}

Testen des Formularverhaltens

Sie können das Formular jetzt testen, um sicherzustellen, dass das Verhalten wie erwartet ausfällt. Um einen Parallelitätsverstoß zu simulieren, ändern Sie Daten in der Datenbank, nachdem Sie das NorthwindDataSet gefüllt haben.

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

  2. Wenn das Formular angezeigt wird, führen Sie es weiterhin, und wechseln Sie zur Visual Studio-IDE.

  3. Wählen Sie im Menü Ansicht den Eintrag Server-Explorer aus.

  4. Erweitern Sie im Server-Explorer die Verbindung, die von der Anwendung verwendet wird, und erweitern Sie den Knoten Tabellen.

  5. Klicken Sie mit der rechten Maustaste auf die Tabelle Customers, und wählen Sie Tabellendaten anzeigen aus.

  6. Ändern Sie im ersten Datensatz (ALFKI) ContactName in Maria Anders2.

    Hinweis

    Navigieren Sie zu einer anderen Zeile, um einen Commit für die Änderung auszuführen.

  7. Wechseln Sie zum ausgeführten Formular von ConcurrencyWalkthrough.

  8. Ändern Sie im ersten Datensatz auf dem Formular (ALFKI) ContactName in Maria Anders1.

  9. Klicken Sie auf die Schaltfläche Speichern.

    Der Parallelitätsfehler wird ausgelöst, und das Meldungsfeld wird angezeigt.

    Wenn Sie Nein auswählen, wird die Aktualisierung abgebrochen und das Dataset mit den Werten aktualisiert, die sich derzeit in der Datenbank befinden. Wenn Sie Ja auswählen, wird der vorgeschlagene Wert in die Datenbank geschrieben.