EntityFamework 4.0 中的並發處理(一)


       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


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com