Share via


Behandeln der Parallelität mit dem Entity Framework 4.0 in einer ASP.NET 4-Webanwendung

von Tom Dykstra

Diese Tutorialreihe baut auf der Webanwendung der Contoso University auf, die vom Erste Schritte mit der Tutorialreihe Entity Framework 4.0 erstellt wird. Wenn Sie die vorherigen Tutorials nicht abgeschlossen haben, können Sie als Ausgangspunkt für dieses Tutorial die Anwendung herunterladen, die Sie erstellt hätten. Sie können auch die Anwendung herunterladen, die von der vollständigen Tutorialreihe erstellt wird. Wenn Sie Fragen zu den Tutorials haben, können Sie diese im ASP.NET Entity Framework-Forum veröffentlichen.

Im vorherigen Tutorial haben Sie gelernt, wie Sie Daten mithilfe des Steuerelements ObjectDataSource und des Entity Framework sortieren und filtern. In diesem Tutorial werden Optionen für die Behandlung von Parallelität in einer ASP.NET-Webanwendung gezeigt, die Entity Framework verwendet. Sie erstellen eine neue Webseite, die der Aktualisierung von Kursleiter-Office-Aufgaben gewidmet ist. Sie behandeln Parallelitätsprobleme auf dieser Seite und auf der Seite "Abteilungen", die Sie zuvor erstellt haben.

Abbildung06

Image01

Nebenläufigkeitskonflikte

Ein Parallelitätskonflikt tritt auf, wenn ein Benutzer einen Datensatz und ein anderer Benutzer denselben Datensatz bearbeitet, bevor die Änderung des ersten Benutzers in die Datenbank geschrieben wird. Wenn Sie das Entity Framework nicht zum Erkennen solcher Konflikte einrichten, überschreibt jeder, der die Datenbank zuletzt aktualisiert, die Änderungen des anderen Benutzers. In vielen Anwendungen ist dieses Risiko akzeptabel, und Sie müssen die Anwendung nicht so konfigurieren, dass mögliche Parallelitätskonflikte verarbeitet werden. (Wenn es nur wenige Benutzer oder wenige Updates gibt oder wenn einige Änderungen nicht wirklich kritisch sind, wenn einige Änderungen überschrieben werden, können die Kosten für die Programmierung für Die Parallelität den Vorteil überwiegen.) Wenn Sie sich keine Sorgen um Parallelitätskonflikte machen müssen, können Sie dieses Tutorial überspringen. Die restlichen beiden Tutorials in der Reihe hängen nicht von den in diesem Artikel erstellten Tutorials ab.

Pessimistische Parallelität (Sperren)

Wenn Ihre Anwendung versehentliche Datenverluste in Parallelitätsszenarios verhindern muss, ist die Verwendung von Datenbanksperren eine Möglichkeit. Dies wird als pessimistische Parallelität bezeichnet. Bevor Sie zum Beispiel eine Zeile aus einer Datenbank lesen, fordern Sie eine Sperre für den schreibgeschützten Zugriff oder den Aktualisierungszugriff an. Wenn Sie eine Zeile für den Aktualisierungszugriff sperren, kann kein anderer Benutzer diese Zeile für den schreibgeschützten Zugriff oder den Aktualisierungszugriff sperren, da er eine Kopie der Daten erhalten würde, die gerade geändert werden. Wenn Sie eine Zeile für den schreibgeschützten Zugriff sperren, können andere diese Zeile ebenfalls für den schreibgeschützten Zugriff sperren, aber nicht für den Aktualisierungszugriff.

Die Verwaltung von Sperren hat einige Nachteile. Es kann komplex sein, sie zu programmieren. Es erfordert erhebliche Datenbankverwaltungsressourcen, und es kann Leistungsprobleme verursachen, wenn die Anzahl der Benutzer einer Anwendung zunimmt (das heißt, sie wird nicht gut skaliert). Aus diesen Gründen unterstützen nicht alle Datenbankverwaltungssysteme die pessimistische Parallelität. Entity Framework bietet keine integrierte Unterstützung dafür, und in diesem Tutorial wird nicht gezeigt, wie Sie es implementieren.

Optimistische Nebenläufigkeit

Die Alternative zur pessimistischen Parallelität ist eine optimistische Parallelität. Die Verwendung der optimistischen Parallelität bedeutet, Nebenläufigkeitskonflikte zu erlauben und entsprechend zu reagieren, wenn diese auftreten. John führt beispielsweise die Seite Department.aspx aus, klickt auf den Link Bearbeiten für die Verlaufsabteilung und reduziert den Budgetbetrag von $1.000.000,00 auf $125.000,00. (John verwaltet eine konkurrierende Abteilung und möchte Geld für seine eigene Abteilung freigeben.)

