简体   繁体   English

当模型属性具有 [ConcurrencyCheck] 属性时,ASP.NET Core MVC ConcurrencyCheck 失败

[英]ASP.NET Core MVC ConcurrencyCheck is failing when the model property has the [ConcurrencyCheck] attribute

I'm new to concurrency checking in ASP.NET Core MVC and must be implementing something incorrectly here, however Microsoft documentation seems to be thin on the ground.我是 ASP.NET Core MVC 中的并发检查的新手,必须在这里实现错误,但是 Microsoft 文档似乎很薄弱。 Is anyone able to point me in the right direction?有人能指出我正确的方向吗?

This is a simple user class, which can be edited by an admin.这是一个简单的用户类,可由管理员编辑。 Without any concurrency checking, the form changes are saved to the database with no issue, however, when I add the [ConcurrencyCheck] attribute to one of the model properties, the save to the database fails even when the field value is changed (I was expecting this to only fail when User A updated the field before User B did, etc.)如果没有任何并发​​检查,表单更改将毫无问题地保存到数据库中,但是,当我将 [ConcurrencyCheck] 属性添加到模型属性之一时,即使字段值发生更改,保存到数据库也会失败(我是期望这只会在用户 A 在用户 B 之前更新字段时失败,等等)

Any help would be greatly appreciated.任何帮助将不胜感激。

Here are some snippets.这里有一些片段。

Model模型

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}";
        }

    }

Controller控制器


 // 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");

        }

View看法

@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");}
}

According to ms official document, it recommends to use row version to handle concurrency conflicts .根据ms官方文档,推荐使用row version处理并发冲突

To sum up from the document, we need to add a column named such as "RowVersion" which data type is "rowversion" and add it in your Entity.从文档中总结,我们需要添加一个名为“RowVersion”的列,其数据类型为“rowversion”,并将其添加到您的实体中。

    [Timestamp]
    public byte[] RowVersion { get; set; }

在此处输入图片说明

Next, before calling await _context.SaveChangesAsync();接下来,在调用await _context.SaveChangesAsync();之前await _context.SaveChangesAsync(); we need to put that original RowVersion property value in the OriginalValues collection for the entity我们需要将原始 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.

相关问题 asp.net 核心 3.1 EF ConcurrencyCheck 无法使用剃刀 CRUD 脚手架而不是 MVC - asp.net core 3.1 EF ConcurrencyCheck not working using razor CRUD scaffolding not MVC 实体框架:如何检查列是否具有ConcurrencyCheck属性? - Entity framework : how to check if column has the ConcurrencyCheck attribute? 在ASP.NET Core MVC中绑定具有数组属性的模型 - Bind model with array property to form in ASP.NET Core MVC ASP.NET Core MVC模型属性绑定为null - ASP.NET Core MVC model property binding is null 模型的ASP.NET Core MVC List属性在视图中为空 - ASP.NET Core MVC List property of model empty in view 将img绑定到ASP.NET Core MVC中的模型属性 - Bind img to a model property in ASP.NET Core MVC 包含 asp.net 核心 3 MVC 中的列表属性的 model 的表单 - form for a model that contains a list property in asp.net core 3 MVC 如果 model 验证失败,则 ASP.Net Core MVC model 属性无效 - ASP.Net Core MVC model property is nullified if model validation fails ASP.NET Core Web Application: create model class that has default User model as property - ASP.NET Core Web Application: create model class that has default User model as property ASP.NET Core MVC 从查询中获取 Model 的属性 - ASP.NET Core MVC get Property of Property of Model from Query
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM