Configuring timestamp tokens through data annotation

We have already seen how data annotation configuration works. For timestamp-based tokens, we need a property that will have a byte array, and it should be marked using the Timestamp data annotation. This is the only configuration required from our end; EF will take care of the rest:

    public class Post 
{
// Code removed for brevity
[Timestamp]
public byte[] Timestamp { get; set; }
}

The preceding configuration will let EF Core consider the Timestamp column as the concurrency column, and any further update to this column will restrict users from performing parallel updates.

Since we are introducing a new column to the entity, related commands should be updated as well. In our scenario, we need to update the UpdatePostCommand type with new byte array Timestamp property and its usage as shown in the following code:

    public class UpdatePostCommand : CommandBase, ICreatePostCommand<int>
{
// Code removed for brevity
public byte[] Timestamp { get; set; }
// Code removed for brevity
public async Task<int> HandleAsync()
{
var post = new Post
{
// Code removed for brevity
Timestamp = Timestamp
};
// Code removed for brevity
}
}

The new column should be included in the Edit method of PostController:

    public async Task<IActionResult> Edit(
int id, [Bind("Id,Title,Content,Summary," + "PublishedDateTime,
Url,VisitorCount,CreatedAt,ModifiedAt,BlogId,AuthorId,"
+ "CategoryId,TagIds,FileId,Timestamp")] Post post,
IFormFile headerImage)
{
// Code removed for brevity
if (ModelState.IsValid)
{
var transactions = new TransactionScope();
try
{
Models.File file = null;
if (headerImage == null || (headerImage != null
&& !headerImage.ContentType.ToLower().StartsWith("image/")))
{
await _postRepository.ExecuteAsync(
new UpdatePostCommand(_context)
{
// Code removed for brevity
Timestamp = post.Timestamp
});
return RedirectToAction("Index");
}
// Code removed for brevity
transactions.Transactions.Add
(_context.Database.BeginTransaction());
await _postRepository.ExecuteAsync(
new UpdatePostCommand(_context)
{
// Code removed for brevity
Timestamp = post.Timestamp
});
transactions.Commit();
}
catch (DbUpdateConcurrencyException exception)
{
// Code removed for brevity
}
return RedirectToAction("Index");
}
// Code removed for brevity
}

Finally, the Timestamp column should be configured as a hidden column, just like we would perform for an identity column. This is required since the UI should retain the timestamp value on the Post:

    // Code removed for brevity
<form asp-action="Edit">
<div class="form-horizontal">
<// Code removed for brevity
<input type="hidden" asp-for="Timestamp" />
// Code removed for brevity
</div>
</form>
// Code removed for brevity

We are not there yet; we have updated the entity, but we are supposed to update the database. So, we need to update the schema as shown in the following screenshot, by adding migration and updating the database:

Let's get back to the same scenario, which will have two tabs and will try to perform the update at the same time. The following Post list page screenshot is highlighted with the existing values which will be modified by users for testing:

In the first tab, we are retaining the value and performing an update that will update the values, since EF will allow one update to happen irrespective of the values updated in any of the columns (except Timestamp, which should not be updated by the user):

The Post entity data will be persisted without any issues since the Timestamp value doesn't change and it matches the value from the database.

The non-timestamp based concurrency token stops the update from happening if the concurrency check column value is changed, whereas the timestamp-based concurrency token will track the changes performed in the entity and will allow the change to be persisted if the timestamp value matches with the database value. Before persisting, it will increment the timestamp value.

The first tab operation works fine as expected; we can see the modified column was updated with the new value, confirming that the data was updated properly in the following screenshot:

The second tab will try to update the Post entity, but it will fail irrespective of the column that was updated during this Edit operation. This behavior is expected since the Timestamp value was changed during the previous update, which makes the Timestamp value stale. The data in the following screenshot is updated by the second user which will be posted to the service:

We can see from the following error that the concurrency conflict has occurred and EF has thrown the error, which restricts the user from persisting the stale record. The data update is restricted due to stale data and the same is highlighted in the following image:

We have exhaustively seen how the timestamp-based concurrency token works and how it can be configured using data annotation. In the next section, let's see how we can configure the same token using Fluent API.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset