1

我在尝试使用 Entity Framework 5 捕获 DbUpdateConcurrencyException 时遇到问题。我遇到的问题是 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);
    }

然后,我有一个基本的编辑视图,其中包含 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")
}

然后我有一个 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);
    }

我通过两次打开编辑页面来测试这一点。然后我更新第二页并单击保存,这会将更改提交到数据库。数据库显示行版本实际上已更改以反映更新。当我更改第二页并单击保存时,即使此配置文件的行版本与保存第一个配置文件时创建的行版本不同,更改也会保存到数据库中。我确实检查过,数据库中的行版本实际上更改了两次。

我有一种有趣的感觉,我在这里错过了明显的东西,但任何帮助将不胜感激。

4

1 回答 1

0

通常您不会显式更改行版本。这将由 ORM 本身处理。您要做的是将视图模型版本与域版本进行比较。如果它们不匹配,则需要处理这种情况。

if(viewmodel.RowVersion != domainModel.RowVersion)
{
    //model has changed, notify user...
}
else
{
   //update domain model
   //save changes
}

您可以通过使 RowVersion 不可变来进一步防止这种情况

class domainmodel
{
    ...
    public int RowVersion {get; private set;}
}
于 2013-06-04T18:12:42.623 回答