Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use EF Core concurrency token created by ForNpgsqlUseXminAsConcurrencyToken

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?

like image 351
Fanda Avatar asked Apr 27 '17 09:04

Fanda


1 Answers

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.

like image 97
Fanda Avatar answered Oct 19 '22 21:10

Fanda