实体框架6 - 检查记录插入和并发性存在之前实体、框架、存在、发性

2023-09-03 05:13:14 作者:越回忆、越心痛

我有以下情形:

使用EF6检查是否有记录已经存在业务逻辑功能。如果记录不存在,它被插入在数据库中。

A business logic function that uses ef6 checks if a record already exists. If the record does not exists, it is inserted on the database.

像这样

if (!product.Skus.Any(s => s.SkuCodeGroup == productInfo.SkuCodeGroup && s.SkuCode == productInfo.SkuCode))
    AddProductSku();

我有多个线程调用这个业务逻辑功能。什么情况是:

I have multiple threads calling this business logic function. What happens is:

如果该函数调用同时使用相同的参数,如果两种情况下检查该记录存在 - 并且它不存在。因此,这两种情况下插入相同的记录。

If the function is called simultaneous with the same parameters, both instances checks if the record exists - and it does not exists. So both instances inserts the same record.

在context.SaveChanges()调用的第一个实例,一切都OK。但第二个调用SaveChanges()抛出一个异常,因为记录已经存在(有数据库上的唯一索引)。

When context.SaveChanges() is called on the first instance, all goes ok. But the second SaveChanges() throws an exception because the record already exists (there is an unique index on the database).

如何实现这一点避免异常?

How can I implement this to avoid the exception?

我解决它通过使用锁{},但它会创建一个我不想要一个瓶颈。

I solved it by using a lock {}, but it creates a bottleneck that i don't want.

感谢你。

推荐答案

实际上,你可以赶上 UpdateException 和处理响应。

You can actually catch the UpdateException and handle the response.

首先,以下code将显示我们感兴趣的是从SQL错误:

Firstly, The following code will show the errors we are interested in from 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;
    }
}