前回のロスト アップデートとその解決方法 その 1 では自動トランザクション (TransactionScope) を
利用したコードを示しましたが、今回は手動トランザクションを利用したコードを示します。
ただし、特に説明する部分はないので、ステップ 3 (Read Committed & UPDLOCK) の場合のみ示します。
TransactionScope クラスの代わりに SqlTransaction クラスを利用するだけです。
using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;
namespace LostUpdate
{
class Program
{
private const string NorthwindConnectionString = @"Data Source=.\SQLExpress;Initial Catalog=Northwind;Integrated Security=True";
private const string SelectCommandText = "select UnitsInStock from Products with (updlock) where ProductID = 1";
private const string UpdateCommandText = "update Products set UnitsInStock = @UnitsInStock where ProductID = 1";
static void Main(string[] args)
{
Parallel.For(0, 100, i => AddUnitsInStock());
}
/// <summary>
/// Northwind データベースの Products テーブルの UnitsInStock の値を 1 だけ増加させます。
/// </summary>
private static void AddUnitsInStock()
{
try
{
short oldValue, newValue;
using (var connection = new SqlConnection(NorthwindConnectionString))
{
// データベース接続を開きます。
connection.Open();
// トランザクションを開始します。
SqlTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);
// 在庫個数を取得します。
using (var command = new SqlCommand(SelectCommandText, connection) { Transaction = transaction })
{
oldValue = (short)command.ExecuteScalar();
}
// 在庫個数を 1 だけ増加させます。
newValue = (short)(oldValue + 1);
// 在庫個数を更新します。
using (var command = new SqlCommand(UpdateCommandText, connection) { Transaction = transaction })
{
command.Parameters.AddWithValue("@UnitsInStock", newValue);
command.ExecuteNonQuery();
}
// コミットします。
transaction.Commit();
}
Console.WriteLine("{0} → {1}", oldValue, newValue);
}
catch (SqlException ex)
{
Console.WriteLine(ex.Message);
}
}
}
}
バージョン情報
.NET Framework 4
SQL Server 2008, 2008 R2
2011年7月17日 15:33
[…] ロスト アップデートとその解決方法 その 2 に手動トランザクションを利用したコードを、 ロスト アップデートとその解決方法 その 3 に ADO.NET Entity Framework を利用したコードを載せました。 […]