Мне нужно проверить, существует ли запись - если да: затем возвращает ее идентификатор, если нет: создает новую запись и возвращает ее идентификатор. Я использую WITH (UPDLOCK, HOLDLOCK) в SELECT для предотвращения дублирования (это создает блокировку). Интересно, должен ли я зафиксировать транзакцию, если в базе данных существует запись для реализации блокировки?

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;                    
    }
}   
1
MrChudz 4 Июл 2021 в 12:44

3 ответа

Лучший ответ

я должен совершить транзакцию здесь?

Да. В противном случае произойдет откат после завершения блока using. В данном конкретном случае это не имеет значения, но лучше пояснить. И если бы эта транзакция была частью более крупной транзакции, все это было бы откатано.

4
David Browne - Microsoft 4 Июл 2021 в 09:49

Здесь транзакция не нужна.

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;                    
}

}

-1
KiwiPiet 4 Июл 2021 в 10:28

Что ж, вам не нужно начинать транзакцию во время запроса. Вы можете переписать свой код, как показано ниже:

    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;
    } 
-2
marc_s 4 Июл 2021 в 12:11