前回のファントム リードとその解決方法 その 1 では SqlCommand クラスを利用したコードを示しましたが、
今回は ADO.NET Entity Framework を利用したコードを示します。
ただし、特に説明する部分はないので、ステップ 2 (Serializable) の場合のみ示します。
なお、下記のコードを記述する前に、ロスト アップデートとその解決方法 その 3 と同様に
Visual Studio で ADO.NET Entity Data Model を作成しておきます。
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Transactions;
namespace PhantomRead
{
class Program
{
private static readonly TransactionOptions transactionOptions = new TransactionOptions { IsolationLevel = IsolationLevel.Serializable };
private static int phantomCount;
static void Main(string[] args)
{
// 挿入と読み取りを並列に実行します。
Parallel.Invoke(
() =>
{
for (int i = 0; i < 200; i++)
{
InsertCategory();
}
},
() =>
{
for (int i = 0; i < 100; i++)
{
SelectCategoriesCount();
}
}
);
}
/// <summary>
/// Northwind データベースの Categories テーブルの行を挿入します。
/// </summary>
private static void InsertCategory()
{
using (var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
using (var context = new NorthwindEntities())
{
// カテゴリを追加します。
context.AddToCategories(Category.CreateCategory(0, "New Category"));
context.SaveChanges();
// コミットします。
scope.Complete();
}
}
/// <summary>
/// Northwind データベースの Categories テーブルの行数を読み取ります。
/// </summary>
private static void SelectCategoriesCount()
{
using (var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
using (var context = new NorthwindEntities())
{
// カテゴリの数を読み取ります。
int count1 = context.Categories.Count();
int count2 = context.Categories.Count();
if (count1 != count2)
{
Console.WriteLine("{0} = {1} ({2} 回目)", count1, count2, ++phantomCount);
}
else
{
Console.WriteLine("{0} = {1}", count1, count2);
}
}
}
}
}
注意点
(1) CategoryID 列には IDENTITY が指定されているため、
新しい Category オブジェクトを作成するときに指定する CategoryID は任意の値でかまいません (上記では 0)。
SaveChanges メソッドが呼ばれると、テーブルに行を挿入するとともに、採番された CategoryID を取得します。
具体的には、次と等価な SQL が送信されます。
insert [Categories] ([CategoryName], [Description], [Picture]) values (N’New Category’, null, null)
select [CategoryID] from [Categories] where @@ROWCOUNT > 0 and [CategoryID] = scope_identity()
バージョン情報
.NET Framework 4
SQL Server 2008, 2008 R2