实体框架的线程安全性

46

Entity Framework 生成的上下文对象不是线程安全的。

如果我为每个线程使用两个不同的实体上下文(并在每个上下文上分别调用 SaveChanges()),这样会是线程安全的吗?

// this method is called from several threads concurrently
public void IncrementProperty()
{
   var context = new MyEntities();

   context.SomeObject.SomeIntProperty++;
   context.SaveChanges();
}

我相信实体框架上下文实现了某种“计数器”变量,以跟踪当前上下文中的值是否仍然是最新的。

  1. 使用上述代码 - 从单独的线程中调用 - 我是否仍然需要在增量/保存更改周围加锁?
  2. 如果需要,那么在这种简单情况下,最好的实现方式是什么?

"Entity Framework 生成的上下文对象不是线程安全的。" - 你为什么这么说? - RPM1984
我指的是MSDN: http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.aspx上的内容,它说“ObjectContext类不是线程安全的。” - Harper
没错,我知道你的意思——上下文而非实体。这就是为什么你不应该在OC中使用单例模式。 - RPM1984
3个回答

48

在单个Entity Framework上下文中操作多个线程不是线程安全的。

为每个线程创建一个独立的上下文实例是线程安全的。只要每个执行线程都有其自己的EF上下文实例,就可以放心使用。

在您的示例中,您可以从任意数量的线程同时调用该代码,并且每个线程都将愉快地使用其自己的上下文。

但是,建议您按照以下方式实现“using”块:

// this method is called from several threads concurrently
public void IncrementProperty()
{
   using (var context = new MyEntities())
   {
      context.SomeObject.SomeIntProperty++;
      context.SaveChanges();
   }
}

谢谢。那么当你调用SubmitChanges()时,SomeIntProperty的实际值是在什么时候读取的呢?我的意思是,如果第一个线程将值增加到3,那么第二个线程可能仍然在某处缓存值2,还是会立即检索该值? - Harper
我忽略了你例子中几个重要的方面。首先,你没有检索实例“SomeObject”的代码。因此,如示所示,代码不会工作。在上下文之间没有缓存、缓冲区或锁的共享。因此,每个带有其上下文实例的线程可以被视为在两台不同计算机上运行的进程。因此,您必须相应地处理数据库并发问题。 - Jim Reineri
2
你需要实现乐观并发或者数据库事务来处理数据库的并发问题。 - Jim Reineri

1
您可以使用工厂模式将 DbContext 注入为一个工厂而不是一个实例,查看此链接:https://github.com/vany0114/EF.DbContextFactory。这样更安全,避免在存储库中硬编码实例创建。

http://elvanydev.com/EF-DbContextFactory/

有一个扩展程序可以很容易地实现这一点,它是 Ninject 的扩展程序。只需要调用方法 kernel.AddDbContextFactory<YourContext>(); 即可,同时您需要通过接收一个 Func<YourContext> 来更改您的存储库。

0
我相信 "SomeObject.SomeIntProperty" 是静态的。这与实体是否线程安全无关。如果您在多线程环境中写入静态变量,应始终使用双重检查锁定来确保线程安全。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接