У меня возникла проблема с перехватом исключения DbUpdateConcurrencyException с помощью Entity Framework 5. Проблема заключается в том, что EF обновляет запись, несмотря на то, что свойство RowVersion (Timestamp) изменилось с момента извлечения строки из базы данных. Действие 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);
}
Затем у меня есть базовое представление редактирования, в котором есть HiddenFor для свойства RowVersion.
@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")
}
Затем у меня есть действие HttpPost Edit, которое берет данные из viewModel и добавляет их в профиль пользователя, который я извлек из базы данных. Затем я изменяю свойства этого профиля на те, которые были получены от клиента, включая 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);
}
Я проверяю это, дважды открывая страницу редактирования. Затем я обновляю вторую страницу и нажимаю «Сохранить», что фиксирует изменения в базе данных. База данных показывает, что версия строки фактически была изменена, чтобы отразить обновление. Когда я изменяю вторую страницу и нажимаю «Сохранить», несмотря на то, что версия строки этого профиля отличается от версии строки, созданной при сохранении первого профиля, изменения также сохраняются в базе данных. Я проверил, что действительно, версия строки в базе данных фактически изменена дважды.
У меня странное чувство, что я упускаю здесь очевидное, но любая помощь будет принята с благодарностью.