如何使用由ForNpgsqlUseXminAsConcurrencyToken创建的EF Core并发标记

7
我发现了一个名为npgsql提供程序extension,可以为实体框架核心实体设置并发令牌,应该像这样做:
modelBuilder.Entity<MyEntity>(b =>
{
    b.Property<uint>("xmin")
        .HasColumnType("xid")
        .ValueGeneratedOnAddOrUpdate()
        .IsConcurrencyToken();
});

如果我理解正确,它会在实体上创建阴影属性。
例如,在ASP.NET Core中,我如何使用此属性来跟踪并发更新(多个用户尝试更新同一实体)?我应该尝试将xmin列映射到普通属性,并像asp.net core documentation中所示将其放入隐藏输入标记中吗?或者还有其他方法吗?

“track concurrency update” 究竟是什么意思? - anon
我已经编辑了问题,使其更加清晰。谢谢。 - Fanda
2个回答

7

与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>

并默认的脚手架操作(仅为完成示例而设)
[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值作为实体属性进行访问。

是的,并发令牌必须映射到实体属性。因此,这是管理此令牌的数据库引擎。您可以在我的旧问题中了解更多信息。 - anon

0

Entity Framework会自动为您进行跟踪。

基本上,它是这样的:

  1. 用户A加载ID为1的MyEntity。
  2. 用户B加载ID为1的MyEntity。
  3. 用户A保存ID为1的MyEntity的修改。xmin列由PostgreSQL自动修改。
  4. 用户B保存ID为1的MyEntity的修改。由于xmin的值在用户读取数据和尝试更新数据之间发生了更改,因此Entity Framework引发了OptimisticConcurrencyException。

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


但是我需要记住实体加载时间的xmin值,对吧?如果我不通过保存表单将其发送回来,该怎么办?如果我再次在服务器端加载实体,它将已经具有新值... - Fanda
这是由你在问题中包含的代码自动完成的。你可以通过加载实体,使用pgAdmin修改PostgreSQL表中的相应记录,修改实体并保存更改来尝试它。 - anon
是的,我知道,但在asp.net中使用时,您需要将其发送到客户端,然后再提交,重新组合实体,然后保存。因此,如果客户端没有显式发送xmin,则它是分离的实体(没有xmin值)。并且在生成视图时,您需要访问xmin值以填充某些隐藏字段以在表单保存时将值发送回来。 - Fanda

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接