我的MVC项目中有一个Index
动作,它接受一个整数作为参数,表示我SQL Server
数据库中电影标题的ID。如果该ID已存在于数据库中,则会使用电影标题的数据填充ViewModel
并将其传递回视图。如果ID是新ID,则创建,填充,添加到上下文并保存到数据库的新电影Title
对象。数据库的ID 不会自动分配。下面的操作代码(为简洁和清晰起见,删除/省略了一些代码):
[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();
}
}
此工作正常,但在高流量时偶尔会产生以下错误:
违反PRIMARY KEY约束&#39; PK_Title_Title_Id&#39;。无法插入 对象&#39; dbo.Title&#39;中的重复键。重复的键值是 (0147852)。声明已经终止。
我无法故意重现错误。如果我在该视图上点击刷新,则错误消失并且视图正常加载。我错过了支票还是这是竞争条件?我原以为EF
能够解决这个问题。
更新:
评论中要求的一些其他信息:
如何生成ID?
他们的ID是根据电影的相应IMDb ID生成的。
如何生成新标题?
public static Title GenerateNewTitle(int id, //other properties)
{
Title newTitle = new Title
{
//property population here
}
return newTitle;
}
答案 0 :(得分:1)
前一段时间已经解决了这个问题,但是由于我没有提供任何答案,我提供了以下解决方案以供将来参考。
正如Damien_The_Unbeliever在OP的评论中所建议的那样,问题是由quick'n'dirty解决方案的半完整,未经测试的代码导致的典型竞争条件(错误2627)虽然只是在高流量时发生。除非在检查之前锁定表,否则在插入之前检查对象是否已存在不是解决方案。否则,有人可能会在检查后和插入之前插入对象。
我目前正在覆盖OnException
System.Web.Mvc.Controller
来处理我的例外情况,但"JFDI"
a quick good way to go是关于此的:{/ p>
using System.Data.Entity.Infrastructure;
try
{
await context.SaveChangesAsync();
}
catch(DbUpdateException ex)
{
var sqlException = ex.InnerException.InnerException as SqlException;
if (sqlException != null && sqlException.Number == 2627)
{
//violation of primary key constraint has occurred here, act accordingly
//e.g. pass the populated viewmodel back to the view and return
}
}
答案 1 :(得分:0)
如果在category = show
的位置进行编辑,则会抛出错误,因为
var matchedTitle = await context.Titles.FindAsync(i.Value)
会找到i.value
个ID的记录,但matchedTitle.Category == "movie"
将不匹配,因为这是show
类别的类型,TitleHelper.GenerateNewTitle(/* .. */)
此功能必须是仅从i.value
分配PK ID,因此它会抛出PK违规错误。
试用下面的代码
var newTitle = TitleHelper.GenerateNewTitle(i.Value, /* .. */);
if(newTitle.Title_Id > 0)
{
// Query to update your record
}
else
{
//add and save
context.Titles.Add(newTitle);
await context.SaveChangesAsync();
}