【问题标题】:Returning a value before committing transaction and (UPDLOCK, HOLDLOCK)在提交事务和 (UPDLOCK, HOLDLOCK) 之前返回一个值
【发布时间】:2021-07-04 09:44:02
【问题描述】:

我需要检查一条记录是否存在 - 如果是:则返回其 ID,如果不存在:创建一个新记录并返回其 ID。我在SELECT 中使用WITH (UPDLOCK, HOLDLOCK) 来防止重复(它会创建锁)。我想知道如果数据库中存在用于实现锁定的记录,我是否应该提交事务?

using (SqlConnection connection = new SqlConnection("..."))
{
    await connection.OpenAsync();
    using (var transaction = connection.BeginTransaction())
    {
        var locationId = await connection.QueryFirstOrDefaultAsync<int?>(
                "SELECT id 
                 FROM Locations WITH (UPDLOCK, HOLDLOCK)
                 WHERE regionId = @RegionId", new { RegionId = 1 }, transaction: transaction
            );

        if (locationId.HasValue)
        {
            //transaction.Commit(); // should I commit the transaction here?
            return locationId.Value;
        }

        var location = new Location()
        {
            Name = "test",
            RegionId = 1
        };

        var newLocationid = await connection.InsertAsync<int>(location, transaction);

        transaction.Commit();

        return newLocationid;                    
    }
}   

【问题讨论】:

  • @Charlieface without (UPDLOCK, HOLDLOCK) 我的数据库中有重复项,因为我在 regionId 列上没有唯一索引。
  • 也许可以移除锁定提示,只使用唯一约束来防止重复?
  • @Stu 删除重复项并不是那么简单:(

标签: c# sql sql-server transactions


【解决方案1】:

我应该在这里提交交易吗?

是的。否则它将在 using 块完成时回滚。在这种特殊情况下,这无关紧要,但最好是明确的。如果这个事务是一个更大事务的一部分,那么整个事务就会被回滚。

【讨论】:

    【解决方案2】:

    这里不需要交易。

    using (SqlConnection connection = new SqlConnection("..."))
    {
        await connection.OpenAsync();
        /* The lock hints you had here makes no sense. */
        var locationId = await connection.QueryFirstOrDefaultAsync<int?>(
                "SELECT id 
                 FROM Locations
                 WHERE regionId = @RegionId", new { RegionId = 1 }
            );
    
        if (locationId.HasValue)
        {
            return locationId.Value;
        }
    
        var location = new Location()
        {
            Name = "test",
            RegionId = 1
        };
    
        /* INSERT has an implicit transaction 
           only need to use a transaction if you have multiple DML statements (i.e. INSERT, UPDATE or DELETE statments) */
        var newLocationid = await connection.InsertAsync<int>(location);
        return newLocationid;                    
    }
    

    }

    【讨论】:

    • without (UPDLOCK, HOLDLOCK) 我的数据库中有重复项,因为我在 regionId 列上没有唯一索引。
    【解决方案3】:

    您不必在查询时开始事务。你可以重写你的代码如下:

        using (SqlConnection connection = new SqlConnection("..."))
        {
            await connection.OpenAsync();
            var locationId = await connection.QueryFirstOrDefaultAsync<int?>(
                        "SELECT id 
                         FROM Locations WITH (UPDLOCK, HOLDLOCK)
                         WHERE regionId = @RegionId", new { RegionId = 1 });
        
            if (locationId.HasValue)
            {
                return locationId.Value;
            }
        
            using (var transaction = connection.BeginTransaction())
            {
                var location = new Location()
                {
                    Name = "test",
                    RegionId = 1
                };
        
                var newLocationid = await connection.InsertAsync<int>(location, transaction);
        
                transaction.Commit();
            }
    
            return newLocationid;
        } 
    

    【讨论】:

    • WITH (UPDLOCK, HOLDLOCK) 需要一个事务
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-10
    • 2011-10-08
    • 2019-06-24
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多