LINQ与EF的并发处理

 

 public class Person
{
     public int Id { get; set; }
     public string Name { get; set; }
     
     [Timestamp]
     public Byte[] Version { get; set; }
}

 

LINQ和EF的并发控制,都是在其生成的SQL语句中的 where 加入时间戳字段作为查询条件进行控制的,如:

EF 自动生成的SQL:

 

exec sp_executesql N'UPDATE [dbo].[People]
SET [Name] = @0
WHERE (([Id] = @1) AND ([Version] = @2))
SELECT [Version]
FROM [dbo].[People]
WHERE @@ROWCOUNT > 0 AND [Id] = @1',
N'@0 nvarchar(max) ,@1 int,@2 binary(8)',@0=N'my name',@1=1,@2=0x0000000000000BBF

 

最后返回 [Version] 更新记录。

 

在 LINQ 的 dbml 中每个实体的字段属性的更新检查都是默认为始终的,即会默认产生并发冲突。

  LINQ 并发的解决方法:

try {  

    //如果发生并发冲突,将继续处理,进入 catch 进行处理方案选择

    db.SubmitChanges(ConflictMode.ContinueOnConflict);

    //或FailOnFirstConflict,这种一般不会进行方案选择,因为它在第一次有并发冲突时,就停止尝试更新,

    //而ContinueOnConflict则会尝试更新所有更新,并累积和返回所有并发冲突。

  } catch (ChangeConflictException) {  

    foreach (ObjectChangeConflict occ in db.ChangeConflicts)  {   

 

        //相关的逻辑代码

        // LINQ的实体对象通过 occ.Object 获取

        // 数据库元表通过 db.Mapping.GetTable(occ.Object.GetType()) 获取

        // 冲突成员通过 occ.MemberConflicts 获取,在其中可以获得 :

        // 冲突成员的当前值(CurrentValue)、冲突成员的原始值(OriginalValue)、冲突成员的数据库值(DatabaseValue)

        // 可以根据上面逻辑代码在以下三种方案选择其一:(成员也可以有自己的解决方案)

       // 1. 以数据库的值为准 

        //occ.Resolve(RefreshMode.OverwriteCurrentValues);

        // 2. 以LINQ实体对象的值为准

        //occ.Resolve(RefreshMode.KeepCurrentValues);

         // 3. 只更新LINQ实体对象中改变的字段的值,其他的保留不变   

       //occ.Resolve(RefreshMode.KeepChanges);  

      }

     //最后再次更新数据库 

     data.SubmitChanges();

}

还有一种解决办法,就是通过事务处理,不作并发冲突方案选择,如发生并发异常,就直接回滚。

using (TransactionScope scope = new TransactionScope())
{

  db.SubmitChanges();

  scope.Complete();

}

 

在 EF 的 edmx 或 model 中的每个实体的字段属性的并发模式都是默认为None的,即不会默认产生并发冲突。

如要处理并发冲突,则如下操作:(需要才设置)

1. 在 edmx 中每个实体的字段属性的并发模式设置为Fixed

2. 在 model 中的某个字段属性上加 Timestamp 特性(每个实体只能有一个)或 ConcurrencyCheck特性。

  并发的解决办法:

  

  try{   

      context.SaveChanges();

    }

    catch (OptimisticConcurrencyException ex){

        // 冲突成员实体通过 ex.StateEntries 获取

        // 同样逻辑代码可以 根据 ObjectStateEntry xxx 对象进行操作

         // 可以根据上面逻辑代码在以下两种方案选择其一:

        // 以实体对象为准

        // context.Refresh(RefreshMode.ClientWins, xxx);

          // 以数据库为准

        // context.Refresh(RefreshMode.StoreWins,xxx); 

        //最后再次更新数据库

        context.SaveChanges();

    }

 

    还有LINQ的事务解决办法同样适合EF。

posted @ 2013-01-19 14:32  Yu  阅读(4023)  评论(1编辑  收藏  举报