如果不存在,实体框架线程安全创建实体

时间:2013-12-07 02:50:00

标签: c# entity-framework thread-safety

我想编写一个方法,将实体插入我的数据库(如果它尚不存在)。我希望它在许多服务器上都是线程安全的。实际上,它必须是数据库级别的并发安全。实体框架可以实现吗?或者我必须使用原始SQL吗?

这是我到目前为止所拥有的:

public void CreateFooIfDoesntExist(int bar)
{
    using (var dbContext = new MyDbContex())
    {
        if (!dbContext.Set<Foo>().Any(f => f.Bar == bar))
        {
            //Not thread safe because two servers could both fail to find any 
            //Foos at the same time.  Then they'd both try to add a Foo.
            dbContext.Set<Foo>().Add(new Foo { Bar = bar });
            dbContext.SaveChanges();
        }
    }
}

我正在使用C#5.0,EF 5.0和MSSQL server 2012。

1 个答案:

答案 0 :(得分:2)

您可以创建唯一索引,因此第二次保存将始终失败,并带有众所周知的错误代码。问题是,您需要一个非自动生成ID的字段来创建唯一索引。

try
{
    using (var context ...) 
    {
        context.Foos.Add(new Foo() { UniqueId = id });
        context.SaveChanges();
    }
 }
 catch (UpdateException e) 
 {
     // record with "id" already exists
 }

在更复杂的场景中,您可以使用数据库事务来同步多个进程。在具有SERIALIZABLE隔离级别的事务中执行数据库更新时,SQL Server将对您更新的行进行锁定。此锁定将阻止任何其他进程不仅写入,而且甚至在第一个进程结束事务之前读取数据。因此,您的架构中基本上会有一个锁定列/行/表。每当您需要执行全局操作时,您将启动事务并更新锁定,然后执行您需要执行的任何其他工作。在第一个流程结束交易之前,任何竞争流程都无法读取/更新锁定。