实体框架事务
本文关键字:事务 框架 实体 | 更新日期: 2023-09-27 18:22:09
我有一个程序在两台服务器上重复运行。我需要在程序中选择和更新DB记录,所以我需要EF的表锁或事务,否则程序的每个副本都可以选择和修改相同的记录。当第一个副本进行DB更改时,另一个副本不应该运行相同的代码段。
我在EF中找到了TransactionScope,但它不能正常工作,因为当第一个副本运行时,我可以在SQLServerMGM工作室中对这些表进行多次选择和更新。
我有一个简短的代码片段,请验证它:
using (TransactionScope transaction = new TransactionScope())
{
//select some records which aren't locked by the other copy of the program
//condition: Locked==null
recipientsList = (from c in context.Recipients
where
c.SentToPlatform == false && c.PopupID != null &&
c.Message.MessageStatus == 2 && c.Locked == null
select c).Take(piecePerMinute).ToList();
foreach (var recipient in recipientsList)
{
//i need make some changes on the record, prevent it from the other copy of program
//I need to change locked column to true
recipient.Locked = true;
recipient.LockBy = ipAddress;
Console.Write("I");
Thread.Sleep(1000);
}
//close transaction
try
{
context.SaveChanges();
transaction.Complete();
} catch (Exception ex )
{
}
}
从技术上您想要的是一个长时间运行的事务,其隔离级别高于Read Committed(默认级别)。没有足够的信息让我知道你是想要RepeatableRead还是Serialzable(以避免虚插入)。
你可以通过这样做来完成你的要求:
var opt = new TransactionOptions();
opt.IsolationLevel = IsolationLevel.Serializable;
using (var scope = new TransactionScope(TransactionScopeOption.Required, opt) ){
//read table code
//write table code
//save context & complete scope
}
话虽如此,我非常怀疑这是否是您真正想要的。可序列化事务可能会使数据库的大部分处于锁定状态。这是什么意思?以下是Microsoft如何描述可序列化事务:
SERIALIZABLE指定以下内容:
- 语句无法读取已修改但尚未由其他事务提交的数据
- 在当前事务完成之前,任何其他事务都不能修改当前事务读取的数据
- 在当前事务完成之前,其他事务不能插入键值位于当前事务中任何语句读取的键范围内的新行
范围锁位于与事务中执行的每条语句的搜索条件匹配的键值范围中。这会阻止其他事务更新或插入符合当前交易
由于并发性较低,只有在必要时才使用此选项。
正如@Bertie所指出的,实体框架是围绕乐观并发模型构建的。使用乐观并发(OC)和大量模式来处理不可避免的冲突有很多原因。OC让你更高更有趣。对所有事情都使用可串行化的事务会让你像《12只猴子》中的布鲁斯·威利斯一样——在你的软垫房间里,塞满了一大口的索拉津,流满了口水。你现在不想那样了,是吗?
您需要通过SQL手动锁定表。我找到了这个帖子,我想这正是你需要的。但这并不令人满意。。
使用实体框架中的选择锁定表
编辑:
using (var ts = new TransactionScope())
{
var db = new Db();
var asd = from x in db.MyTable
where x.Id == 1
select x;
asd.First().Name = "test2";
db.SaveChanges(); // if you're here with the debugger the table is locked
}
// and now its unlocked and you can do a select again
internal class Db : DbContext
{
public Db()
{
Database.DefaultConnectionFactory = new SqlConnectionFactory();
//Database.SetInitializer(new DropCreateDatabaseIfModelChanges<Db>());
Database.Connection.ConnectionString =
"yourconnectionstring;";
}
public DbSet<MyTable> MyTable { get; set; }
}
internal class MyTable
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime MyTime { get; set; }
}
第2版:
我在EF和TransactionScope上玩了一会儿。以下是我的结果:
using (var ts = new TransactionScope())
{
var db = new Db();
var asd = from x in db.MyTable
where x.Id == 1
select x;
db.SaveChanges(); // you still can fire selects in the studio
asd.First().Name = "test2"; // now a change is made but not written to the transaction
db.SaveChanges(); // after this call you can't fire selects in the management studio, the table is locked
var asd2 = from x in db.MyTable
where x.Id == 1
select x;
asd2.First().Name = "test3";
db.SaveChanges(); // the table still is locked
}
// now you can do selects again, the table is unlocked
这归结为乐观并发。你需要一种方法来确保每个应用程序都知道它下面的数据何时发生了变化,然后创建一个处理它的策略
http://msdn.microsoft.com/en-us/library/bb738618.aspx
如果它们有帮助的话:
http://blogs.msdn.com/b/cesardelatorre/archive/2008/09/04/updating-data-using-entity-framework-in-n-tier-and-n-layer-applications-short-lived-ef-contexts.aspx
http://blogs.msdn.com/b/cesardelatorre/archive/2008/09/05/optimistic-concurrency-updates-using-entity-framework-in-n-tier-and-n-layer-applications-part-2.aspx
希望这能有所帮助——如果有什么具体的事情需要澄清,请告诉我!
快乐编码,
干杯,
克里斯。