![](/img/trans.png)
[英]asp.net core 3.1 EF ConcurrencyCheck not working using razor CRUD scaffolding not MVC
[英]ASP.NET Core MVC ConcurrencyCheck is failing when the model property has the [ConcurrencyCheck] attribute
我是 ASP.NET Core MVC 中的并发检查的新手,必须在这里实现错误,但是 Microsoft 文档似乎很薄弱。 有人能指出我正确的方向吗?
这是一个简单的用户类,可由管理员编辑。 如果没有任何并发检查,表单更改将毫无问题地保存到数据库中,但是,当我将 [ConcurrencyCheck] 属性添加到模型属性之一时,即使字段值发生更改,保存到数据库也会失败(我是期望这只会在用户 A 在用户 B 之前更新字段时失败,等等)
任何帮助将不胜感激。
这里有一些片段。
模型
public class MyUser : IdentityUser
{
[ConcurrencyCheck]
[PersonalData]
[DisplayName("First Name")]
[Column(TypeName = "nvarchar(100)")]
public string FirstName { get; set; }
[PersonalData]
[DisplayName("Last Name")]
[Column(TypeName = "nvarchar(100)")]
public string LastName { get; set; }
[DisplayName("Created Date")]
[Column(TypeName = "DateTime")]
public DateTime CreatedDateTime { get; set; }
[DisplayName("Customer ID")]
[Column(TypeName = "int")]
public int CustomerId { get; set; }
public string GetFullName()
{
return $"{FirstName} {LastName}";
}
}
控制器
// GET: UserController/Edit/5
public ActionResult Edit(string id)
{
// New user view model.
UserViewModel userViewModel = new UserViewModel();
// Get user details.
MyUser user = _context.Users.Where(u => u.Id == id).Single();
var roles = _context.UserRoles.Where(r => r.UserId == id).ToList();
if (user != null)
{
// Set viewmodel user with user details.
userViewModel.thisUser = user;
// userViewModel.thisUserRoles = _context.UserRoles.Where()
// Get roles assigned to the user.
//userViewModel.thisUserRoles = _context.userRoles.Where(r => r.UserId == id).ToList();
// Set viewmodel variables based on which roles the user has.
if (roles.Count > 0)
{
foreach(var role in roles)
{
switch (_context.Roles.Where(r => r.Id == role.RoleId).Select(r => r.Name).Single().ToString())
{
case "User":
userViewModel.UserRole = true;
break;
case "Sales User":
userViewModel.SalesUserRole = true;
break;
case "Sales Branch User":
userViewModel.SalesBranchUserRole = true;
break;
case "Sales Administrator":
userViewModel.SalesAdministrator = true;
break;
case "Administrator":
userViewModel.Administrator = true;
break;
}
}
}
return View(userViewModel);
}
else
{
ViewBag.ErrorMessage = "The user could not be found.";
return RedirectToAction("Index");
}
}
// POST: UserController/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(UserViewModel userViewModel)
{
if (ModelState.IsValid)
{
userViewModel.thisUser.UserName = userViewModel.thisUser.Email;
userViewModel.thisUser.NormalizedUserName = userViewModel.thisUser.Email.ToUpper();
userViewModel.thisUser.NormalizedEmail = userViewModel.thisUser.Email.ToUpper();
try
{
_context.Entry(userViewModel.thisUser).State = EntityState.Modified;
_context.SaveChanges();
}
catch (Exception ex)
{
// Prompt the user to try again.
ModelState.AddModelError("", "The information has been updated by someone else before you saved. Please try again.");
return View(userViewModel);
}
}
return RedirectToAction("Index");
}
看法
@model MyApp.ViewModels.UserViewModel
@{
ViewData["Title"] = "Edit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
@*<h1>Edit</h1>*@
@*<h5>
Details
</h5>
<hr />*@
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="All" class="text-danger"></div>
@Html.HiddenFor(m => m.thisUser.Id)
@Html.HiddenFor(m => m.thisUser.PasswordHash)
@Html.HiddenFor(m => m.thisUser.LockoutEnd)
@Html.HiddenFor(m => m.thisUser.LockoutEnabled)
@Html.HiddenFor(m => m.thisUser.AccessFailedCount)
@Html.HiddenFor(m => m.thisUser.CreatedDateTime)
@Html.HiddenFor(m => m.thisUser.SecurityStamp)
@Html.HiddenFor(m => m.thisUser.ConcurrencyStamp)
@Html.HiddenFor(m => m.thisUser.EmailConfirmed)
<div class="form-group">
<label asp-for="thisUser.FirstName" class="control-label"></label>
<input asp-for="thisUser.FirstName" class="form-control" />
<span asp-validation-for="thisUser.FirstName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="thisUser.LastName" class="control-label"></label>
<input asp-for="thisUser.LastName" class="form-control" />
<span asp-validation-for="thisUser.LastName" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="thisUser.Email" class="control-label"></label>
<input asp-for="thisUser.Email" class="form-control" />
<span asp-validation-for="thisUser.Email" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="thisUser.PhoneNumber" class="control-label"></label>
<input asp-for="thisUser.PhoneNumber" class="form-control" />
<span asp-validation-for="thisUser.PhoneNumber" class="text-danger"></span>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="thisUser.PhoneNumberConfirmed" /> @Html.DisplayNameFor(model => model.thisUser.PhoneNumberConfirmed)
</label>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="thisUser.TwoFactorEnabled" /> @Html.DisplayNameFor(model => model.thisUser.TwoFactorEnabled)
</label>
</div>
<div class="form-group">
<label asp-for="thisUser.CustomerId" class="control-label"></label>
<input asp-for="thisUser.CustomerId" class="form-control" />
<span asp-validation-for="thisUser.CustomerId" class="text-danger"></span>
</div>
<h5>
Roles
</h5>
<hr />
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="UserRole" /> @Html.DisplayNameFor(model => model.UserRole)
</label>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="SalesUserRole" /> @Html.DisplayNameFor(model => model.SalesUserRole)
</label>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="SalesBranchUserRole" /> @Html.DisplayNameFor(model => model.SalesBranchUserRole)
</label>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="SalesAdministrator" /> @Html.DisplayNameFor(model => model.SalesAdministrator)
</label>
</div>
<div class="form-group form-check">
<label class="form-check-label">
<input class="form-check-input" asp-for="Administrator" /> @Html.DisplayNameFor(model => model.Administrator)
</label>
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
根据ms官方文档,推荐使用row version来处理并发冲突。
从文档中总结,我们需要添加一个名为“RowVersion”的列,其数据类型为“rowversion”,并将其添加到您的实体中。
[Timestamp]
public byte[] RowVersion { get; set; }
接下来,在调用await _context.SaveChangesAsync();
之前await _context.SaveChangesAsync();
我们需要将原始 RowVersion 属性值放在实体的 OriginalValues 集合中
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> EditAsync(UserViewModel userViewModel)
{
if (ModelState.IsValid)
{
try
{
_context.Entry(userViewModel.thisUser).Property("RowVersion").OriginalValue = userViewModel.thisUser.RowVersion;
_context.Update(userViewModel.thisUser);
await _context.SaveChangesAsync();
}
catch (Exception ex)
{
// Prompt the user to try again.
ModelState.AddModelError("", "The information has been updated by someone else before you saved. Please try again.");
return View(userViewModel);
}
}
return RedirectToAction("Index");
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.