Reputation: 3786
I have found npgsql provider extension to set up concurrency token for entity framework core entity, which should do something like this:
modelBuilder.Entity<MyEntity>(b =>
{
b.Property<uint>("xmin")
.HasColumnType("xid")
.ValueGeneratedOnAddOrUpdate()
.IsConcurrencyToken();
});
If I understand it well, it creates shadow property on entity.
How can I use this property to track concurrent updates (more users try to update the same entity) in ASP.NET Core, for example? Should I try to to map xmin column to normal property and put it to hidden input tag as it is showed in asp.net core documentation? Or is there another way?
Upvotes: 7
Views: 5836
Reputation: 3786
Discussing with Olivier MATROT I realized how to do what I need.
The solution is not ideal because it is tied up with provider (SQL server provider needs byte[] as concurrency token property), but works as expected:
public class MyEntity
{
public int Id { get; set; }
public string Name { get; set; }
public uint ConcurrencyStamp { get; set; }
}
In the context (If migrations are used, property need to be removed from migration code to eliminate column creation attempt)
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// ...
builder.Entity<MyEntity>()
.Property(e => e.ConcurrencyStamp)
.ForNpgsqlHasColumnName("xmin")
.ForNpgsqlHasColumnType("xid")
.ValueGeneratedOnAddOrUpdate()
.IsConcurrencyToken();
}
Edit view
@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>
and default scaffolded action (just to complete the example)
[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);
}
So the solution is to make xmin value accessible as the entity property.
Upvotes: 7
Reputation:
The tracking is done automatically for you by Entity Framework .
Basically, it goes like this :
Technically, in this sample, the xmin value is used in a where clause during the update statment. Because the value of xmin has changed, the number of row affected by the UPDATE query is 0 instead of 1.
Upvotes: 0