我有以下情况:
使用ef6的业务逻辑功能检查记录是否已存在。如果记录不存在,则将其插入数据库中。
像这样的东西
if (!product.Skus.Any(s => s.SkuCodeGroup == productInfo.SkuCodeGroup && s.SkuCode == productInfo.SkuCode))
AddProductSku();
我有多个线程调用此业务逻辑功能。会发生什么:
如果使用相同的参数同时调用该函数,则两个实例都会检查该记录是否存在 - 并且它不存在。因此两个实例都插入相同的记录。
在第一个实例上调用context.SaveChanges()时,一切正常。但是第二个SaveChanges()抛出异常,因为记录已经存在(数据库上有唯一索引)。
如何实现此目的以避免异常?
我使用锁{}解决了它,但它造成了我不想要的瓶颈。
谢谢。
答案 0 :(得分:5)
我们不得不处理同样的问题。如果您不想在代码中实现锁定,那么确实没有好的解决方法。您还必须确保将来不会有新行进入数据库的多种方式。
我们所做的是评估异常消息,如果它是重复的密钥错误,我们只是吃异常。事实上,我们甚至不会先检查该行是否存在。无论如何,SQL Server都会为我们这样做。因此,每次插入时都会保存搜索。这种方法适用于我们和我们的应用程序。它可能适用于所有情况,也可能不适用,具体取决于插入后您想要执行的操作。
答案 1 :(得分:2)
您实际上可以抓住UpdateException
并处理回复。
首先,以下代码将显示我们对SQL感兴趣的错误:
SELECT error, description
FROM master..sysmessages
WHERE msglangid == 1033 /* eng */
AND description LIKE '%insert%duplicate%key%'
ORDER BY error
这显示以下输出:
2601 Cannot insert duplicate key row in object '%.*ls' with unique index '%.*ls'. The duplicate key value is %ls.
2627 Violation of %ls constraint '%.*ls'. Cannot insert duplicate key in object '%.*ls'. The duplicate key value is %ls.
因此,我们可以看到我们需要捕获SqlException
值2601
和2627
。
try {
using(var db = new DatabaseContext()){
//save
db.SaveChanges(); //Boom, error
}
}
catch(UpdateException ex) {
var sqlException = ex.InnerException as SqlException;
if(sqlException != null && sqlException.Errors.OfType<SqlError>()
.Any(se => se.Number == 2601 || se.Number == 2627)) /* PK or UKC violation */
{
// it's a dupe... do something about it,
//depending on business rules, maybe discard new insert and attach to existing item
}
else {
// it's some other error, throw back exception
throw;
}
}
答案 2 :(得分:0)
我会利用Entity Framework中内置的并发处理,而不是编写自己的逻辑。
您将并发令牌添加到数据库。通常,这将是RowVersion
字段:
使用FluentAPI:
modelBuilder.Entity<OfficeAssignment>()
.Property(t => t.Timestamp)
.IsRowVersion();
使用数据注释:
[Timestamp]
public byte[] RowVersion { get; set; }
通常,您会在出现问题时处理DbUpdateConcurrencyException
:
using (var context = new SchoolDBEntities())
{
try
{
context.Entry(student1WithUser2).State = EntityState.Modified;
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
Console.WriteLine("Optimistic Concurrency exception occured");
}
}
参考文献:
Configuring a concurrency token
Handling concurrency in Entity Framework
Optimistic concurrency patterns using Entity Framework
修改强> 刚刚意识到我误解了你的问题。正如你的问题标题所示,你在这里并不是真的在谈论并发性。您只想确保记录的自然键是唯一的。这样做的方法就在这里:https://stackoverflow.com/a/18736484/150342