根据操作结果可以看到,在Entity Framework的默认环境情况下,系统会使用合并方式处理并发,把输入数据的所有修改值都保存到当前上下文当中,并同时修改数据库当中的值。
4.1.2 删除与更新操作同时运行
Entity Framework 能以完善的机制灵活处理同时更新同一对象的操作,但一旦删除操作与更新操作同时运行时,就可能存在逻辑性的异常。例如:两个客户端同时加载了同一个对象,第一个客户端更新了数据后,把数据再次提交。但在提交前,第二个客户端已经把数据库中的已有数据删除。此时,上下文中的对象处于不同的状态底下,将会引发 OptimisticConcurrencyException 异常。
观察运行测试结果,当运行 Delete 方法,对象已经在数据库中被删除,对象的EntityState处于 Detached 状态。此时使用 SaveChanges 保存更新数据时,引发了OptimisticConcurrencyException 异常。在捕获异常,把对象状态更改为 Added ,再使用SaveChanges保存数据,数据就能顺利地保存到数据库中。但值得留意,因为对象是在删除后重新加载的,所以对象的 Id 也会被同步更新。
以合并数据的方式处理并发冲突固然方便快节,但在业务逻辑较为复杂的系统下并不适合使用此处理方式。比如在常见的Order、OrderItem的表格中,OrderItem 的单价,数量会直接影响Order的总体价格,这样使用合并数据的方式处理并发,有可能引起逻辑性的错误。此时,应该考虑以其他方式处理并发冲突。
4.2 当发生数据并发时,保留最新输入的数据
要验证输入对象的属性,必须先把该属性的 ConcurencyMode 设置为 Fixed,这样系统就会实时检测对象属性的输入值 。
观察测试结果,可见当RefreshMode状态为ClientWins时,系统将会保存上下文当中的对象属性,使用此方法可以在发生并发异常时保持最新输入的对象属性。
4.3 当发生数据并发时,保留最初输入的数据
把对象属性的 ConcurencyMode 设置为 Fixed 后,同时更新该属性,将会激发 OptimisticConcurrencyException 异常。此时使用 ObjectContext.Refresh (RefreshMode,object) 刷新上下文中该对象的状态,当 RefreshMode 为 StoreWins 时,系统就会把数据源中的数据代替上下文中的数据。
观察测试结果,可见当 RefreshMode 状态为 StoreWins 时,系统将会以数据源中的数据代替上下文当中的对象属性。在业务逻辑较为复杂的的系统当中,建议使用此方式处理并发异常。
回到目录
五、回顾 LINQ to SQL 并发处理的方式
Entity Framework 当中简化了并发处理的方式,然而温故而知新,LINQ to SQL 中并发处理所使用的方式也值得回顾一下。下面将与大家一起回顾一下 LINQ to SQL 当中并发处理的方式。
例子当中使用 RefreshMode.KeepChanges 的方式处理并发,系统会保存最新输入的数据。观察测试结果,当系统发生第一次更新时,数据成功保存到数据库当中。在 DataContext 未释放前,再次输入数据,引发了ChangeConflictException异常。在捕获此并发异常后,系统以 RefreshMode.KeepChanges 方式进行处理,最后新输入的数据成功保存到数据库当中。
回到目录
六、结合事务处理并发冲突
Entity Framework 中已经有比较完善的机制处理并发,但使用乐观性并发处理数据,一旦多个客户端同时更新同一张表格的同一个对象时,将会激发 OptimisticConcurrencyException 异常,系统必须预先定制好处理方案对此并发异常进行处理。结合事务使用乐观性并发共同处理数据,是一个比较高效的数据管理方式。事务能对数据的更新进行检测,一旦发现异常,便会实现回滚。本文会使用常用的隐式事务 TransactionScope 作为例子进行介绍,对事务的详细介绍,可以参考 “C#综合揭秘——细说事务”。
观察测试结果,当系统发生并发异常时,数据库只会保留首次输入的更新值。值得注意的是:使用此数据处理方式与悲观并发方式最大区别在于,传统的悲观并发处理方式不允许同一时刻有多个客户端处理同一个数据表中的相同对象,但因为客观因数的影响,系统难以仔细辨认客户同时进行修改的是否同一个数据项,所以基本的做法是使用锁把更新对象进行锁定。但如此一来,无论客户同时更新的是否同一个数据项,操作都将会被延时或禁止。换句话说,无论需要更新的是相同对象还是不同对象,客户端都不能同时更新同一个数据表。若使用此乐观并发方式,系统允许多个客户端同时处理同一个数据表。如果所处理的是数据表中的不同对象,操作可以顺利地进行而不会相互影响。如果所处理的是数据表中的相同对象,操作将会保存第一次输入的对象值。
回到目录
总结
并发话题与get='_blank'>线程、进程等其他话题有所不同,它并没有复杂的类和方法。处理并发来来去去都是简单的几行代码,它所重视的是并发异常发生后所带来的后果与处理方式。与中国传统的太极相似,并发只重其意,不重其招,只要深入地了解其过程,考虑其可能带来的问题后,你便可以对其收发自如。
原标题:C# Entity Framework并发处理
关键词:C#