好吧,我有这段代码:
var companyId = 1;
var categoryId = 1;
var item = _dbContext.FirstOrDefault(i =>
i.CompanyId == companyId && i.CategoryId == categoryId);
if (item == null) {
var newItem = new Item()
{
CategoryId = companyId,
CompanyId = categoryId,
// some other properties
};
_dbContext.Items.Add(newItem);
_dbContext.SaveChanges();
}
else
{
// update some properties of the item (not CompanyId or CategoryId)
item.xyz = "new Value"
_dbContext.Items.Update(item);
_dbContext.SaveChanges();
}
这是我访问Items
表的唯一代码。我想确保没有为CompanyId-CategoryId
组合制作重复的条目,也就是说,这种组合应该是唯一的。但即使代码在插入之前检查是否存在,我如何才能在数据库中找到重复的条目?
请忽略其他所有内容(架构,使用DB Context等,因为我刚刚编写了这个简单的代码来理解这个问题)。另外,我知道我可以在数据库级别实现这个功能并使列组合独一无二,但有没有办法在应用程序代码中执行此操作(在C#中)?我正在使用EF,如果这很重要的话。为什么会出现这个问题,多线程?
请帮助我理解这个问题或者给我一些指示,以便我在这方面应该进一步阅读。感谢。
答案 0 :(得分:1)
如果您想在应用程序代码中执行此操作,那么您将不得不在某处强制执行单线程。一种方法是使用锁定来生成一段代码,一次只能输入一个线程。然后,您将重新检查记录的存在,如果仍然为,则执行您的插入。它看起来像这样:
// declared at class level
private static readonly object ItemCreationSyncLock = new object();
public void MyMethodThatCreatesAnItem()
{
// ... setup code ...
var item = _dbContext.Items.FirstOrDefault(itm => item.Name == criteria);
if(item == null)
{
lock(ItemCreationSyncLock)
{
item = _dbContext.Items.FirstOfDefault(itm => item.Name == criteria);
if(item == null)
{
item = new Item { Name = criteria };
_dbContext.Items.Add(item);
_dbContext.SaveChanges();
}
}
}
}
所以我介绍了""避免应用程序代码中的重复条目,但重要的是要意识到,即使作为备份,您仍然希望在数据库级别强制执行唯一约束,如果您确实希望确保重复项不存在。通过将应用程序扩展到多个实例,无论是使用Web园/场还是扩展到Azure上的其他实例,我都能够击败我刚刚向您展示的后卫。
答案 1 :(得分:1)
只有拥有一台Web服务器才能在C#中使用锁定。如果您有一个服务器场,那么您的应用程序的每个实例都将拥有自己的ItemCreationSyncLock。
要在数据库中创建同步锁,对于SQL Server就像这样简单:
using (var db = new Db())
using (var tran = db.Database.BeginTransaction())
{
db.Database.ExecuteSqlCommand("exec sp_getapplock 'MyAppLock','exclusive';");
//only one thread across all application instances will execute this code at one time
tran.Commit();
}