Image07

Bevor John auf Aktualisieren klickt, führt Jane dieselbe Seite aus, klickt auf den Link Bearbeiten für die Abteilung Verlauf und ändert dann das Feld Startdatum vom 10.1.2011 in den 1.1.1.1999. (Jane verwaltet die Geschichtsabteilung und möchte ihr mehr Seniorität geben.)

Image08

John klickt zuerst auf Aktualisieren , dann auf Aktualisieren. Janes Browser listet den Budgetbetrag jetzt mit $1.000.000,00 auf, aber dies ist falsch, da der Betrag von John in $125.000,00 geändert wurde.

Zu den Aktionen, die Sie in diesem Szenario ausführen können, gehören die folgenden:

  • Sie können nachverfolgen, welche Eigenschaft ein Benutzer geändert hat und nur die entsprechenden Spalten in der Datenbank aktualisieren. Im Beispielszenario würden keine Daten verloren gehen, da verschiedene Eigenschaften von zwei Benutzern aktualisiert wurden. Wenn jemand das nächste Mal die Geschichtsabteilung durchsucht, wird der 1.1.1999 und 125.000,00 USD angezeigt.

    Dies ist das Standardverhalten im Entity Framework und kann die Anzahl von Konflikten, die zu Datenverlust führen können, erheblich reduzieren. Dieses Verhalten vermeidet jedoch keinen Datenverlust, wenn konkurrierende Änderungen an derselben Eigenschaft einer Entität vorgenommen werden. Darüber hinaus ist dieses Verhalten nicht immer möglich. wenn Sie gespeicherte Prozeduren einem Entitätstyp zuordnen, werden alle Eigenschaften einer Entität aktualisiert, wenn Änderungen an der Entität in der Datenbank vorgenommen werden.

  • Sie können Janes Änderung johns Änderung überschreiben lassen. Nachdem Jane auf Update geklickt hat, geht der Budgetbetrag auf 1.000.000,00 USD zurück. Das ist entweder ein Client gewinnt- oder ein Letzter Schreiber gewinnt-Szenario. (Die Werte des Clients haben Vorrang vor dem, was sich im Datenspeicher befindet.)

  • Sie können verhindern, dass Janes Änderung in der Datenbank aktualisiert wird. In der Regel würden Sie eine Fehlermeldung anzeigen, ihr den aktuellen Status der Daten anzeigen und ihr erlauben, ihre Änderungen erneut einzugeben, wenn sie sie weiterhin vornehmen möchte. Sie könnten den Prozess weiter automatisieren, indem Sie ihre Eingabe speichern und ihr die Möglichkeit geben, sie erneut zu verwenden, ohne sie erneut eingeben zu müssen. Dieses Szenario wird Speicher gewinnt genannt. (Die Werte des Datenspeichers haben Vorrang gegenüber den Werten, die vom Client gesendet werden).

Erkennen von Parallelitätskonflikten

