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。
浙公网安备 33010602011771号