当RowVersion发生变化时,为什么Entity Framework会进行更新?

时间:2013-06-04 18:05:59

标签: asp.net-mvc entity-framework optimistic-concurrency

我在尝试使用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);
    }

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

我有一种有趣的感觉,我在这里错过了明显的,但任何帮助都会非常感激。

1 个答案:

答案 0 :(得分: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;}
}