Im Entity Framework können Sie Konflikte lösen, indem Sie Ausnahmen behandeln OptimisticConcurrencyException , die vom Entity Framework ausgelöst werden. Entity Framework muss dazu in der Lage sein, Konflikte zu erkennen, damit es weiß, wann diese Ausnahmen ausgelöst werden sollen. Aus diesem Grund müssen Sie die Datenbank und das Datenmodell entsprechend konfigurieren. Einige der Optionen für das Aktivieren der Konflikterkennung schließen Folgendes ein:

  • Fügen Sie in die Datenbank eine Tabellenspalte ein, mit der ermittelt werden kann, wann eine Zeile geändert wurde. Anschließend können Sie das Entity Framework so konfigurieren, dass diese Spalte in die Where SQL Update - oder Delete -Befehle-Klausel eingeschlossen wird.

    Das ist der Zweck der Timestamp Spalte in der OfficeAssignment Tabelle.

    Image09

    Der Datentyp der Timestamp Spalte wird auch genannt Timestamp. Die Spalte enthält jedoch keinen Datums- oder Uhrzeitwert. Stattdessen ist der Wert eine sequenzielle Zahl, die jedes Mal erhöht wird, wenn die Zeile aktualisiert wird. In einem Update - oder Delete -Befehl enthält die Where -Klausel den ursprünglichen Timestamp Wert. Wenn die zeile, die aktualisiert wird, von einem anderen Benutzer geändert wurde, unterscheidet sich der Wert in Timestamp vom ursprünglichen Wert, sodass die Where -Klausel keine zu aktualisierende Zeile zurückgibt. Wenn Entity Framework feststellt, dass keine Zeilen durch den aktuellen Update Befehl oder Delete den Befehl aktualisiert wurden (d. h. wenn die Anzahl der betroffenen Zeilen 0 ist) interpretiert es dies als Parallelitätskonflikt.

  • Konfigurieren Sie Entity Framework so, dass die ursprünglichen Werte jeder Spalte in der Tabelle in die -Klausel und UpdateDelete -WhereBefehle eingeschlossen werden.

    Wie bei der ersten Option gibt die Klausel keine zu aktualisierende Zeile zurück, Where wenn sich etwas in der Zeile seit dem ersten Lesen geändert hat, was vom Entity Framework als Parallelitätskonflikt interpretiert wird. Diese Methode ist genauso effektiv wie die Verwendung eines Felds Timestamp , kann aber ineffizient sein. Für Datenbanktabellen mit vielen Spalten kann dies zu sehr großen Where Klauseln führen, und in einer Webanwendung kann es erforderlich sein, dass Sie große Mengen an Zustand beibehalten. Das Beibehalten großer Mengen an Zustand kann sich auf die Anwendungsleistung auswirken, da sie entweder Serverressourcen erfordert (z. B. Sitzungszustand) oder in die Webseite selbst einbezogen werden muss (z. B. Ansichtszustand).

In diesem Tutorial fügen Sie die Fehlerbehandlung für optimistische Parallelitätskonflikte für eine Entität ohne Überwachungseigenschaft (die Department Entität) und für eine Entität mit einer Nachverfolgungseigenschaft (die OfficeAssignment Entität) hinzu.

Behandeln von optimistischer Parallelität ohne Nachverfolgungseigenschaft

Um eine optimistische Parallelität für die Department Entität zu implementieren, die keine Nachverfolgungseigenschaft (Timestamp) aufweist, führen Sie die folgenden Aufgaben aus:

  • Ändern Sie das Datenmodell, um die Parallelitätsnachverfolgung für Department Entitäten zu aktivieren.
  • Behandeln Sie in der SchoolRepository -Klasse Parallelitätsausnahmen in der SaveChanges -Methode.
  • Behandeln Sie auf der Seite Departments.aspx Parallelitätsausnahmen, indem Sie eine Meldung an den Benutzer anzeigen, dass die versuchten Änderungen nicht erfolgreich waren. Der Benutzer kann dann die aktuellen Werte anzeigen und die Änderungen wiederholen, wenn sie noch benötigt werden.

Aktivieren der Parallelitätsnachverfolgung im Datenmodell

Öffnen Sie in Visual Studio die Contoso University-Webanwendung, mit der Sie im vorherigen Tutorial dieser Reihe gearbeitet haben.

Öffnen Sie SchoolModel.edmx, und klicken Sie im Datenmodell-Designer mit der rechten Maustaste auf die Name Eigenschaft in der Department Entität, und klicken Sie dann auf Eigenschaften. Ändern Sie im Fenster Eigenschaften die ConcurrencyMode -Eigenschaft in Fixed.

Bild16

Führen Sie die gleichen Schritte für die anderen skalaren Eigenschaften ohne Primärschlüssel (Budget, StartDateund ) aus Administrator. (Für Navigationseigenschaften ist dies nicht möglich.) Dies gibt an, dass diese Spalten (mit ursprünglichen Werten) in die Klausel einbezogen werden müssen, wenn Entity Framework einen Update oder Delete SQL-Befehl generiert, um die Department Entität in der Where Datenbank zu aktualisieren. Wenn beim Ausführen des Update Befehls oder Delete keine Zeile gefunden wird, löst das Entity Framework eine Ausnahme für optimistische Parallelität aus.

Speichern und schließen Sie das Datenmodell.

Behandeln von Parallelitätsausnahmen in der DAL

Öffnen Sie SchoolRepository.cs , und fügen Sie die folgende using Anweisung für den System.Data Namespace hinzu:

using System.Data;

Fügen Sie die folgende neue SaveChanges Methode hinzu, die optimistische Parallelitätsausnahmen behandelt:

public void SaveChanges()
{
    try
    {
        context.SaveChanges();
    }
    catch (OptimisticConcurrencyException ocex)
    {
        context.Refresh(RefreshMode.StoreWins, ocex.StateEntries[0].Entity);
        throw ocex;
    }
}

