为什么在尝试避免将具有相同键的实体插入数据库时 RepeatableRead 隔离级别不适用于 EF Core?
Why RepeatableRead isolation level not working with EF Core when trying to avoid inserting entities with the same key into the database?
我试图避免将具有相同 Identifier
字段值的多行插入到数据库中。这是代码:
public async Task InsertTransaction(Transaction transaction)
{
await using var dbTransaction = await _dbContext.Database.BeginTransactionAsync(IsolationLevel.RepeatableRead);
var existing = await _dbContext.Set<Transaction>()
.AsNoTracking()
.FirstOrDefaultAsync(t => t.Identifier == transaction.Identifier);
if (existing != null) {
return;
}
_dbContext.Set<Transaction>().Add(transaction);
await _dbContext.SaveChangesAsync();
await dbTransaction.CommitAsync();
return;
}
我假设 RepeatableRead
隔离级别在这里应该足够了,因为它应该锁定读取搜索条件包含 Identifier
的查询,并且在第一个进入事务之后的所有请求都将等待它完成。
但是,当 运行 并发测试时,我插入了多个具有相同 Identifier
的行,并且只有在将事务隔离级别更改为 Serializable
.
后才能正常工作
I'm assuming that RepeatableRead isolation level should be enough here
没有。 REPEATABLE READ 不会对不存在的数据使用 key range locks。 SERIALIZABLE 是唯一的隔离级别。尽管 SERIALIZABLE 将采用共享 (S) 范围锁,因此在多个会话持有相同的 S 锁之后,冲突的插入将造成死锁,而不是在 SELECT 查询处阻塞第二个会话。
I'm getting multiple rows inserted with the same Identifier
除了 insert-if-not-exists 事务之外,您还应该 在 Identifier
上有一个唯一索引到 prevent重复。
在 SQL 服务器中,要使用的锁提示是 updlock,holdlock
,它强制限制性更新 (U) 锁和键范围锁。所以像:
public bool Exists<TEntity>(int Id) where TEntity: class
{
var et = this.Model.FindEntityType(typeof(TEntity));
return this.Set<TEntity>().FromSqlRaw($"select * from [{et.GetSchema()??"dbo"}].[{et.GetTableName()}] with(updlock,holdlock) where Id = @Id",new SqlParameter("@Id",Id)).Any();
}
你还需要一个事务,否则U锁会立即释放,但事务可以在默认的READ COMMITTED隔离级别。
我试图避免将具有相同 Identifier
字段值的多行插入到数据库中。这是代码:
public async Task InsertTransaction(Transaction transaction)
{
await using var dbTransaction = await _dbContext.Database.BeginTransactionAsync(IsolationLevel.RepeatableRead);
var existing = await _dbContext.Set<Transaction>()
.AsNoTracking()
.FirstOrDefaultAsync(t => t.Identifier == transaction.Identifier);
if (existing != null) {
return;
}
_dbContext.Set<Transaction>().Add(transaction);
await _dbContext.SaveChangesAsync();
await dbTransaction.CommitAsync();
return;
}
我假设 RepeatableRead
隔离级别在这里应该足够了,因为它应该锁定读取搜索条件包含 Identifier
的查询,并且在第一个进入事务之后的所有请求都将等待它完成。
但是,当 运行 并发测试时,我插入了多个具有相同 Identifier
的行,并且只有在将事务隔离级别更改为 Serializable
.
I'm assuming that RepeatableRead isolation level should be enough here
没有。 REPEATABLE READ 不会对不存在的数据使用 key range locks。 SERIALIZABLE 是唯一的隔离级别。尽管 SERIALIZABLE 将采用共享 (S) 范围锁,因此在多个会话持有相同的 S 锁之后,冲突的插入将造成死锁,而不是在 SELECT 查询处阻塞第二个会话。
I'm getting multiple rows inserted with the same Identifier
除了 insert-if-not-exists 事务之外,您还应该 在 Identifier
上有一个唯一索引到 prevent重复。
在 SQL 服务器中,要使用的锁提示是 updlock,holdlock
,它强制限制性更新 (U) 锁和键范围锁。所以像:
public bool Exists<TEntity>(int Id) where TEntity: class
{
var et = this.Model.FindEntityType(typeof(TEntity));
return this.Set<TEntity>().FromSqlRaw($"select * from [{et.GetSchema()??"dbo"}].[{et.GetTableName()}] with(updlock,holdlock) where Id = @Id",new SqlParameter("@Id",Id)).Any();
}
你还需要一个事务,否则U锁会立即释放,但事务可以在默认的READ COMMITTED隔离级别。