Boo*_*ius 5 c# sql multithreading entity-framework
在关于实体框架的文档中写道,它不支持开箱即用的悲观并发.这就是为什么在使用Linq to Entities语法从中读取实体时无法锁定表的原因.
让我们假设我们有下一个任务:有多个线程共享数据库中的一个表.表有下一个结构:
Table Counter
Name : varchar[10]
Value : bigint
Run Code Online (Sandbox Code Playgroud)
每个线程都希望得到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();
}
Run Code Online (Sandbox Code Playgroud)
非隔离级别允许我们在从中读取记录后锁定表.在transact 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();
}
}
Run Code Online (Sandbox Code Playgroud)
现在我可以使用这个方法,当我想在读取之后锁定表格如下:
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();
}
Run Code Online (Sandbox Code Playgroud)
之后,所有想要增加计数器值的线程都将从数据库中获取实际值,因为它们将一直等到表空闲.
正如我所说,这是我的解决方法,也许我错了.这就是为什么我想请你指出更好的解决方案.因为我不想重新发明轮子提前谢谢!
小智 0
在您的情况下,您可以在 TransactionScope using 块中针对这种特定情况使用解决方法,如下所示:
const string updateStr = "update table set counter = counter + 1";
context.ExecuteSqlCommand(updateStr);
context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
2724 次 |
最近记录: |