Wenn beim Aufrufen dieser Methode ein Parallelitätsfehler auftritt, werden die Eigenschaftswerte der Entität im Arbeitsspeicher durch die Werte ersetzt, die sich derzeit in der Datenbank befinden. Die Parallelitätsausnahme wird erneut ausgeführt, damit die Webseite sie behandeln kann.

Ersetzen Sie in den DeleteDepartment Methoden und UpdateDepartment den vorhandenen Aufruf von context.SaveChanges() durch einen Aufruf von SaveChanges() , um die neue Methode aufzurufen.

Behandeln von Parallelitätsausnahmen auf der Präsentationsebene

Öffnen Sie Departments.aspx, und fügen Sie dem DepartmentsObjectDataSource Steuerelement ein OnDeleted="DepartmentsObjectDataSource_Deleted" Attribut hinzu. Das öffnende Tag für das Steuerelement ähnelt nun dem folgenden Beispiel.

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.BLL.SchoolBL" DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartmentsByName" DeleteMethod="DeleteDepartment" UpdateMethod="UpdateDepartment"
        ConflictDetection="CompareAllValues" OldValuesParameterFormatString="orig{0}" 
        OnUpdated="DepartmentsObjectDataSource_Updated" SortParameterName="sortExpression" 
        OnDeleted="DepartmentsObjectDataSource_Deleted" >

Geben Sie im DepartmentsGridView -Steuerelement alle Tabellenspalten im DataKeyNames -Attribut an, wie im folgenden Beispiel gezeigt. Beachten Sie, dass dadurch sehr große Ansichtszustandsfelder erstellt werden, was einer der Gründe ist, warum die Verwendung eines Nachverfolgungsfelds im Allgemeinen die bevorzugte Methode zum Nachverfolgen von Parallelitätskonflikten ist.

<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" 
        DataKeyNames="DepartmentID,Name,Budget,StartDate,Administrator" 
        OnRowUpdating="DepartmentsGridView_RowUpdating"
        OnRowDataBound="DepartmentsGridView_RowDataBound"
        AllowSorting="True" >

Öffnen Sie Departments.aspx.cs , und fügen Sie die folgende using Anweisung für den System.Data Namespace hinzu:

using System.Data;

Fügen Sie die folgende neue Methode hinzu, die Sie aus den Ereignishandlern und Deleted des Datenquellensteuerelements Updated aufrufen, um Parallelitätsausnahmen zu behandeln:

private void CheckForOptimisticConcurrencyException(ObjectDataSourceStatusEventArgs e, string function)
{
    if (e.Exception.InnerException is OptimisticConcurrencyException)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = 
            "The record you attempted to edit or delete was modified by another " +
            "user after you got the original value. The edit or delete operation was canceled " +
            "and the other user's values have been displayed so you can " +
            "determine whether you still want to edit or delete this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Dieser Code überprüft den Ausnahmetyp, und wenn es sich um eine Parallelitätsausnahme handelt, erstellt der Code dynamisch ein CustomValidator Steuerelement, das wiederum eine Meldung im ValidationSummary Steuerelement anzeigt.

Rufen Sie die neue Methode aus dem Ereignishandler auf, den Updated Sie zuvor hinzugefügt haben. Erstellen Sie außerdem einen neuen Deleted Ereignishandler, der dieselbe Methode aufruft (aber nichts anderes tut):

protected void DepartmentsObjectDataSource_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        CheckForOptimisticConcurrencyException(e, "update");
        // ...
    }
}

protected void DepartmentsObjectDataSource_Deleted(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        CheckForOptimisticConcurrencyException(e, "delete");
    }
}

Testen der optimistischen Parallelität auf der Abteilungsseite

Führen Sie die Seite Departments.aspx aus.

Screenshot der Seite

Klicken Sie in einer Zeile auf Bearbeiten , und ändern Sie den Wert in der Spalte Budget . (Denken Sie daran, dass Sie nur Datensätze bearbeiten können, die Sie für dieses Tutorial erstellt haben, da die vorhandenen School Datenbankdatensätze einige ungültige Daten enthalten. Der Datensatz für die Wirtschaftsabteilung ist ein sicherer, mit dem man experimentieren kann.)

Bild18

Öffnen Sie ein neues Browserfenster, und führen Sie die Seite erneut aus (kopieren Sie die URL aus dem Adressfeld des ersten Browserfensters in das zweite Browserfenster).

