如何使用ForNpgsqlUseXminAsConcurrencyToken創建的EF Core並發令牌

asp.net-core c# concurrency entity-framework-core npgsql

我找到了npgsql provider 擴展來為實體框架核心實體設置並發令牌,它應該做這樣的事情:

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

如果我理解得很好,它會在實體上創建陰影屬性。

例如,如何使用此屬性跟踪ASP.NET Core中的並發更新(更多用戶嘗試更新同一實體)?我是否應該嘗試將xmin列映射到普通屬性並將其放入隱藏的輸入標記中,因為它在asp.net核心文檔中顯示 ?或者還有另一種方式嗎?

一般承認的答案

與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值作為實體屬性進行訪問。


熱門答案

實體框架會自動為您完成跟踪。

基本上,它是這樣的:

  1. 用戶A加載ID為1的MyEntity。
  2. 用戶B加載ID為1的MyEntity。
  3. 用戶A保存對ID為1的MyEntity的修改.PygreSQL會自動修改xmin列。
  4. 用戶B保存對ID為1的MyEntity的修改。實體框架引發了一個OptimisticConcurrencyException,因為xmin的值在用戶紅色數據和嘗試更新數據的那一刻之間發生了變化。

從技術上講,在此示例中,xmin值在更新語句期間的where子句中使用。由於xmin的值已更改,因此受UPDATE查詢影響的行數為0而不是1。



Related

許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow
許可下: CC-BY-SA with attribution
不隸屬於 Stack Overflow