现在的位置: 首页 > 综合 > 正文

Entity framework:关于并发处理的一点建议

2012年08月25日 ⁄ 综合 ⁄ 共 5100字 ⁄ 字号 评论关闭

 其实2个月之前就在做关于并发的练习了,不过这段时间都在忙碌mvc的开发与应用,很多事情都被搁置了,就连ef 4.0beta版修复的几个bug都没来得及做点什么。

  除了前一篇提到的几篇关于ef在n层应用中的文章,这里还介绍2篇Updating data using Entity Framework in N-Tier and N-Layer Applications (part1,part2),
可以说cesardelatorre 的这2篇文章的立意还是很高的(如图1),希望有兴趣的朋友自己仔细看看。因为个人觉得,相比前面提到cesardelatorre 的文章似乎更有针对性。

http://blogs.msdn.com/cesardelatorre/archive/2008/09/04/updating-data-using-entity-framework-in-n-tier-and-n-layer-applications-short-lived-ef-contexts.aspx

不过我做练习的时候,受dsimmonsAttachAsModified a small step toward simplifying EF n-tier patterns这篇文章的影响更多一些。显然对于wcf service对于实体的操作无非就是读取和更新,读取当然容易,对于更新就必须知道更新之前 和更新之后2个版本的值,需要知道那些属性的原始值并用于并发检查,需要知道哪些属性已被更改,通常的做法是:在更新的方法里先调用context.Attach(originalEntity)” 接着调用 “context.ApplyPropertyChanges(newEntity最后 调用context.SaveChanges 完成整个更新过程,因为ApplyPropertyChanges()方法会通过迭代来判断那些属性被更改,但是对于传递2个版本的实体无疑会给wcf传输带来负担,另外一方面clone手动克隆出一个newEntity也可能是一个令人讨厌的过程,当该实体有非常多的属性(几十或者一百+)。如果我们发现,我们的实体使用一个固定的属性用于并发检查,然后我们就可以通过改变wcf service方法把整个进程变得简单。

当然这个修改wcf service的扩展方dsimmons的blog也给出了

private static void SetAllPropertiesModified(ObjectContext context,

                                            object entity)

{

   var stateEntry = context.ObjectStateManager.GetObjectStateEntry(entity);

   foreach (var propertyName in from fm in stateEntry.CurrentValues.                 

                                               DataRecordInfo.FieldMetadata

                                select fm.FieldType.Name)

   {

       stateEntry.SetModifiedProperty(propertyName);

   }

}

 

public static void AttachAsModified(this ObjectContext context,

                                   IEntityWithKey entity)

{

   context.Attach(entity);

   SetAllPropertiesModified(context, entity);

}

 

public static void AttachAsModifiedTo(this ObjectContext context,

                                     string entitySetName, object entity)

{

   context.AttachTo(entitySetName, entity);

   SetAllPropertiesModified(context, entity);

}

为了理解并发检查,这一特性我做了一个练习:首先,关于如何在EF中设置并发,只需要设置属性的concurrency mode 为fixed即可,下面的工作就是捕捉并发异常Updating data using Entity Framework in N-Tier and N-Layer Applications (part2)中举例,我将Northwind 模型下的orders实体的RequiredDate设置为fixd,然后在加载一个Order(id=12458)对象后,手动去数据库修改RequiredDate的值,这样就实现了一个并发,在代码中用就可以捕获 catch (OptimisticConcurrencyException)                {                  

}捕获这个异常了,

using (NorthWind.NorthwindEFEntities context = new NorthWind.NorthwindEFEntities())

   {

        try

        {

 NorthWind.Orders update = context.Orders.Where(p => p.OrderID == obj.OrderID).FirstOrDefault();

           update.RequiredDate = obj.RequiredDate;

           context.AttachAsModified(obj);

           context.SaveChanges();

          return true;

        }

        catch (OptimisticConcurrencyException)

          {

           context.Refresh(System.Data.Objects.RefreshMode.ClientWins, obj);

           return false;

         }

      }

显然在捕获的异常里,我用context.refresh()方法处理并发,RefreshMode.ClintWins,RefreshMode.StoreWins,

为了搞清他们之间的区别,我在catch (OptimisticConcurrencyException) { }里面我分别使用catch  context.Refresh(RefreshMode.StoreWins, o),结果是保持employeeID 6,RequiredDate 也没有更新,也就是说了忽略当前更新,

catch 使用context.Refresh(RefreshMode.ClientWins, o),结果是o.RequiredDate = DateTimeemployeeID还是7,忽略并发更新,保存本次更新.

鉴于saveAsAttach文中提及的,对于N-tier应用似乎要把每一个属性设为并发模式,这样就似乎比较麻烦,我在设想是不是为实体指定一个专门用于并发检查的属性,
这个问题在在stackoverflow上,我看到很多国外朋友在讨论ef1.0在
 N-Tier 在concurrency上的应用,的确有人为了在N-tier解决并发问题,而特意为每一张表都增加一个rowguid ,timestampl类型的字段,当然对于rowguid ,timestamp类型的字段我之前不是很熟悉,在看完msdn之后,对其有了一定了解之后,
我就把northwind  order表增加一个timestamp类型,并且edm中设置了RequiredDate字段的concurrency mode为fixed:
就是下面
RefreshMode.ClintWins,RefreshMode.StoreWins在处理并发时的sql记录:

--refresh ClientWins ,[RequiredDate] 设为并发模式

exec sp_executesql N'update [dbo].[Orders]

set [EmployeeID] = @0, [Freight] = @1, [OrderDate] = @2, [RequiredDate] = @3, [ShipAddress] = @4, [ShipCity] = @5, [ShipCountry] = @6, [ShipName] = @7, 

[ShippedDate] = @8, [ShipPostalCode] = @9, [ShipRegion] = null

where (([OrderID] = @10) and ([RequiredDate] = @11))

',N'@0 int,@1 decimal(19,4),@2 datetime,@3 datetime,@4 nvarchar(18),@5 nvarchar(5),@6 nvarchar(6),@7 nvarchar(25),@8 datetime,@9 nvarchar(5),@10 int,@11 

datetime',@0=5,@1=32.3800,@2=''1996-07-04 00:00:00:000'',@3=''2009-03-26 13:11:11:990'',@4=N'59 rue de l''Abbaye',@5=N'Reims',@6=N'France',@7=N'Vins et 

alcools Chevalier',@8=''1996-07-16 00:00:00:000'',@9=N'51100',@10=10248,@11=''2009-03-26 01:00:00:000''

go 

--refresh StroeWins ()

exec sp_executesql N'update [dbo].[Orders]

set [RequiredDate] = @0

where (([OrderID] = @1) and ([RequiredDate] = @2))

',N'@0 datetime,@1 int,@2 datetime',@0=''2009-03-26 13:54:55:547'',@1=10248,@2=''2009-03-26 00:00:00:000''

go

select top 1 OrderID,rowguid from Orders ---10248 0x0000000000000E24

--after modify a colume

select top 1 OrderID,rowguid from Orders ---10248 0x0000000000000F6D

--table orders  add a new colume timestamp

exec sp_executesql N'update [dbo].[Orders]

set [RequiredDate] = @0

where ([OrderID] = @1)

select [rowguid]

from [dbo].[Orders]

where @@ROWCOUNT > 0 and [OrderID] = @1',N'@0 datetime,@1 int',@0=GetDate(),@1=10248

以上是2者的不同,显然这种方法是可以很好的控制并发问题的,当然这和是不是使用wcf没有必然关系。在我将要完结的项目中,使用mvc+ef,使用这种方式控制并发效果还不错。当然在stackoverflow上,一些国外朋友早已证实了这点,这次只是自己切身体会了一把。

抱歉!评论已关闭.