I have an Index
action in my MVC project that accepts an integer as a parameter which represents the ID of a movie title in my SQL Server
database. If the ID is already in the database, a ViewModel
is populated with the movie title's data and passed back to the view. If the ID is a new one, a new movie Title
object is created, populated, added to the context, and saved to the database. The ID is not automatically assigned by the database. Action code below (some code removed/omitted for brevity and clarity):
[HttpGet]
public async Task<ActionResult> Index(int? i)
{
if (i.HasValue)
{
var movieViewModel = new MovieViewModel();
using (var context = new someEntities())
{
//check if this id already exists in the DB
var matchedTitle = await context.Titles.FindAsync(i.Value);
if (matchedTitle != null && matchedTitle.Category == "movie")
{
//matchedTitle already in the DB..
//..so populate movieViewModel with matchedTitle
}
else
{
//matchedTitle not in the DB, generate a new Title item
var newTitle = TitleHelper.GenerateNewTitle(i.Value, /* .. */);
//add and save
context.Titles.Add(newTitle);
await context.SaveChangesAsync();
//code omitted for brevity
}
}
Response.StatusCode = (int)HttpStatusCode.OK;
return View(movieViewModel);
}
else
{
return View();
}
}
This works fine but will occasionally produce the following error during high traffic:
Violation of PRIMARY KEY constraint 'PK_Title_Title_Id'. Cannot insert duplicate key in object 'dbo.Title'. The duplicate key value is (0147852). The statement has been terminated.
I have not been able to reproduce the error on purpose. If I hit a refresh on that view, the error goes away and the view loads normally. Am I missing a check or is this a race condition scenario? I would have thought that EF
would be able to handle this.
Update:
Some additional info as requested in the comments:
How do I generate IDs?
They IDs are generated based on a movie's respective IMDb ID.
How do I generate a new title?
public static Title GenerateNewTitle(int id, //other properties) { Title newTitle = new Title { //property population here } return newTitle; }