How to handle concurrency in ASP.NET Core Web API.
SolutionCreate an empty project and update the Startup class to add services and middleware for MVC.
- public void ConfigureServices(
- IServiceCollection services)
- {
- services.AddMvc();
- }
- public void Configure(
- IApplicationBuilder app,
- IHostingEnvironment env)
- {
- app.UseMvcWithDefaultRoute();
- }
Add a controller with GET and PUT to demonstrate concurrency.
- [Route("movies")]
- public class MoviesController : Controller
- {
- const string ETAG_HEADER = "ETag";
- const string MATCH_HEADER = "If-Match";
- [HttpGet("{id}")]
- public IActionResult Get(int id)
- {
- var model_from_db = new Movie
- {
- Id = 1,
- Title= "Thunderball",
- ReleaseYear = 1965,
- };
- var eTag = HashFactory.GetHash(model_from_db);
- HttpContext.Response.Headers.Add(ETAG_HEADER, eTag);
- if (HttpContext.Request.Headers.ContainsKey(MATCH_HEADER) &&
- HttpContext.Request.Headers[MATCH_HEADER].RemoveQuotes() == eTag)
- return new StatusCodeResult(StatusCodes.Status304NotModified);
- return Ok(model_from_db);
- }
- [HttpPut("{id}")]
- public IActionResult Put(int id, [FromBody]Movie model)
- {
- var model_from_db = new Movie
- {
- Id = 1,
- Title = "Thunderball-changed", // data changed
- ReleaseYear = 1965,
- };
- var eTag = HashFactory.GetHash(model_from_db);
- HttpContext.Response.Headers.Add(ETAG_HEADER, eTag);
- if (!HttpContext.Request.Headers.ContainsKey(MATCH_HEADER) ||
- HttpContext.Request.Headers[MATCH_HEADER].RemoveQuotes() != eTag)
- {
- return new StatusCodeResult(StatusCodes.Status412PreconditionFailed);
- }
- else
- {
- // saving should be OK
- }
- return NoContent();
- }
- }
Send a GET request and observe the ETag header (using Postman).

Now, using this ETag value, send a PUT request.

We want to ensure that the users are not overriding changes done by other users between the time they retrieved the data and the time they submitted their changes.
At its core, implementing such concurrency is a simple two-step process:
GETWe add a magic value to the response based on the data we hold at the time. Usua, ly ETag header is added for this purpose containing hashed value based on data/body of the response.
Clients send this ETag value in subsequent requests as If-Match header, which we compare against ETag produced for current data and if unchanged, send a 304 (Not Modified) response.
PUTWe compare against ETag produced for current data and if changed, send a 412 (Precondition Failed) response.
Other MethodsUsing hashed values of model representation is not the only way to implement concurrency. Entity Framework also provides mechanism to implement concurrency. I’ll discuss in a later post on EF Core.
Source Code
Tridip BhattacharjeePosted Dec 19, 2017, 4:18 AM
Tell me what the below line is doing for post action var eTag = HashFactory.GetHash(model_from_db); HttpContext.Response.Headers.Add(ETAG_HEADER, eTag); if (!HttpContext.Request.Headers.ContainsKey(MATCH_HEADER) || HttpContext.Request.Headers[MATCH_HEADER].RemoveQuotes() != eTag) { return new StatusCodeResult(StatusCodes.Status412PreconditionFailed); } else { // saving should be OK } .Status304NotModified);
Tridip BhattacharjeePosted Dec 19, 2017, 4:17 AM
Tell me what the below line is doing for get action var eTag = HashFactory.GetHash(model_from_db); HttpContext.Response.Headers.Add(ETAG_HEADER, eTag); if (HttpContext.Request.Headers.ContainsKey(MATCH_HEADER) && HttpContext.Request.Headers[MATCH_HEADER].RemoveQuotes() == eTag) return new StatusCodeResult(StatusCodes.Status304NotModified);
Tridip BhattacharjeePosted Dec 19, 2017, 4:14 AM
I am not very advanced developer. so after reading your write up do not understand the intention or objective. we think about concurrency when we dealing data in db. we protect two user should not be able to edit and submit data to db by concurrency check with rowversion in sql server but here you have a action get/post which return data to client. so why we need to think about concurrency ? please tell me what is your objective and why we need to think about concurrency at action level?