如何使用 ForNpgsqlUseXminAsConcurrencyToken 创建的 EF Core 并发令牌
How to use EF Core concurrency token created by ForNpgsqlUseXminAsConcurrencyToken
我发现 npgsql 提供程序 extension 为 entity framework 核心实体设置并发令牌,它应该做这样的事情:
modelBuilder.Entity<MyEntity>(b =>
{
b.Property<uint>("xmin")
.HasColumnType("xid")
.ValueGeneratedOnAddOrUpdate()
.IsConcurrencyToken();
});
如果我理解得很好,它会在实体上创建阴影 属性。
例如,如何使用此 属性 跟踪 ASP.NET Core 中的并发更新(更多用户尝试更新同一实体)?我是否应该尝试将 xmin 列映射到正常 属性 并将其放入隐藏的输入标签,如 asp.net core documentation 中所示?或者有别的办法吗?
跟踪由 Entity Framework 自动为您完成。
基本上,它是这样的:
- 用户 A 使用 ID 1 加载 MyEntity。
- 用户 B 使用 ID 1 加载 MyEntity。
- 用户A保存了对id为1的MyEntity的修改。xmin列由PostgreSQL自动修改。
- 用户 B 保存了对 ID 为 1 的 MyEntity 的修改。entity Framework 引发了 OptimisticConcurrencyException,因为 xmin 的值在用户更改数据和他尝试更新数据之间发生了变化数据。
从技术上讲,在此示例中,xmin 值用于更新语句期间的 where 子句中。因为xmin的值变了,所以UPDATE查询影响的行数是0,而不是1。
通过与 Olivier MATROT 的讨论,我意识到如何做我需要做的事情。
该解决方案并不理想,因为它与提供程序捆绑在一起(SQL 服务器提供程序需要 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 值作为实体可访问 属性。
我发现 npgsql 提供程序 extension 为 entity framework 核心实体设置并发令牌,它应该做这样的事情:
modelBuilder.Entity<MyEntity>(b =>
{
b.Property<uint>("xmin")
.HasColumnType("xid")
.ValueGeneratedOnAddOrUpdate()
.IsConcurrencyToken();
});
如果我理解得很好,它会在实体上创建阴影 属性。
例如,如何使用此 属性 跟踪 ASP.NET Core 中的并发更新(更多用户尝试更新同一实体)?我是否应该尝试将 xmin 列映射到正常 属性 并将其放入隐藏的输入标签,如 asp.net core documentation 中所示?或者有别的办法吗?
跟踪由 Entity Framework 自动为您完成。
基本上,它是这样的:
- 用户 A 使用 ID 1 加载 MyEntity。
- 用户 B 使用 ID 1 加载 MyEntity。
- 用户A保存了对id为1的MyEntity的修改。xmin列由PostgreSQL自动修改。
- 用户 B 保存了对 ID 为 1 的 MyEntity 的修改。entity Framework 引发了 OptimisticConcurrencyException,因为 xmin 的值在用户更改数据和他尝试更新数据之间发生了变化数据。
从技术上讲,在此示例中,xmin 值用于更新语句期间的 where 子句中。因为xmin的值变了,所以UPDATE查询影响的行数是0,而不是1。
通过与 Olivier MATROT 的讨论,我意识到如何做我需要做的事情。
该解决方案并不理想,因为它与提供程序捆绑在一起(SQL 服务器提供程序需要 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 值作为实体可访问 属性。