![](/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.