gpt4 book ai didi

c# - Entity Framework 的悲观并发

转载 作者:太空狗 更新时间:2023-10-29 23:51:14 25 4
gpt4 key购买 nike

在关于 Entity Framework 的文档中写到它不支持开箱即用的悲观并发。这就是为什么在使用 Linq to Entities 语法从表中读取实体时无法锁定表的原因。

假设我们有下一个任务:数据库中有多个线程共享一个表。表具有下一个结构:

Table Counter
Name : varchar[10]
Value : bigint

每个线程都希望获得 name = "some name"的计数器,获取它的计数器值,然后增加它并保存回数据库。

在这里我们可以看到一个问题,即所有这些线程都可以同时从数据库中读取相同的值并增加它。假设我们有 5 个线程。初始计数器值为 1。如果所有线程都读取计数器,则所有线程都获得值 1。然后它们增加它的值并将其保存回数据库。最后我们的计数器值等于 2。但预期值为 6,因为我们希望每个线程都从数据库中获取实际值。

我认为可以使用像这样具有高隔离级别的事务范围来解决这个问题:

    using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions{IsolationLevel = System.Transactions.IsolationLevel.Serializable}))
{
using (var context = new TestDbContext())
{
var counter = context.Counters.First();
Thread.Sleep(2000);
counter.Value ++;
Console.WriteLine("Value={0}", counter.Value);
context.SaveChanges();
}
transactionScope.Complete();
}

没有隔离级别允许我们在从表中读取记录后锁定表。在事务 SQL 中,我们有 UPDLOCK 和 HOLDLOCK 关键字,它们允许我们锁定表直到事务完成。所以我解决了这个问题,为 DbContext 创建了一个扩展方法:

public static class DataContextExtensions
{
public static void LockTable<TEntity>(this DbContext context) where TEntity : class
{
var tableName = (context as IObjectContextAdapter).ObjectContext.CreateObjectSet<TEntity>().EntitySet.Name;
List<TEntity> topEntity = context.Database.SqlQuery<TEntity>(String.Format("SELECT TOP 1 * from {0} WITH (UPDLOCK, HOLDLOCK)", tableName)).ToList();
}
}

现在我可以在读取后锁定表时使用此方法:

    using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions{IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted}))
{
using (var context = new FrontierDbContext())
{
context.LockTable<Counter>();
var counter = context.Counters.First();
Thread.Sleep(2000);
counter.Value ++;
Console.WriteLine("Value={0}", counter.Value);
context.SaveChanges();
}
transactionScope.Complete();
}

之后所有想要增加计数器值的线程将从数据库中获取实际值,因为它们将等待直到表空闲。

正如我所说,这是我的解决方法,也许我在某个地方错了。这就是为什么我想请您指出更好的解决方案。因为我不想重新发明轮子提前致谢!

最佳答案

在您的情况下,您可以在 TransactionScope using block 中针对这种特定情况使用变通方法,如下所示:

const string updateStr = "update table set counter = counter + 1";
context.ExecuteSqlCommand(updateStr);
context.SaveChanges();

关于c# - Entity Framework 的悲观并发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22114360/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com