Entity Framework 4.0 在默认时并不处理并发的情况,也就是出现并发忽略它们。但EF支持处理并发的情况,有两种方法,一种是在存储过程中自行处理。另一种是EF的MODEL上增加一个TimeStamp,EF支持这个TimeStamp来处理并发。看下面EF的模型EDM中SSDL节:

   1:          <EntityType Name="Categories2">
   2:            <Key>
   3:              <PropertyRef Name="CategoryID" />
   4:            </Key>
   5:            <Property Name="CategoryID" Type="int" Nullable="false" StoreGeneratedPattern="Identity" />
   6:            <Property Name="CategoryName" Type="nvarchar" MaxLength="15" />
   7:            <Property Name="Description" Type="ntext" />
   8:            <Property Name="Picture" Type="image" />
   9:            <Property Name="RowVersion" Type="timestamp" Nullable="false" StoreGeneratedPattern="Computed" />
  10:          </EntityType>

CSDL节:

   1:  <EntityType Name="Categories2">
   2:            <Key>
   3:              <PropertyRef Name="CategoryID" />
   4:            </Key>
   5:            <Property Type="Int32" Name="CategoryID" Nullable="false" annotation:StoreGeneratedPattern="Identity" />
   6:            <Property Type="String" Name="CategoryName" MaxLength="15" FixedLength="false" Unicode="true" />
   7:            <Property Type="String" Name="Description" MaxLength="Max" FixedLength="false" Unicode="true" />
   8:            <Property Type="Binary" Name="Picture" MaxLength="Max" FixedLength="false" />
   9:            <Property Type="Binary" Name="RowVersion" Nullable="false" MaxLength="8" FixedLength="true" annotation:StoreGeneratedPattern="Computed" ConcurrencyMode="Fixed" />
  10:          </EntityType>

注意第9行,我们新增加一个列RowVersion. 它的ConcurrencyMode等Fixed,这个是关键之处。这里我们使用POCO:

   1:      [DataContract] 
   2:      public partial class Categories2
   3:      {
   5:          [DataMember]
   6:          public virtual int CategoryID
   7:          {
   8:              get;
   9:              set;
  10:          }
  11:          [DataMember]
  12:          public virtual string CategoryName
  13:          {
  14:              get;
  15:              set;
  16:          }
  17:          [DataMember]
  18:          public virtual string Description
  19:          {
  20:              get;
  21:              set;
  22:          }
  23:          [DataMember]
  24:          public virtual byte[] Picture
  25:          {
  26:              get;
  27:              set;
  28:          }
  29:          [DataMember]
  30:          public virtual byte[] RowVersion
  31:          {
  32:              get;
  33:              set;
  34:          }

注意,那个attribute不是必须的。接下来看到主要代码:

   1:             using (var context = new DBEntities())
   2:              {
   3:                  //Add new categroy
   4:                  Categories2 newcategroy = new Categories2() { CategoryName = "testname" };
   5:                  context.Categories2.AddObject(newcategroy);
   6:                  context.SaveChanges();
   7:                  //Get it
   8:                  var categoriesfromdb = context.Categories2.Where(c => c.CategoryID == newcategroy.CategoryID).First();
   9:                  Console.WriteLine("Just insert category Id: {0}", categoriesfromdb.CategoryID);
  10:                  
  11:                  //使用SQL执行一个Update模拟并发的情况
  12:                  context.ExecuteStoreCommand(@"update Categories2 set CategoryName='456' where CategoryID = @p0"
  13:                      , categoriesfromdb.CategoryID );
  14:              
  15:                  categoriesfromdb.CategoryName = "testname333";
  16:   
  17:                  try
  18:                  {
  19:                      context.SaveChanges();
  20:                      Console.WriteLine("No concurrency exception.");
  21:                  }
  22:                  catch (OptimisticConcurrencyException)
  23:                  {
  24:                       ///出现并发冲突的处理
  25:                      Console.WriteLine(" concurrency exception happend");
  26:                      //testname333
  27:                      //implement last record wins strategy
  28:                      context.Refresh(System.Data.Objects.RefreshMode.ClientWins, categoriesfromdb);
  29:                      //456
  30:                      //context.Refresh(System.Data.Objects.RefreshMode.StoreWins, categoriesfromdb);
  31:   
  32:                      Console.WriteLine("categoriesfromdb.CategoryName: {0} ", categoriesfromdb.CategoryName);
  33:                      Console.WriteLine("OptimisticConcurrencyException handled and changes saved");
  34:   
  35:                      context.SaveChanges();  
  36:                  }
  37:   
  38:                  //Clear
  39:                  context.DeleteObject(newcategroy);
  40:                  context.SaveChanges();
  41:              }

你可以把上面的代码放到一个UnitTest中, 先增加一个Entity,然后从DB查询出,然后用T-SQL语句修改它,  后面接着又修它的属性。运行时我们将Catch到OptimisticConcurrencyException

输出:

Just insert category Id: 58
concurrency exception happend
categoriesfromdb.CategoryName: 456
OptimisticConcurrencyException handled and changes saved

1 passed, 0 failed, 0 skipped, took 0.83 seconds (Ad hoc).

EF中有两种处理模式强制用户数据到服务器(ClientWins)和用服务器数据刷新用户数据(StoreWins),上面代码使用Context来RefreshModel.ClientWins,最后Name属性是testname333,实现了并发中“最后一个赢”的效果。相反,使用StoreWins策略那个属性的值将是456。 最后清理刚才测试的对象。

这是上面通过Command运行的Update T-SQL:

exec sp_executesql N'update Categories2 set CategoryName=''456'' where CategoryID = @p0',N'@p0 int',@p0=60

我们再看来EF是怎么实现的, SaveChange时生成的T-SQL是:

   1:  exec sp_executesql N'update [dbo].[Categories2]
   2:  set [CategoryName] = @0
   3:  where (([CategoryID] = @1) and ([RowVersion] = @2))
   4:  select [RowVersion]
   5:  from [dbo].[Categories2]
   6:  where @@ROWCOUNT > 0 and [CategoryID] = @1',N'@0 nvarchar(15),@1 int,@2 binary(8)',@0=N'testname333',@1=60,@2=0x0000000000008521

注意TimeStamp做为了where查询的子句,这样保证了这条数据的被更新。 我们还可以使用SQL SERVER 2008 中 rowversion 类型 来做为这个列的类型。

希望这篇POST对您开发有帮助。


作者:Petter Liu
出处:http://www.cnblogs.com/wintersun/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
该文章也同时发布在我的独立博客中-Petter Liu Blog

posted on 2011-04-02 16:31  PetterLiu  阅读(1196)  评论(0编辑  收藏  举报