2012-02-01 124 views
2

我有一个程序在两台服务器上重复运行。 我需要选择和更新程序中的DB记录,所以我需要EF锁定表或事务,否则程序的每个副本都可以选择和修改相同的记录。第一个副本使数据库发生更改时,另一个不应运行相同的代码部分。实体框架事务

我在EF中发现了TransactionScope,但它无法正常工作,因为在第一个副本运行时,我可以在SQL Server MGM工作室中对该表进行多次选择和更新。

我有一个简短的代码片段,请验证它:

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) 
    { 


    } 
} 
+0

在这一点上,你有3个合理的答案。所有三个答案都集中在同一件事上,但从你的意见看来,我们没有达到你想要的。我认为你应该花一些**质量时间来考虑你的问题以及如何清楚地表达它。 – EBarr 2012-02-02 13:45:52

回答

1

你需要通过SQL手动锁定表。我发现这篇文章,我认为它是你需要的。但它不是令人满意..

Locking a table with a select in Entity Framework

编辑:

 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; } 
} 

EDIT2:

我用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 
+0

但是为什么?如果我在没有提交或回滚的情况下在MGM工作室中写入事务,并在另一个选项卡中写入选择内容,则会看到选择运行但尚未得到结果。如果您停止交易,则选择将立即运行。 – domozi 2012-02-01 12:57:56

+0

如果使用调试器来遍历代码,则可以实现此行为。当你在“}”上时,你在SQL Server Management Studio中具有完全相同的行为。 TransactionScope使用using收集所有信息,然后在事务中using块调用的“Dispose”函数中提交它。那就是你不能编辑表格的地方。 – stylefish 2012-02-01 17:39:23

0

这可归结为乐观并发。您需要一种确保每个应用程序都知道数据何时发生变化并确定处理策略的方法。我强烈建议阅读本:

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

希望帮助 - 让我知道,如果有具体的,你需要澄清什么!

快乐编码,
干杯,
克里斯。

2

技术上你所要求的是一个很长的(er)正在运行的事务,其隔离级别高于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 

} 

随着中说,我很怀疑这是你真正想要。可序列化的事务可能会让大部分数据库被锁定。那是什么意思?这里是微软如何描述串行事务:

SERIALIZABLE指定以下内容:

  • 声明不能读取已修改但其他事务尚未提交的数据。
  • 没有其他事务可以修改当前事务读取的数据,直到当前事务完成。
  • 其他事务不能插入具有键值的新行,这些键值落在当前事务中任何语句读取的键的范围内,直到当前事务完成。

范围锁被放置在匹配一个transaction.This块执行每个语句的搜索条件的关键值的范围从更新或插入那些符合任何由执行的语句的任何行其他交易当前 交易。

....

因为并发低,使用此选项仅在必要时

正如@Bertie指出的,实体框架是围绕乐观并发模型构建的。使用乐观并发(OC)和大量模式来处理不可避免的碰撞有很多原因。 OC让你变得更高,更有趣。使用serializalbe交易的一切都会让你喜欢布鲁斯威利斯在12只猴子里 - 塞满了Thorazine的气球,在你的软垫房间的地板上流着直jack d的流涎声。你不想要那个,现在呢?

+0

根据这篇文章默认级别是可序列化的:http://msdn.microsoft.com/en-us/library/ms172152(v=vs.90).aspx所以水平是在他的情况下可序列化 – stylefish 2012-02-01 18:38:52

+0

没有开玩笑.. 。我很习惯指定选项,我忘了默认级别 - 呃。 – EBarr 2012-02-01 18:56:10

+0

默认级别是READ COMMITTED。 但我分析我的代码,我认为它不能正常工作。如果我打开一个上下文,并在打开一个transactionScope里面的某个地方,它不起作用!但为什么? 每个示例代码显示,我需要打开一个tranScope,并在里面打开EF上下文。 我的想法是否正确? – domozi 2012-02-02 13:10:34