Screenshot eines neuen Browserfensters, das für die Eingabe bereit ist.

Klicken Sie in derselben Zeile auf Bearbeiten , die Sie zuvor bearbeitet haben, und ändern Sie den Wert Budget in einen anderen Wert.

Bild19

Klicken Sie im zweiten Browserfenster auf Aktualisieren. Der Budgetbetrag wurde erfolgreich in diesen neuen Wert geändert.

Image20

Klicken Sie im ersten Browserfenster auf Aktualisieren. Das Update schlägt fehl. Der Budgetbetrag wird mithilfe des Werts, den Sie im zweiten Browserfenster festgelegt haben, erneut angezeigt, und es wird eine Fehlermeldung angezeigt.

Image21

Behandeln von optimistischer Parallelität mithilfe einer Nachverfolgungseigenschaft

Um die optimistische Parallelität für eine Entität mit einer Überwachungseigenschaft zu behandeln, führen Sie die folgenden Aufgaben aus:

  • Fügen Sie dem Datenmodell gespeicherte Prozeduren hinzu, um Entitäten zu verwalten OfficeAssignment . (Überwachungseigenschaften und gespeicherte Prozeduren müssen nicht zusammen verwendet werden, sie werden hier nur zur Veranschaulichung gruppiert.)
  • Fügen Sie der DAL und der BLL für OfficeAssignment Entitäten CRUD-Methoden hinzu, einschließlich Code zur Behandlung von Ausnahmen für optimistische Parallelität in der DAL.
  • Erstellen Sie eine Webseite mit Office-Zuweisungen.
  • Testen Sie die optimistische Parallelität auf der neuen Webseite.

Hinzufügen gespeicherter OfficeAssignment-Prozeduren zum Datenmodell

Öffnen Sie die Datei SchoolModel.edmx im Modell-Designer, klicken Sie mit der rechten Maustaste auf die Entwurfsoberfläche, und klicken Sie auf Modell aus Datenbank aktualisieren. Erweitern Sie auf der Registerkarte Hinzufügen des Dialogfelds Datenbankobjekte auswählen den Eintrag Gespeicherte Prozeduren , wählen Sie die drei OfficeAssignment gespeicherten Prozeduren aus (siehe folgenden Screenshot), und klicken Sie dann auf Fertig stellen. (Diese gespeicherten Prozeduren befanden sich bereits in der Datenbank, als Sie sie heruntergeladen oder mithilfe eines Skripts erstellt haben.)

Image02

Klicken Sie mit der rechten Maustaste auf die OfficeAssignment Entität, und wählen Sie Zuordnung gespeicherter Prozeduren aus.

Image03

Legen Sie die Funktionen Einfügen, Aktualisieren und Löschen so fest, dass die entsprechenden gespeicherten Prozeduren verwendet werden. Legen Sie für den OrigTimestamp Parameter der Update Funktion die Eigenschaft auf Timestamp fest, und wählen Sie die Option Originalwert verwenden aus.

Image04

Wenn Entity Framework die UpdateOfficeAssignment gespeicherte Prozedur aufruft, wird der ursprüngliche Wert der Timestamp Spalte im OrigTimestamp Parameter übergeben. Die gespeicherte Prozedur verwendet diesen Parameter in ihrer Where Klausel:

ALTER PROCEDURE [dbo].[UpdateOfficeAssignment]
    @InstructorID int,
    @Location nvarchar(50),
    @OrigTimestamp timestamp
    AS
    UPDATE OfficeAssignment SET Location=@Location 
    WHERE InstructorID=@InstructorID AND [Timestamp]=@OrigTimestamp;
    IF @@ROWCOUNT > 0
    BEGIN
        SELECT [Timestamp] FROM OfficeAssignment 
            WHERE InstructorID=@InstructorID;
    END

Die gespeicherte Prozedur wählt auch den neuen Wert der Timestamp Spalte nach der Aktualisierung aus, sodass das Entity Framework die OfficeAssignment Entität, die sich im Arbeitsspeicher befindet, mit der entsprechenden Datenbankzeile synchron halten kann.

(Beachten Sie, dass die gespeicherte Prozedur zum Löschen einer Office-Zuweisung keinen Parameter enthält OrigTimestamp . Aus diesem Grund kann das Entity Framework nicht überprüfen, ob eine Entität unverändert ist, bevor sie gelöscht wird.)

Speichern und schließen Sie das Datenmodell.

Hinzufügen von OfficeAssignment-Methoden zur DAL

