同时执行C#代码会导致重复的数据库条目

同时执行C#代码会导致重复的数据库条目,c#,asp.net,entity-framework,C#,Asp.net,Entity Framework,嗯,我有这个代码: 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 = compan

嗯,我有这个代码:

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();
}
这是我访问
表的唯一一段代码。我想确保
CompanyId CategoryId
组合没有重复的条目,也就是说,这个组合应该是唯一的。但是,即使代码在插入之前检查是否存在,如何在数据库中找到重复的条目

请忽略所有其他内容(架构、DB上下文的使用等,因为我刚刚编写了这个简单的代码来理解这个问题)。另外,我知道我可以在DB级别实现这一点,并使列组合唯一,但在应用程序代码(C#)中有什么方法可以做到这一点吗?我正在使用EF,如果这很重要的话。究竟是什么原因导致了这个问题,多线程


请帮助我理解这个问题,或者给我一些关于这方面我应该进一步阅读的指导。谢谢。

如果您想在应用程序代码中执行此操作,那么您必须在某个地方强制执行单线程。一种方法是使用锁定来生成一段代码,一次只能有一个线程进入。然后,您将重新检查记录是否存在,如果仍然为null,则执行插入。它看起来像这样:

// 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 garden/farm还是扩展到Azure上的其他实例,我都能够击败我刚才向您展示的防护措施。

如果您想在应用程序代码中实现这一点,那么您必须在某个地方强制使用单线程。一种方法是使用锁定来生成一段代码,一次只能有一个线程进入。然后,您将重新检查记录是否存在,如果仍然为null,则执行插入。它看起来像这样:

// 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 garden/farm还是扩展到Azure上的其他实例,我都能够击败我刚才向您展示的防护装置。

使用C#锁只能在您只有一个web服务器的情况下起作用。如果您有一个服务器场,那么应用程序的每个实例都将有自己的ItemCreationSync

要在数据库中创建同步锁,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();
    }
只有在只有一台web服务器的情况下,在C#中使用锁才能起作用。如果您有一个服务器场,那么应用程序的每个实例都将有自己的ItemCreationSync

要在数据库中创建同步锁,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();
    }

两个线程都试图找到现有的连接,但都失败了,然后添加了一个,两个都是。您必须确保代码的这个特定部分(至少)不是并行运行的,而是顺序运行的。谢谢。所以多线程。正确的。有什么有效的解决方案吗?“…但在应用程序代码中有什么方法可以做到这一点吗…”当然可以,但实际上您是在做DB设计的工作,不管它是表约束还是存储过程。在代码中这样做只会导致在数据库中为这些列的组合设置一个唯一的约束。您将在何处创建dbcontext。两个线程都尝试查找现有连接,但都失败了,然后添加一个。您必须确保代码的这个特定部分(至少)不是并行运行的,而是顺序运行的。谢谢。所以多线程。正确的。有什么有效的解决方案吗?“…但在应用程序代码中有什么方法可以做到这一点吗…”当然可以,但实际上您是在做DB设计的工作,不管它是表约束还是存储过程。在代码中这样做只会导致噪音,至少会对数据库中这些列的组合设置一个唯一的约束。正确的。我没想到在一个网络农场会有什么后果。谢谢:)哇。正确的。我没想到在一个网络农场会有什么后果。谢谢:)