前提・実現したいこと
Entity Framework Core の勉強を兼ねて、
C# + SQLite + MSTest で DB アクセスのラーニングテストをしているんですが、
トランザクションのロールバックができない、という状況ではまってます。
ターゲットの .NET バージョンは 5.0 です。
使っている (NuGet) パッケージの問題なのか Entity Framework の使い方が悪いのか。
問題の切り分けが不十分で申し訳ありませんが、動作しない理由/原因について
心当たりのことがあればご教示ください。
使っているNuGetパッケージは下記、ターゲットフレームワークは .NET5 です。
- Microsoft.Data.Sqlite.Core, 5.0.10
- Microsoft.EntityFrameworkCore.Sqlite, 5.0.10
発生している問題・エラーメッセージ
テストの期待動作は下記。3回目のコンソール書き込み内容が3、というのが問題点です。
- 初期状態:DBにテーブルはあるが、エンティティは登録されていない
- 1回め:新規エンティティ作成、1をテーブルに書き込んで1を返す
- 2回目:テーブルから1を読み込み、+1した値2を書き込んで2を返す
- transaction.Rollback() により書き込んだ値が1に戻るはず
- 3回目:テーブルから1を読み込み、+1した2を書き込んで2を返すはず
⇔実際にはテーブルから読み取った値が2、+1した3を書き込んで3が返ってくる
該当のソースコード
.NET5 のコンソールアプリを作成し、Mainでテストコードを実行しています。
見通しが悪いのでコードを分割しました。
テストコード:
C#
1static void Main(string[] args) 2{ 3 // test.db 作成済、SEQUENCE も作成済み:CREATE TABLE SEQUENCES ( NAME TEXT NOT NULL PRIMARY KEY, VALUE INT ) 4 var testDB = new SqliteConnection("Data Source=./test.db;Mode=ReadWrite"); 5 testDB.Open(); 6 using (var cmd = testDB.CreateCommand()) 7 { 8 cmd.CommandText = "DELETE FROM SEQUENCES"; 9 cmd.ExecuteNonQuery(); 10 } 11 12 var seqName = "SEQ1"; 13 using (var context = new TestDBContext(testDB)) 14 { 15 var seq1 = new SequenceValue() { Name = seqName, Value = 1 }; 16 context.Sequences.Add(seq1); 17 context.SaveChanges(); 18 // OK: 1 が出力される 19 Console.WriteLine(seq1.Value); 20 21 using (var transaction = context.Database.BeginTransaction()) 22 { 23 var seq2 = context.Sequences.Find(seqName); 24 seq2.Value++; 25 context.SaveChanges(); 26 // OK: 2 が出力される 27 Console.WriteLine(seq2.Value); 28 transaction.Rollback(); 29 } 30 31 var seq3 = context.Sequences.Find(seqName); 32 seq3.Value++; 33 context.SaveChanges(); 34 35 // NG:ローバックされているので 2 が出力されるはず:実際には3が出力される 36 Console.WriteLine(seq3.Value); 37 } 38 39 testDB.Close(); 40}
SequenceValueクラス
DB上の SEQUENCES テーブルに対応する、Entity Framework のエンティティクラス
C#
1[Table("SEQUENCES")] 2class SequenceValue 3{ 4 [Key, Column("NAME")] public string Name { get; set; } 5 [Column("VALUE")] public int Value { get; set; } 6}
TestContext クラス
C#
1public class TestDBContext : DbContext 2{ 3 private SqliteConnection _conn; 4 public TestDBContext(SqliteConnection conn) { _conn = conn; } 5 protected override void OnConfiguring(DbContextOptionsBuilder builder) 6 { 7 builder.UseSqlite(_conn); 8 } 9 public DbSet<SequenceValue> Sequences { get; set; } 10}
試したこと
以下試しましたが、いずれもテスト結果は変わりません。
(3回目の出力呼出が3:2を出力してほしい。)
- context.SaveChanges() を呼び出さない
- インクリメント(seqValue.Value++) の後で context.Sequences.Update(seqValue) 実施
transaction.Rollback() を transaction.Commit() に変えると3回目のNext()が3を返すのは意図通りの振る舞いなんですが、Rollback() しても3回目のNext()が3を返すのは不思議です。
また context.Dtabase 経由で SQL を直接実行したときは transaction.Rollback()
が利くことを確認しています:
C#
1using (var context = new TestDBContext(testDB)) 2{ 3 context.Database.ExecuteSqlRaw("INSERT INTO SEQUENCES VALUES ('SEQ1', 1)"); 4 using(var transaction = context.Database.BeginTransaction()) 5 { 6 context.Database.ExecuteSqlRaw("UPDATE SEQUENCES SET VALUE = 2 WHERE NAME='SEQ1'"); 7 transaction.Rollback(); 8 } 9 using (var cmd3 = context.Database.GetDbConnection().CreateCommand()) 10 { 11 cmd3.CommandText = "SELECT VALUE FROM SEQUENCES WHERE NAME='SEQ1'"; 12 var reader = cmd3.ExecuteReader(); 13 reader.Read(); 14 // コンソールには1が出力される:意図通りロールバックできている 15 Console.WriteLine(reader.GetInt32(0)); 16 } 17}
補足情報(FW/ツールのバージョンなど)
Visual Studio 2019、
MSTest を使う単体テストプロジェクトを作成。
コンソールアプリケーションを作成。
ターゲットフレームワークは「.NET 5.0 (現在)」です。
回答3件
あなたの回答
tips
プレビュー