Entity Framework的ConcurrencyCheck和标准TransactionScopes之间的区别?

时间:2013-09-15 16:52:27

标签: c# entity-framework concurrency

我是管理并发的新手,所以如果这个问题不明智,请道歉。

在过去的项目中,我通过在TransactionScope中包装操作来实现并发检查 - 类似这样:

using (var scope = new TransactionScope(TransactionScopeOption.Required, options))
{
    var copiedFolder = new Folder();
    using (var db = CreateContext())
    {
        // do stuff safely
    }
    scope.Complete();
    return copiedFolder;
}

但是我刚刚遇到了Entity Framework的并发方法:http://www.asp.net/mvc/tutorials/getting-started-with-ef-using-mvc/handling-concurrency-with-the-entity-framework-in-an-asp-net-mvc-application

我想知道什么时候用一个比另一个好。从技术上讲,它们是一样的吗?如果没有,它们有何不同?

3 个答案:

答案 0 :(得分:3)

他们不是一回事。并发作为一种机制可以确保当两个用户同时访问同一个实体时不会进行覆盖。

我会举个例子。想象一下Id 541的一行,其名称设置为“Alex”。

假设您有两个用户,即用户A和用户B,都试图修改该行的名称。以下场景是并发性的全部内容:

  • 用户A从数据库中读取实体。
  • 用户B从数据库中读取实体。
  • 用户A将Name的值修改为“Alexander”并提交更改为 数据库中。
  • 用户B将Name的值修改为“Alexander B”并提交更改为 数据库中。

用户A完成的更改将被覆盖,用户B不知道它。

并发操作基本上是确保如果用户B读取和用户B更改提交之间的值发生变化,它将抛出DbConcurrencyException,指示实体已更改,从而为用户B提供取消保存或继续操作的能力反正。

如果要确保特定属性不受不可见覆盖的影响,请使用[ConcurrencyCheck]进行标记。默认情况下,实体框架通过在WHERE子句中提供Id(标识)列来进行更新。当另一列用[ConcurrencyCheck]标记时,它还将向UPDATE命令添加附加条件,这在本例中基本上是WHERE NAME ='Alex'。因此,由于Alex不再是数据库中的值(用户A已经更改了它),它将执行0次更新,这将在内部导致异常。

答案 1 :(得分:2)

  

从技术上讲,它们是一样的吗?

不,他们不是。它们甚至都不相关。

  • TransactionScope 用于确保在一个事务中提交数据库更改。当出于任何原因需要在一个数据库事务中发生多个SaveChanges调用时,通常将TS与实体框架一起使用。典型的场景是保存实体并将其生成的主键设置在另一个实体的某个属性中。 (请注意,一个SaveChanges调用始终在一个事务中,通常不需要TS。)

    TS无法解决任何并发冲突。当两个用户影响相同的记录时,提交事务的最后一个用户将获胜。

  • 并发解决方案是关于当两个不同的用户尝试“同时”更改相同记录时要执行的操作。您引用的链接详细说明了EF支持的最常见策略 - 乐观并发。 EF中最常见的方法是在数据库表中引入TimeStamp列(至少在SQL服务器中)在每次更新记录时自动递增。时间戳列也在概念模型(=类模型)中引入并标记为[Timestamp](数据注释)或IsConcurrencyToken(流畅映射),因此该属性将包含在update和delete命令中。简而言之,它看起来像这样:

    UPDATE x SET y WHERE x.TimeStamp = <value when the record was fetched>
    

    当另一位用户同时更新记录时,EF会记录受影响的零记录并抛出DbUpdateConcurrencyException。您可以在number of ways

  • 中处理此异常

答案 2 :(得分:1)

resolver.resolve(options, callback)属性只能应用于单字节数组属性,而Timestamp属性可以应用于任何数据类型的任意数量的属性。