Öffnen Sie ISchoolRepository.cs, und fügen Sie die folgenden CRUD-Methoden für den Entitätssatz OfficeAssignment hinzu:

IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression);
void InsertOfficeAssignment(OfficeAssignment OfficeAssignment);
void DeleteOfficeAssignment(OfficeAssignment OfficeAssignment);
void UpdateOfficeAssignment(OfficeAssignment OfficeAssignment, OfficeAssignment origOfficeAssignment);

Fügen Sie SchoolRepository.cs die folgenden neuen Methoden hinzu. In der UpdateOfficeAssignment -Methode rufen Sie die lokale SaveChanges Methode anstelle von auf context.SaveChanges.

public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    return new ObjectQuery<OfficeAssignment>("SELECT VALUE o FROM OfficeAssignments AS o", context).Include("Person").OrderBy("it." + sortExpression).ToList();
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    context.OfficeAssignments.AddObject(officeAssignment);
    context.SaveChanges();
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    context.OfficeAssignments.Attach(officeAssignment);
    context.OfficeAssignments.DeleteObject(officeAssignment);
    context.SaveChanges();
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    context.OfficeAssignments.Attach(origOfficeAssignment);
    context.ApplyCurrentValues("OfficeAssignments", officeAssignment);
    SaveChanges();
}

Öffnen Sie im Testprojekt MockSchoolRepository.cs , und fügen Sie die folgenden OfficeAssignment Auflistungs- und CRUD-Methoden hinzu. (Das Pseudorepository muss die Repositoryschnittstelle implementieren, sonst wird die Lösung nicht kompiliert.)

List<OfficeAssignment> officeAssignments = new List<OfficeAssignment>();
        
public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    return officeAssignments;
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    officeAssignments.Add(officeAssignment);
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    officeAssignments.Remove(officeAssignment);
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    officeAssignments.Remove(origOfficeAssignment);
    officeAssignments.Add(officeAssignment);
}

Hinzufügen von OfficeAssignment-Methoden zur BLL

Öffnen Sie im Standard-Projekt SchoolBL.cs, und fügen Sie die folgenden CRUD-Methoden für die Entität hinzu, die OfficeAssignment ihr zugewiesen ist:

public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    if (string.IsNullOrEmpty(sortExpression)) sortExpression = "Person.LastName";
    return schoolRepository.GetOfficeAssignments(sortExpression);
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    try
    {
        schoolRepository.InsertOfficeAssignment(officeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    try
    {
        schoolRepository.DeleteOfficeAssignment(officeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    try
    {
        schoolRepository.UpdateOfficeAssignment(officeAssignment, origOfficeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

Erstellen einer OfficeAssignments-Webseite

Erstellen Sie eine neue Webseite, die die Seite Site.Master master verwendet, und nennen Sie sie OfficeAssignments.aspx. Fügen Sie dem Steuerelement das folgende Markup mit dem Content Namen hinzu Content2:

<h2>Office Assignments</h2>
    <asp:ObjectDataSource ID="OfficeAssignmentsObjectDataSource" runat="server" TypeName="ContosoUniversity.BLL.SchoolBL"
        DataObjectTypeName="ContosoUniversity.DAL.OfficeAssignment" SelectMethod="GetOfficeAssignments"
        DeleteMethod="DeleteOfficeAssignment" UpdateMethod="UpdateOfficeAssignment" ConflictDetection="CompareAllValues"
        OldValuesParameterFormatString="orig{0}"
        SortParameterName="sortExpression"  OnUpdated="OfficeAssignmentsObjectDataSource_Updated">
    </asp:ObjectDataSource>
    <asp:ValidationSummary ID="OfficeAssignmentsValidationSummary" runat="server" ShowSummary="true"
        DisplayMode="BulletList" Style="color: Red; width: 40em;" />
    <asp:GridView ID="OfficeAssignmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="OfficeAssignmentsObjectDataSource" DataKeyNames="InstructorID,Timestamp"
        AllowSorting="True">
        <Columns>
            <asp:CommandField ShowEditButton="True" ShowDeleteButton="True" ItemStyle-VerticalAlign="Top">
                <ItemStyle VerticalAlign="Top"></ItemStyle>
            </asp:CommandField>
            <asp:TemplateField HeaderText="Instructor" SortExpression="Person.LastName">
                <ItemTemplate>
                    <asp:Label ID="InstructorLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>'></asp:Label>,
                    <asp:Label ID="InstructorFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:DynamicField DataField="Location" HeaderText="Location" SortExpression="Location"/>
        </Columns>
        <SelectedRowStyle BackColor="LightGray"></SelectedRowStyle>
    </asp:GridView>

Beachten Sie, dass das Markup im DataKeyNames Attribut die Timestamp Eigenschaft sowie den Datensatzschlüssel (InstructorID) angibt. Das Angeben von DataKeyNames Eigenschaften im -Attribut bewirkt, dass das Steuerelement sie im Steuerelementzustand speichert (ähnlich dem Ansichtszustand), sodass die ursprünglichen Werte während der Postbackverarbeitung verfügbar sind.

Wenn Sie den Timestamp Wert nicht gespeichert haben, hat das Entity Framework ihn nicht für die Where Klausel des SQL-Befehls Update . Folglich würde nichts aktualisiert werden. Infolgedessen löst das Entity Framework bei jeder Aktualisierung einer Entität eine optimistische Parallelitätsausnahme aus OfficeAssignment .

Öffnen Sie OfficeAssignments.aspx.cs , und fügen Sie die folgende using Anweisung für die Datenzugriffsebene hinzu:

using ContosoUniversity.DAL;

Fügen Sie die folgende Page_Init Methode hinzu, die Dynamic Data-Funktionalität aktiviert. Fügen Sie auch den folgenden Handler für das ObjectDataSource -Ereignis des Steuerelements Updated hinzu, um nach Parallelitätsfehlern zu suchen:

protected void Page_Init(object sender, EventArgs e)
{
    OfficeAssignmentsGridView.EnableDynamicData(typeof(OfficeAssignment));
}

protected void OfficeAssignmentsObjectDataSource_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = "The record you attempted to " +
            "update has been modified by another user since you last visited this page. " +
            "Your update was canceled to allow you to review the other user's " +
            "changes and determine if you still want to update this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Testen der optimistischen Parallelität auf der OfficeAssignments-Seite

Führen Sie die Seite OfficeAssignments.aspx aus.

Screenshot: Seite

Klicken Sie auf In zeile bearbeiten , und ändern Sie den Wert in der Spalte Speicherort .

Image11

Öffnen Sie ein neues Browserfenster, und führen Sie die Seite erneut aus (kopieren Sie die URL aus dem ersten Browserfenster in das zweite Browserfenster).

Screenshot eines neuen Browserfensters

Klicken Sie in derselben Zeile, die Sie zuvor bearbeitet haben, auf Bearbeiten , und ändern Sie den Wert Location in einen anderen Wert.

Bild12

Klicken Sie im zweiten Browserfenster auf Aktualisieren.

Bild13

Wechseln Sie zum ersten Browserfenster, und klicken Sie auf Aktualisieren.

Bild15

Es wird eine Fehlermeldung angezeigt, und der Wert Location wurde aktualisiert, um den Wert anzuzeigen, in den Sie ihn im zweiten Browserfenster geändert haben.

Behandeln von Parallelität mit dem EntityDataSource-Steuerelement

Das EntityDataSource Steuerelement enthält eine integrierte Logik, die die Parallelitätseinstellungen im Datenmodell erkennt und Aktualisierungs- und Löschvorgänge entsprechend verarbeitet. Wie bei allen Ausnahmen müssen Sie ausnahmen jedoch selbst behandeln OptimisticConcurrencyException , um eine benutzerfreundliche Fehlermeldung bereitzustellen.

Als Nächstes konfigurieren Sie die Seite Courses.aspx (die ein EntityDataSource Steuerelement verwendet), um Aktualisierungs- und Löschvorgänge zuzulassen und eine Fehlermeldung anzuzeigen, wenn ein Parallelitätskonflikt auftritt. Die Course Entität verfügt nicht über eine Parallelitätsnachverfolgungsspalte, sodass Sie dieselbe Methode wie bei der Department Entität verwenden: Nachverfolgen der Werte aller Nichtschlüsseleigenschaften.

Öffnen Sie die Datei SchoolModel.edmx . Legen Sie für die Nichtschlüsseleigenschaften der Course Entität (Title, Creditsund DepartmentID) die Eigenschaft Parallelitätsmodus auf fest Fixed. Speichern und schließen Sie dann das Datenmodell.

Öffnen Sie die Seite Courses.aspx , und nehmen Sie die folgenden Änderungen vor:

  • Fügen Sie im CoursesEntityDataSource Steuerelement Attribute hinzuEnableUpdate="true".EnableDelete="true" Das öffnende Tag für dieses Steuerelement ähnelt nun dem folgenden Beispiel:

    <asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false" 
            AutoGenerateWhereClause="True" EntitySetName="Courses"
            EnableUpdate="true" EnableDelete="true">
    
  • Ändern Sie im CoursesGridView Steuerelement den DataKeyNames Attributwert in "CourseID,Title,Credits,DepartmentID". Fügen Sie dann dem Element ein CommandField Element hinzu, das die Columns Schaltflächen Bearbeiten und Löschen (<asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />) anzeigt. Das GridView Steuerelement ähnelt nun dem folgenden Beispiel:

    <asp:GridView ID="CoursesGridView" runat="server" AutoGenerateColumns="False" 
            DataKeyNames="CourseID,Title,Credits,DepartmentID"
            DataSourceID="CoursesEntityDataSource" >
            <Columns>
                <asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />
                <asp:BoundField DataField="CourseID" HeaderText="CourseID" ReadOnly="True" SortExpression="CourseID" />
                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
                <asp:BoundField DataField="Credits" HeaderText="Credits" SortExpression="Credits" />
            </Columns>
        </asp:GridView>
    

Führen Sie die Seite aus, und erstellen Sie eine Konfliktsituation wie zuvor auf der Seite "Abteilungen". Führen Sie die Seite in zwei Browserfenstern aus, klicken Sie in der gleichen Zeile in jedem Fenster auf Bearbeiten , und nehmen Sie in jedem Fenster eine andere Änderung vor. Klicken Sie in einem Fenster auf Aktualisieren und dann im anderen Fenster auf Aktualisieren . Wenn Sie zum zweiten Mal auf Aktualisieren klicken, wird die Fehlerseite angezeigt, die sich aus einer Ausnahme für nicht behandelte Parallelität ergibt.

Bild22

Sie behandeln diesen Fehler in einer Weise, die der Behandlung für das ObjectDataSource Steuerelement sehr ähnlich ist. Öffnen Sie die Seite Courses.aspx , und geben Sie im CoursesEntityDataSource -Steuerelement Handler für die Deleted Ereignisse und Updated an. Das öffnende Tag des Steuerelements ähnelt nun dem folgenden Beispiel:

<asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
        ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false"
        AutoGenerateWhereClause="true" EntitySetName="Courses" 
        EnableUpdate="true" EnableDelete="true" 
        OnDeleted="CoursesEntityDataSource_Deleted" 
        OnUpdated="CoursesEntityDataSource_Updated">

Fügen Sie vor dem CoursesGridView -Steuerelement das folgende ValidationSummary Steuerelement hinzu:

<asp:ValidationSummary ID="CoursesValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

Fügen Sie in Courses.aspx.cs eine using Anweisung für den System.Data Namespace hinzu, fügen Sie eine Methode hinzu, die auf Parallelitätsausnahmen überprüft, und fügen Sie Handler für die Handler und UpdatedDeleted des EntityDataSource Steuerelements hinzu. Der Code sieht wie folgt aus:

using System.Data;
protected void CoursesEntityDataSource_Updated(object sender, EntityDataSourceChangedEventArgs e)
{
    CheckForOptimisticConcurrencyException(e, "update");
}

protected void CoursesEntityDataSource_Deleted(object sender, EntityDataSourceChangedEventArgs e)
{
    CheckForOptimisticConcurrencyException(e, "delete");
}

private void CheckForOptimisticConcurrencyException(EntityDataSourceChangedEventArgs e, string function)
{
    if (e.Exception != null && e.Exception is OptimisticConcurrencyException)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = 
            "The record you attempted to edit or delete was modified by another " +
            "user after you got the original value. The edit or delete operation was canceled " +
            "and the other user's values have been displayed so you can " +
            "determine whether you still want to edit or delete this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Der einzige Unterschied zwischen diesem Code und dem, was Sie für das ObjectDataSource Steuerelement gemacht haben, besteht darin, dass die Parallelitätsausnahme in diesem Fall in der Exception -Eigenschaft des Ereignisargumentobjekts und nicht in der -Eigenschaft dieser InnerException Ausnahme liegt.

Führen Sie die Seite aus, und erstellen Sie erneut einen Parallelitätskonflikt. Dieses Mal wird eine Fehlermeldung angezeigt:

Bild23

Damit ist die Einführung in die Behandlung von Nebenläufigkeitskonflikten abgeschlossen. Das nächste Tutorial enthält Anleitungen zum Verbessern der Leistung in einer Webanwendung, die Entity Framework verwendet.