我想编写一个方法,将实体插入我的数据库(如果它尚不存在)。我希望它在许多服务器上都是线程安全的。实际上,它必须是数据库级别的并发安全。实体框架可以实现吗?或者我必须使用原始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。
答案 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将对您更新的行进行锁定。此锁定将阻止任何其他进程不仅写入,而且甚至在第一个进程结束事务之前读取数据。因此,您的架构中基本上会有一个锁定列/行/表。每当您需要执行全局操作时,您将启动事务并更新锁定,然后执行您需要执行的任何其他工作。在第一个流程结束交易之前,任何竞争流程都无法读取/更新锁定。