Tutorial: Behandeln der Parallelität mit EF in einer ASP.NET MVC 5-App
In früheren Tutorials haben Sie gelernt, wie Sie Daten aktualisieren. In diesem Tutorial wird gezeigt, wie Sie optimistische Parallelität verwenden, um Konflikte zu behandeln, wenn mehrere Benutzer die gleiche Entität gleichzeitig aktualisieren. Sie ändern die Webseiten, die mit der Department
Entität arbeiten, sodass sie Parallelitätsfehler behandeln. In der nachfolgenden Abbildung sehen Sie die Seiten „Bearbeiten“ und „Löschen“, einschließlich einiger Meldungen, die angezeigt werden, wenn ein Parallelitätskonflikt auftritt.
In diesem Tutorial:
- Erhalten Sie Informationen über Parallelitätskonflikte
- Hinzufügen von optimistischer Parallelität
- Ändern des Abteilungscontrollers
- Parallelitätsbehandlung für Tests
- Aktualisieren der Seite „Delete“ (Löschen)
Voraussetzungen
Nebenläufigkeitskonflikte
Ein Parallelitätskonflikt tritt auf, wenn ein Benutzer die Daten einer Entität anzeigt, um diese zu bearbeiten, und ein anderer Benutzer eben diese Entitätsdaten aktualisiert, bevor die Änderungen des ersten Benutzers in die Datenbank geschrieben wurden. Wenn Sie die Erkennung solcher Konflikte nicht aktivieren, überschreibt das letzte Update der Datenbank die Änderungen des anderen Benutzers. In vielen Anwendungen ist dieses Risiko akzeptabel: Wenn es nur wenige Benutzer bzw. wenige Updates gibt, oder wenn es nicht schlimm ist, dass Änderungen überschrieben werden können, ist es den Aufwand, für die Parallelität zu programmieren, möglicherweise nicht wert. In diesem Fall müssen Sie für die Anwendung keine Behandlung von Nebenläufigkeitskonflikten konfigurieren.
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.
Das Verwalten von Sperren hat Nachteile. Es kann komplex sein, sie zu programmieren. Dies erfordert erhebliche Datenbankverwaltungsressourcen und kann mit steigender Anzahl der Benutzer einer Anwendung zu Leistungsproblemen führen. 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 die optimistische Parallelität. Die Verwendung der optimistischen Parallelität bedeutet, Nebenläufigkeitskonflikte zu erlauben und entsprechend zu reagieren, wenn diese auftreten. Beispielsweise führt John die Seite "Departments Edit" aus und ändert den Budgetbetrag für die englische Abteilung von $350.000,00 in $0,00.
Bevor John auf Speichern klickt, führt Jane dieselbe Seite aus und ändert das Feld Startdatum vom 01.09.2007 in den 8.08.2013.
John klickt zuerst auf Speichern und sieht seine Änderung, wenn der Browser zur Indexseite zurückkehrt, dann klickt Jane auf Speichern. Was daraufhin geschieht, ist abhängig davon, wie Sie Nebenläufigkeitskonflikte behandeln. Einige der Optionen schließen Folgendes ein:
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 englische Abteilung durchsucht, werden sowohl Johns als auch Janes Änderungen angezeigt – ein Startdatum am 8.08.2013 und ein Budget von Null Dollar.
Diese Methode der Aktualisierung kann die Anzahl von Konflikten reduzieren, die zu Datenverlusten führen können. Sie kann Datenverluste jedoch nicht verhindern, wenn konkurrierende Änderungen an der gleichen Eigenschaft einer Entität vorgenommen werden. Ob Entity Framework auf diese Weise funktioniert, hängt davon ab, wie Sie Ihren Aktualisierungscode implementieren. Oft ist dies in Webanwendungen nicht praktikabel, da es erforderlich sein kann, viele Zustände zu verwalten, um alle ursprünglichen Eigenschaftswerte einer Entität und die neuen Werte im Auge zu behalten. Das Verwalten vieler Zuständen kann sich auf die Leistung der Anwendung auswirken, da es entweder Serverressourcen beansprucht oder in der Webseite selbst (z.B. in ausgeblendeten Feldern) oder in einem Cookie enthalten sein muss.
Sie können Janes Änderung überschreiben Lassen Sie Johns Änderung. Wenn jemand das nächste Mal die englische Abteilung durchsucht, wird der Wert vom 8.8.2013 und der wiederhergestellte Wert von $350.000,00 angezeigt. Das ist entweder ein Client gewinnt- oder ein Letzter Schreiber gewinnt-Szenario. (Alle Werte des Clients haben Vorrang vor dem Datenspeicher.) Wie in der Einführung dieses Abschnitts beschrieben, geschieht dies automatisch, wenn Sie für die Behandlung der Parallelität keinen Code schreiben.
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 zu übernehmen, wenn sie sie noch vornehmen möchte. Dieses Szenario wird Speicher gewinnt genannt. (Die Werte des Datenspeichers haben Vorrang vor den Werten, die vom Client gesendet werden.) In diesem Tutorial implementieren Sie das Szenario „Speicher gewinnt“. Diese Methode stellt sicher, dass keine Änderung überschrieben wird, ohne dass ein Benutzer darüber benachrichtigt wird.
Erkennen von Parallelitätskonflikten
Sie können Konflikte lösen, indem Sie Vom Entity Framework ausgelöste Ausnahmen vom Typ OptimisticConcurrencyException behandeln. 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 eine Änderungsverfolgungsspalte in die Datenbanktabelle ein, die verwendet werden kann, um zu bestimmen, wenn eine Änderung an einer Zeile vorgenommen wurde. Anschließend können Sie das Entity Framework so konfigurieren, dass diese Spalte in die
Where
-Klausel von SQLUpdate
- oderDelete
-Befehlen eingeschlossen wird.Der Datentyp der Nachverfolgungsspalte ist in der Regel rowversion. Der Rowversion-Wert ist eine sequenzielle Zahl, die bei jeder Aktualisierung der Zeile inkrementiert wird. In einem
Update
- oderDelete
-Befehl enthält dieWhere
-Klausel den ursprünglichen Wert der Nachverfolgungsspalte (die ursprüngliche Zeilenversion). Wenn die Zeile, die aktualisiert wird, von einem anderen Benutzer geändert wurde, unterscheidet sich der Wert in derrowversion
Spalte vom ursprünglichen Wert, sodass dieUpdate
- oderDelete
-Anweisung die zu aktualisierende Zeile aufgrund derWhere
-Klausel nicht finden kann. Wenn Entity Framework feststellt, dass keine Zeilen vomUpdate
Befehl oderDelete
aktualisiert wurden (d. h. wenn die Anzahl der betroffenen Zeilen null ist), interpretiert es dies als Parallelitätskonflikt.Konfigurieren Sie das Entity Framework, um die ursprünglichen Werte jeder Spalte in der Tabelle in den -Klauseln von
Update
undDelete
-Where
Befehlen einzuschließen.Wie bei der ersten Option gibt die -Klausel, wenn sich etwas in der Zeile seit dem ersten Lesen der Zeile geändert hat,
Where
keine zu aktualisierende Zeile zurück, die vom Entity Framework als Parallelitätskonflikt interpretiert wird. Bei Datenbanktabellen, die viele Spalten enthalten, kann dieser Ansatz zu sehr großenWhere
Klauseln führen und erfordern, dass Sie große Zustandsmengen beibehalten. Wie bereits erwähnt, kann das Verwalten großer Mengen von Zuständen die Anwendungsleistung beeinträchtigen. Deshalb wird dieser Ansatz in der Regel nicht empfohlen, und ist nicht die Methode, die in diesem Tutorial verwendet wird.Wenn Sie diesen Parallelitätsansatz implementieren möchten, müssen Sie alle Nicht-Primärschlüssel-Eigenschaften in der Entität markieren, für die Sie die Parallelität nachverfolgen möchten, indem Sie ihnen das ConcurrencyCheck-Attribut hinzufügen. Diese Änderung ermöglicht es dem Entity Framework, alle Spalten in die SQL-Klausel
WHERE
vonUPDATE
-Anweisungen einzuschließen.
Im weiteren Verlauf dieses Tutorials fügen Sie der Department
Entität eine Rowversion-Nachverfolgungseigenschaft hinzu, erstellen einen Controller und Ansichten und testen, ob alles ordnungsgemäß funktioniert.
Hinzufügen von optimistischer Parallelität
Fügen Sie in Models\Department.cs eine Nachverfolgungseigenschaft mit dem Namen hinzu RowVersion
:
public class Department
{
public int DepartmentID { get; set; }
[StringLength(50, MinimumLength = 3)]
public string Name { get; set; }
[DataType(DataType.Currency)]
[Column(TypeName = "money")]
public decimal Budget { get; set; }
[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]
[Display(Name = "Start Date")]
public DateTime StartDate { get; set; }
[Display(Name = "Administrator")]
public int? InstructorID { get; set; }
[Timestamp]
public byte[] RowVersion { get; set; }
public virtual Instructor Administrator { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
Das Timestamp-Attribut gibt an, dass diese Spalte in die An die Where
Datenbank gesendete -Klausel von Update
- und Delete
-Befehlen eingeschlossen wird. Das Attribut wird als Timestamp bezeichnet, da frühere Versionen von SQL Server einen SQL-Zeitstempeldatentyp verwendet haben, bevor die SQL-Rowversion ihn ersetzt hat. Der .NET-Typ für rowversion ist ein Bytearray.
Wenn Sie lieber die Fluent-API verwenden möchten, können Sie die IsConcurrencyToken-Methode verwenden, um die Nachverfolgungseigenschaft anzugeben, wie im folgenden Beispiel gezeigt:
modelBuilder.Entity<Department>()
.Property(p => p.RowVersion).IsConcurrencyToken();
Durch das Hinzufügen einer Eigenschaft ändern Sie das Datenbankmodell, daher müssen Sie eine weitere Migration durchführen. Geben Sie die folgenden Befehle in die Paket-Manager-Konsole ein:
Add-Migration RowVersion
Update-Database
Ändern des Abteilungscontrollers
Fügen Sie in Controllers\DepartmentController.cs eine -Anweisung hinzu using
:
using System.Data.Entity.Infrastructure;
Ändern Sie in der Datei DepartmentController.cs alle vier Vorkommen von "LastName" in "FullName", sodass die Dropdownlisten des Abteilungsadministrators den vollständigen Namen des Dozenten und nicht nur den Nachnamen enthalten.
ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "FullName");
Ersetzen Sie den vorhandenen Code für die HttpPost
Edit
-Methode durch den folgenden Code:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Edit(int? id, byte[] rowVersion)
{
string[] fieldsToBind = new string[] { "Name", "Budget", "StartDate", "InstructorID", "RowVersion" };
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
var departmentToUpdate = await db.Departments.FindAsync(id);
if (departmentToUpdate == null)
{
Department deletedDepartment = new Department();
TryUpdateModel(deletedDepartment, fieldsToBind);
ModelState.AddModelError(string.Empty,
"Unable to save changes. The department was deleted by another user.");
ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "FullName", deletedDepartment.InstructorID);
return View(deletedDepartment);
}
if (TryUpdateModel(departmentToUpdate, fieldsToBind))
{
try
{
db.Entry(departmentToUpdate).OriginalValues["RowVersion"] = rowVersion;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
catch (DbUpdateConcurrencyException ex)
{
var entry = ex.Entries.Single();
var clientValues = (Department)entry.Entity;
var databaseEntry = entry.GetDatabaseValues();
if (databaseEntry == null)
{
ModelState.AddModelError(string.Empty,
"Unable to save changes. The department was deleted by another user.");
}
else
{
var databaseValues = (Department)databaseEntry.ToObject();
if (databaseValues.Name != clientValues.Name)
ModelState.AddModelError("Name", "Current value: "
+ databaseValues.Name);
if (databaseValues.Budget != clientValues.Budget)
ModelState.AddModelError("Budget", "Current value: "
+ String.Format("{0:c}", databaseValues.Budget));
if (databaseValues.StartDate != clientValues.StartDate)
ModelState.AddModelError("StartDate", "Current value: "
+ String.Format("{0:d}", databaseValues.StartDate));
if (databaseValues.InstructorID != clientValues.InstructorID)
ModelState.AddModelError("InstructorID", "Current value: "
+ db.Instructors.Find(databaseValues.InstructorID).FullName);
ModelState.AddModelError(string.Empty, "The record you attempted to edit "
+ "was modified by another user after you got the original value. The "
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again. Otherwise click the Back to List hyperlink.");
departmentToUpdate.RowVersion = databaseValues.RowVersion;
}
}
catch (RetryLimitExceededException /* dex */)
{
//Log the error (uncomment dex variable name and add a line here to write a log.)
ModelState.AddModelError("", "Unable to save changes. Try again, and if the problem persists, see your system administrator.");
}
}
ViewBag.InstructorID = new SelectList(db.Instructors, "ID", "FullName", departmentToUpdate.InstructorID);
return View(departmentToUpdate);
}
Wenn die Methode FindAsync
NULL zurückgibt, wurde die Abteilung von einem anderen Benutzer gelöscht. Der gezeigte Code verwendet die bereitgestellten Formularwerte, um eine Abteilungsentität zu erstellen, sodass die Seite Bearbeiten mit einer Fehlermeldung erneut angezeigt werden kann. Alternativ müssen Sie die Abteilungsentität nicht erneut erstellen, wenn Sie nur eine Fehlermeldung anzeigen, ohne die Abteilungsfelder erneut anzuzeigen.
Die Ansicht speichert den ursprünglichen RowVersion
Wert in einem ausgeblendeten Feld, und die -Methode empfängt ihn im rowVersion
-Parameter. Bevor Sie SaveChanges
aufrufen, müssen Sie diesen ursprünglichen Eigenschaftswert von RowVersion
in die Auflistung OriginalValues
für die Entität einfügen. Wenn das Entity Framework dann einen SQL-Befehl UPDATE
erstellt, enthält dieser Befehl eine WHERE
-Klausel, die nach einer Zeile sucht, die den ursprünglichen RowVersion
Wert aufweist.
Wenn vom Befehl keine Zeilen betroffen UPDATE
sind (keine Zeilen haben den ursprünglichen RowVersion
Wert), löst entity Framework eine Ausnahme aus DbUpdateConcurrencyException
, und der Code im catch
Block ruft die betroffene Department
Entität aus dem Ausnahmeobjekt ab.
var entry = ex.Entries.Single();
Dieses Objekt enthält die neuen Werte, die der Benutzer in seiner Entity
-Eigenschaft eingegeben hat, und Sie können die Werte aus der Datenbank lesen, indem Sie die GetDatabaseValues
-Methode aufrufen.
var clientValues = (Department)entry.Entity;
var databaseEntry = entry.GetDatabaseValues();
Die GetDatabaseValues
-Methode gibt NULL zurück, wenn jemand die Zeile aus der Datenbank gelöscht hat. Andernfalls müssen Sie das zurückgegebene Objekt in die Department
-Klasse umwandeln, um auf die Department
Eigenschaften zuzugreifen. (Da Sie bereits auf Löschung überprüft haben, wäre nur null, databaseEntry
wenn die Abteilung nach FindAsync
den Ausführungen und vor den SaveChanges
Ausführungen gelöscht wurde.)
if (databaseEntry == null)
{
ModelState.AddModelError(string.Empty,
"Unable to save changes. The department was deleted by another user.");
}
else
{
var databaseValues = (Department)databaseEntry.ToObject();
Als Nächstes fügt der Code eine benutzerdefinierte Fehlermeldung für jede Spalte hinzu, deren Datenbankwerte sich von denen unterscheiden, die der Benutzer auf der Seite Bearbeiten eingegeben hat:
if (databaseValues.Name != currentValues.Name)
ModelState.AddModelError("Name", "Current value: " + databaseValues.Name);
// ...
In einer längeren Fehlermeldung wird erläutert, was passiert ist und was zu tun ist:
ModelState.AddModelError(string.Empty, "The record you attempted to edit "
+ "was modified by another user after you got the original value. The"
+ "edit operation was canceled and the current values in the database "
+ "have been displayed. If you still want to edit this record, click "
+ "the Save button again. Otherwise click the Back to List hyperlink.");
Schließlich legt der Code den RowVersion
Wert des Department
Objekts auf den neuen Wert fest, der aus der Datenbank abgerufen wird. Dieser neue RowVersion
-Wert wird in dem ausgeblendeten Feld gespeichert, wenn die Seite „Bearbeiten“ erneut angezeigt wird. Das nächste Mal, wenn der Benutzer auf Speichern klickt, werden nur Parallelitätsfehler abgefangen, die nach dem erneuten Anzeigen der Seite „Bearbeiten“ aufgetreten sind.
Fügen Sie in Views\Department\Edit.cshtml ein ausgeblendetes Feld hinzu, um den RowVersion
Eigenschaftswert zu speichern, unmittelbar nach dem ausgeblendeten Feld für die DepartmentID
Eigenschaft:
@model ContosoUniversity.Models.Department
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm())
{
@Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Department</h4>
<hr />
@Html.ValidationSummary(true)
@Html.HiddenFor(model => model.DepartmentID)
@Html.HiddenFor(model => model.RowVersion)
Parallelitätsbehandlung für Tests
Führen Sie die Website aus, und klicken Sie auf Abteilungen.
Klicken Sie mit der rechten Maustaste auf den Link Bearbeiten für die englische Abteilung, und wählen Sie In neuer Registerkarte öffnen aus, und klicken Sie dann auf den Link Bearbeiten für die englische Abteilung. Auf den beiden Registerkarten werden die gleichen Informationen angezeigt.
Ändern Sie ein Feld in der ersten Registerkarte, und klicken Sie auf Speichern.
Der Browser zeigt die Indexseite mit dem geänderten Wert an.
Ändern Sie ein Feld auf der zweiten Browserregisterkarte, und klicken Sie auf Speichern. Folgende Fehlermeldung wird angezeigt:
Klicken Sie erneut auf Speichern. Der Wert, den Sie auf der zweiten Browserregisterkarte eingegeben haben, wird zusammen mit dem ursprünglichen Wert der Daten gespeichert, die Sie im ersten Browser geändert haben. Die gespeicherten Werte werden Ihnen auf der Indexseite angezeigt.
Aktualisieren der Seite „Delete“ (Löschen)
Bei der Seite „Löschen“ entdeckt Entity Framework Nebenläufigkeitskonflikte, die durch die Bearbeitung einer Abteilung ausgelöst wurden, auf ähnliche Weise. Wenn die HttpGet
Delete
-Methode die Bestätigungsansicht anzeigt, enthält die Ansicht den ursprünglichen RowVersion
Wert in einem ausgeblendeten Feld. Dieser Wert ist dann für die Methode verfügbar, die HttpPost
Delete
aufgerufen wird, wenn der Benutzer den Löschvorgang bestätigt. Wenn entity Framework den SQL-Befehl DELETE
erstellt, enthält es eine WHERE
-Klausel mit dem ursprünglichen RowVersion
Wert. Wenn der Befehl zu 0 betroffenen Zeilen führt (d. h. die Zeile wurde geändert, nachdem die Bestätigungsseite löschen angezeigt wurde), wird eine Parallelitätsausnahme ausgelöst, und die HttpGet Delete
Methode wird mit einem Fehlerflag aufgerufen, das auf true
festgelegt ist, um die Bestätigungsseite erneut mit einer Fehlermeldung anzuzeigen. Es ist auch möglich, dass null Zeilen betroffen waren, da die Zeile von einem anderen Benutzer gelöscht wurde, sodass in diesem Fall eine andere Fehlermeldung angezeigt wird.
Ersetzen Sie in DepartmentController.cs die HttpGet
Delete
-Methode durch den folgenden Code:
public async Task<ActionResult> Delete(int? id, bool? concurrencyError)
{
if (id == null)
{
return new HttpStatusCodeResult(HttpStatusCode.BadRequest);
}
Department department = await db.Departments.FindAsync(id);
if (department == null)
{
if (concurrencyError.GetValueOrDefault())
{
return RedirectToAction("Index");
}
return HttpNotFound();
}
if (concurrencyError.GetValueOrDefault())
{
ViewBag.ConcurrencyErrorMessage = "The record you attempted to delete "
+ "was modified by another user after you got the original values. "
+ "The delete operation was canceled and the current values in the "
+ "database have been displayed. If you still want to delete this "
+ "record, click the Delete button again. Otherwise "
+ "click the Back to List hyperlink.";
}
return View(department);
}
Die Methode akzeptiert einen optionalen Parameter, der angibt, ob die Seite nach einem Parallelitätsfehler erneut angezeigt wird. Wenn dieses Flag lautet true
, wird eine Fehlermeldung mithilfe einer ViewBag
-Eigenschaft an die Ansicht gesendet.
Ersetzen Sie den Code in der HttpPost
Delete
-Methode (mit dem Namen DeleteConfirmed
) durch den folgenden Code:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Delete(Department department)
{
try
{
db.Entry(department).State = EntityState.Deleted;
await db.SaveChangesAsync();
return RedirectToAction("Index");
}
catch (DbUpdateConcurrencyException)
{
return RedirectToAction("Delete", new { concurrencyError = true, id=department.DepartmentID });
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to write a log.
ModelState.AddModelError(string.Empty, "Unable to delete. Try again, and if the problem persists contact your system administrator.");
return View(department);
}
}
In dem eingerüsteten Code, den Sie soeben ersetzt haben, akzeptiert diese Methode nur eine Datensatz-ID:
public async Task<ActionResult> DeleteConfirmed(int id)
Diesen Parameter haben Sie in eine Department
-Entitätsinstanz geändert, die durch die Modellbindung erstellt wurde. Dadurch erhalten Sie zusätzlich zum RowVersion
Datensatzschlüssel Zugriff auf den Eigenschaftswert.
public async Task<ActionResult> Delete(Department department)
Ebenfalls haben Sie den Namen der Aktionsmethode DeleteConfirmed
auf Delete
geändert. Der gerüstete Code namens die HttpPost
Delete
-Methode DeleteConfirmed
, um der HttpPost
Methode eine eindeutige Signatur zu geben. ( Die CLR erfordert überladene Methoden, um unterschiedliche Methodenparameter zu haben.) Nachdem die Signaturen eindeutig sind, können Sie sich an die MVC-Konvention halten und denselben Namen für die HttpPost
Methoden und HttpGet
löschen verwenden.
Wenn ein Parallelitätsfehler abgefangen wird, zeigt der Code erneut die Bestätigungsseite „Löschen“ an, und stellt ein Flag bereit, das angibt, dass eine Fehlermeldung für die Parallelität angezeigt werden soll.
Ersetzen Sie in Views\Department\Delete.cshtml den gerüsteten Code durch den folgenden Code, der ein Fehlermeldungsfeld und ausgeblendete Felder für die Eigenschaften DepartmentID und RowVersion hinzufügt. Die Änderungen werden hervorgehoben.
@model ContosoUniversity.Models.Department
@{
ViewBag.Title = "Delete";
}
<h2>Delete</h2>
<p class="error">@ViewBag.ConcurrencyErrorMessage</p>
<h3>Are you sure you want to delete this?</h3>
<div>
<h4>Department</h4>
<hr />
<dl class="dl-horizontal">
<dt>
Administrator
</dt>
<dd>
@Html.DisplayFor(model => model.Administrator.FullName)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Name)
</dt>
<dd>
@Html.DisplayFor(model => model.Name)
</dd>
<dt>
@Html.DisplayNameFor(model => model.Budget)
</dt>
<dd>
@Html.DisplayFor(model => model.Budget)
</dd>
<dt>
@Html.DisplayNameFor(model => model.StartDate)
</dt>
<dd>
@Html.DisplayFor(model => model.StartDate)
</dd>
</dl>
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.HiddenFor(model => model.DepartmentID)
@Html.HiddenFor(model => model.RowVersion)
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-default" /> |
@Html.ActionLink("Back to List", "Index")
</div>
}
</div>
Dieser Code fügt eine Fehlermeldung zwischen den h2
Überschriften und h3
hinzu:
<p class="error">@ViewBag.ConcurrencyErrorMessage</p>
Im Feld wird durch FullName
Administrator
ersetztLastName
:
<dt>
Administrator
</dt>
<dd>
@Html.DisplayFor(model => model.Administrator.FullName)
</dd>
Schließlich werden ausgeblendete Felder für die DepartmentID
Eigenschaften und RowVersion
nach der Html.BeginForm
-Anweisung hinzugefügt:
@Html.HiddenFor(model => model.DepartmentID)
@Html.HiddenFor(model => model.RowVersion)
Führen Sie die Seite Abteilungsindex aus. Klicken Sie mit der rechten Maustaste auf den Link Löschen für die englische Abteilung, und wählen Sie In neuer Registerkarte öffnen aus, und klicken Sie dann auf der ersten Registerkarte auf den Link Bearbeiten für die englische Abteilung.
Ändern Sie im ersten Fenster einen der Werte, und klicken Sie auf Speichern.
Die Seite Index bestätigt die Änderung.
Klicken Sie in der zweiten Registerkarte auf Löschen.
Ihnen wird eine Fehlermeldung zur Parallelität angezeigt, und die Abteilungswerte werden mit den aktuellen Werten der Datenbank aktualisiert.
Wenn Sie erneut auf Löschen klicken, werden Sie auf die Indexseite weitergeleitet, die anzeigt, dass die Abteilung gelöscht wurde.
Abrufen des Codes
Abgeschlossenes Projekt herunterladen
Zusätzliche Ressourcen
Links zu anderen Entity Framework-Ressourcen finden Sie im ASP.NET Datenzugriff – Empfohlene Ressourcen.
Informationen zu anderen Möglichkeiten zum Behandeln verschiedener Parallelitätsszenarien finden Sie unter Optimistische Parallelitätsmuster und Arbeiten mit Eigenschaftswerten auf MSDN. Im nächsten Tutorial wird gezeigt, wie Sie die Vererbung von Tabellen pro Hierarchie für die Instructor
Entitäten und Student
implementieren.
Nächste Schritte
In diesem Tutorial:
- Haben Sie Informationen über Parallelitätskonflikte erhalten
- Optimistische Parallelität hinzugefügt
- Geänderter Abteilungscontroller
- Getestete Parallelitätsbehandlung
- Die Seite „Löschen“ aktualisiert
Fahren Sie mit dem nächsten Artikel fort, um zu erfahren, wie Sie die Vererbung im Datenmodell implementieren.
Feedback
https://aka.ms/ContentUserFeedback.
Bald verfügbar: Im Laufe des Jahres 2024 werden wir GitHub-Issues stufenweise als Feedbackmechanismus für Inhalte abbauen und durch ein neues Feedbacksystem ersetzen. Weitere Informationen finden Sie unterFeedback senden und anzeigen für