首页 文章

如何使用ForNpgsqlUseXminAsConcurrencyToken创建的EF Core并发令牌

提问于
浏览
4

我找到了npgsql provider extension为实体框架核心实体设置并发令牌,它应该做这样的事情:

modelBuilder.Entity<MyEntity>(b =>
{
    b.Property<uint>("xmin")
        .HasColumnType("xid")
        .ValueGeneratedOnAddOrUpdate()
        .IsConcurrencyToken();
});

如果我理解得很好,它会在实体上创建阴影属性 .

例如,如何使用此属性跟踪ASP.NET Core中的并发更新(更多用户尝试更新同一实体)?我是否应该尝试将xmin列映射到普通属性并将其放入隐藏的输入标记中,因为它显示在asp.net core documentation中?或者还有另一种方式吗?

2 回答

  • 0

    实体框架会自动为您完成跟踪 .

    基本上,它是这样的:

    • 用户A加载ID为1的MyEntity .

    • 用户B加载ID为1的MyEntity .

    • 用户A保存对身份1的MyEntity的修改.PygreSQL会自动修改xmin列 .

    • 用户B保存对身份1的MyEntity的修改 . 实体框架引发了一个OptimisticConcurrencyException,因为xmin的值在用户红色数据和尝试更新数据的那一刻之间发生了变化 .

    从技术上讲,在此示例中,xmin值在更新语句期间的where子句中使用 . 由于xmin的值已更改,因此受UPDATE查询影响的行数为0而不是1 .

  • 4

    与Olivier MATROT讨论我意识到如何做我需要的 .

    解决方案并不理想,因为它与提供程序绑定(SQL Server提供程序需要byte []作为并发令牌属性),但是按预期工作:

    public class MyEntity
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public uint ConcurrencyStamp { get; set; }
    }
    

    在上下文中(如果使用迁移,则需要从迁移代码中删除属性以消除列创建尝试)

    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
    
        // ...
    
        builder.Entity<MyEntity>()
            .Property(e => e.ConcurrencyStamp)
                .ForNpgsqlHasColumnName("xmin")
                .ForNpgsqlHasColumnType("xid")
                .ValueGeneratedOnAddOrUpdate()
                .IsConcurrencyToken();
    }
    

    编辑视图

    @model Namespace.MyEntity
    
    <form asp-action="Edit">
        <div class="form-horizontal">
            <h4>Person</h4>
            <hr />
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    
            <input type="hidden" asp-for="Id" />
            <input type="hidden" asp-for="ConcurrencyStamp" />
    
            <div class="form-group">
                <label asp-for="Name" class="col-md-2 control-label"></label>
                <div class="col-md-10">
                    <input asp-for="Name" class="form-control" />
                    <span asp-validation-for="Name" class="text-danger"></span>
                </div>
            </div>
    
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit" value="Save" class="btn btn-default" />
                </div>
            </div>
    
        </div>
    </form>
    

    和默认的scaffolded动作(只是为了完成示例)

    [HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Edit(int id, [Bind("Id,Name,ConcurrencyStamp")] MyEntity model)
    {
        if (id != model.Id)
        {
            return NotFound();
        }
    
        if (ModelState.IsValid)
        {
            try
            {
                _context.Update(model);
                await _context.SaveChangesAsync();
            }
            catch (DbUpdateConcurrencyException)
            {
                if (!MyEntityExists(model.Id))
                {
                    return NotFound();
                }
                else
                {
                    throw;
                }
            }
            return RedirectToAction("Index");
        }
        return View(model);
    }
    

    因此,解决方案是将xmin值作为实体属性进行访问 .

相关问题