Entity Framework 5 を使用して DbUpdateConcurrencyException をキャッチしようとすると問題が発生します。行がデータベースから取得されてから RowVersion (Timestamp) プロパティが変更されたにもかかわらず、EF がレコードを更新しているという問題があります。HttpGet Edit アクションはデータベースからユーザー プロファイルを取得し、ユーザーがロールを選択してビューに渡すためのチェックボックスのリストを含む値を ViewModel に渡します。
public ActionResult Edit(int id = 0)
{
UserProfile userProfile = unitOfWork.UserProfileRepository.GetUserProfileById(id);
UserProfileEditViewModel viewModel = new UserProfileEditViewModel
{
UserId = userProfile.UserId,
UserName = userProfile.UserName,
FirstName = userProfile.FirstName,
LastName = userProfile.LastName,
Email = userProfile.Email,
RowVersion = userProfile.RowVersion,
};
var allRoles = unitOfWork.RoleRepository.GetAllRoles();
var userProfileRoles = userProfile.Roles;
foreach (var role in allRoles)
{
if (userProfileRoles.Contains(role))
{
viewModel.Roles.Add(new RoleViewModel
{
RoleId = role.RoleId,
RoleName = role.RoleName,
Assigned = true,
});
}
else
{
viewModel.Roles.Add(new RoleViewModel
{
RoleId = role.RoleId,
RoleName = role.RoleName,
Assigned = false,
});
}
}
return View(viewModel);
}
次に、RowVersion プロパティの HiddenFor を持つ基本的な編集ビューを作成します。
@model MvcWebsite.ViewModels.UserProfileEditViewModel
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<legend>UserProfile</legend>
@Html.HiddenFor(model => model.UserId)
@Html.HiddenFor(model => model.UserName)
@Html.HiddenFor(model => model.RowVersion)
<div class="editor-label">
@Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
@Html.DisplayFor(model => model.UserName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Email)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Email)
@Html.ValidationMessageFor(model => model.Email)
</div>
<div class="editor-field">
<table>
<tr>
@Html.EditorFor(model => model.Roles)
@Html.ValidationMessageFor(model => model.Roles)
</tr>
</table>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
次に、viewModel からデータを取得し、データベースから取得したユーザー プロファイルに追加する HttpPost Edit アクションを作成します。次に、このプロファイルのプロパティを、RowVersion を含むクライアントから取得したものに変更します (RowVersion を元の状態に戻します)。
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(UserProfileEditViewModel model)
{
try
{
if (ModelState.IsValid)
{
var userProfile = unitOfWork.UserProfileRepository.GetUserProfileById(model.UserId);
userProfile.UserName = model.UserName;
userProfile.FirstName = model.FirstName;
userProfile.LastName = model.LastName;
userProfile.Email = model.Email;
userProfile.RowVersion = model.RowVersion;
var roleAssignments = model.Roles;
foreach (var roleAssignment in roleAssignments)
{
if (roleAssignment.Assigned)
{
userProfile.Roles.Add(unitOfWork.RoleRepository.GetRoleById(roleAssignment.RoleId));
}
else
{
userProfile.Roles.Remove(unitOfWork.RoleRepository.GetRoleById(roleAssignment.RoleId));
}
}
unitOfWork.UserProfileRepository.UpdateUserProfile(userProfile);
unitOfWork.Save();
return RedirectToAction("Details", new { id = userProfile.UserId });
}
}
catch (DbUpdateConcurrencyException ex)
{
... Code omitted for brevity
}
}
return View(model);
}
編集ページを 2 回開いてこれをテストします。次に、2 番目のページを更新して [保存] をクリックします。これにより、変更がデータベースにコミットされます。データベースは、更新を反映するために行バージョンが実際に変更されたことを示しています。2 番目のページを変更して [保存] をクリックすると、このプロファイルの行バージョンが最初のプロファイルを保存したときに作成された行バージョンと異なっていても、変更はデータベースにも保存されます。実際、データベースの行バージョンが実際に 2 回変更されていることを確認しました。
ここで明らかなことを見逃しているというおかしな気持ちがありますが、どんな助けでも大歓迎です。