質問するログイン新規登録

質問編集履歴

2

NuGetパッケージとターゲットが.NET5であることを前提に追記

2021/09/17 03:03

投稿

Yas.T
Yas.T

スコア6

title CHANGED
File without changes
body CHANGED
@@ -8,13 +8,17 @@
8
8
  問題の切り分けが不十分で申し訳ありませんが、動作しない理由/原因について
9
9
  心当たりのことがあればご教示ください。
10
10
 
11
+ 使っているNuGetパッケージは下記、ターゲットフレームワークは .NET5 です。
12
+  - Microsoft.Data.Sqlite.Core, 5.0.10
13
+  - Microsoft.EntityFrameworkCore.Sqlite, 5.0.10
14
+
11
15
  ### 発生している問題・エラーメッセージ
12
- テストの期待動作は下記。3回目のNext()呼出で3返ってくるのが問題点です。 
16
+ テストの期待動作は下記。3回目のコンソール書き込み内容3、というのが問題点です。
13
17
  - 初期状態:DBにテーブルはあるが、エンティティは登録されていない
14
-  - Next(1回め):新規エンティティ作成、1をテーブルに書き込んで1を返す
18
+  - 1回め:新規エンティティ作成、1をテーブルに書き込んで1を返す
15
-  - Next(2回目):テーブルから1を読み込み、+1した値2を書き込んで2を返す
19
+  - 2回目:テーブルから1を読み込み、+1した値2を書き込んで2を返す
16
20
  - transaction.Rollback() により書き込んだ値が1に戻るはず
17
-  - Next(3回目):テーブルから1を読み込み、+1した2を書き込んで2を返すはず
21
+  - 3回目:テーブルから1を読み込み、+1した2を書き込んで2を返すはず
18
22
    ⇔実際にはテーブルから読み取った値が2、+1した3を書き込んで3が返ってくる
19
23
 
20
24
  ### 該当のソースコード
@@ -125,7 +129,4 @@
125
129
  Visual Studio 2019、
126
130
  ~~MSTest を使う単体テストプロジェクトを作成。~~
127
131
  コンソールアプリケーションを作成。
128
- ターゲットフレームワークは「.NET 5.0 (現在)」です。
132
+ ターゲットフレームワークは「.NET 5.0 (現在)」です。
129
- また NuGet で以下のパッケージを入手しました。
130
-  - Microsoft.Data.Sqlite.Core, 5.0.10
131
-  - Microsoft.EntityFrameworkCore.Sqlite, 5.0.10

1

サンプルコード見直し、SQL直呼びだとトランザクションがロールバックできることを追記

2021/09/17 03:03

投稿

Yas.T
Yas.T

スコア6

title CHANGED
File without changes
body CHANGED
@@ -1,8 +1,6 @@
1
1
  ### 前提・実現したいこと
2
-
3
-
4
2
  Entity Framework Core の勉強を兼ねて、
5
- C# + SQLite + MSTest で DB アクセスのラーニングテストをしているんですが、
3
+ C# + SQLite ~~+ MSTest~~ で DB アクセスのラーニングテストをしているんですが、
6
4
  トランザクションのロールバックができない、という状況ではまってます。
7
5
  ターゲットの .NET バージョンは 5.0 です。
8
6
 
@@ -11,97 +9,123 @@
11
9
  心当たりのことがあればご教示ください。
12
10
 
13
11
  ### 発生している問題・エラーメッセージ
14
- 下記コードで、3つめのAssertionが失敗する:テスト実行結果がRED
15
- テスト対象簡単な「DBシーケンスオブジェクトもどき」
12
+ テストの期待動作下記。3回目Next()呼出3が返ってくるのが問題点です。 
16
- テストの待動作下記。
13
+ - 初状態:DBにテーブルあるが、エンティティは登録されていない
17
14
   - Next(1回め):新規エンティティ作成、1をテーブルに書き込んで1を返す
18
15
   - Next(2回目):テーブルから1を読み込み、+1した値2を書き込んで2を返す
19
- - transaction.Rollback() により書き込んだ値が1に戻る
16
+ - transaction.Rollback() により書き込んだ値が1に戻るはず
20
17
   - Next(3回目):テーブルから1を読み込み、+1した2を書き込んで2を返すはず
21
18
    ⇔実際にはテーブルから読み取った値が2、+1した3を書き込んで3が返ってくる
22
19
 
23
20
  ### 該当のソースコード
24
- とりあえず1セット。適宜NuGetパッケジをそろえればビドが通るはずです。
21
+ .NET5 のコンソールアプリを作成しMainテストコードを実行しています。
22
+ 見通しが悪いのでコードを分割しました。
23
+
24
+ #### テストコード:
25
25
  ``` C#
26
- using Microsoft.Data.Sqlite; // Microsoft.Data.Sqlite.Core, 5.0.10
27
- using Microsoft.EntityFrameworkCore; // Microsoft.EntityFrameworkCore.Sqlite, 5.0.10
28
- using Microsoft.VisualStudio.TestTools.UnitTesting; // MSTest.TestFramework, 2.2.7
29
-
30
- using System.ComponentModel.DataAnnotations;
31
- using System.ComponentModel.DataAnnotations.Schema;
32
-
33
- namespace Project1 {
34
- [TestClass] public class FooTest {
35
- private SqliteConnection InMemoryDB { get; set; }
36
- [Table("SEQUENCES")] private class SequenceValue {
37
- [Key,Column("NAME")] public string Name { get; set; }
38
- [Column("VALUE")] public int Value { get; set; }
39
- }
40
- private class TestDBContext : DbContext {
41
- private SqliteConnection _conn;
26
+ static void Main(string[] args)
42
- public TestDBContext(SqliteConnection conn) { _conn = conn; }
43
- protected override void OnConfiguring(DbContextOptionsBuilder builder) {
44
- builder.UseSqlite(_conn);
45
- }
46
- public DbSet<SequenceValue> Sequences { get; set; }
47
- }
48
- [TestInitialize] public void Setup() {
49
- InMemoryDB = new SqliteConnection("Data Source=:memory:");
50
- InMemoryDB.Open();
51
- using (var cmd = InMemoryDB.CreateCommand())
52
- {
27
+ {
53
- cmd.CommandText = "CREATE TABLE SEQUENCES ( NAME TEXT NOT NULL PRIMARY KEY, VALUE INT )";
28
+ // test.db 作成済、SEQUENCE も作成済み:CREATE TABLE SEQUENCES ( NAME TEXT NOT NULL PRIMARY KEY, VALUE INT )
29
+ var testDB = new SqliteConnection("Data Source=./test.db;Mode=ReadWrite");
30
+ testDB.Open();
31
+ using (var cmd = testDB.CreateCommand())
32
+ {
33
+ cmd.CommandText = "DELETE FROM SEQUENCES";
54
34
  cmd.ExecuteNonQuery();
55
- }
56
35
  }
57
- [TestCleanup] public void Cleanup() {
58
- InMemoryDB.Close();
59
- }
60
36
 
61
- private int Next(TestDBContext context) {
62
- var seqName = "SEQ1";
37
+ var seqName = "SEQ1";
63
- var seqValue = context.Sequences.Find(seqName);
38
+ using (var context = new TestDBContext(testDB))
64
- if (seqValue == null) {
39
+ {
65
- seqValue = new SequenceValue() { Name = seqName, Value = 1 };
40
+ var seq1 = new SequenceValue() { Name = seqName, Value = 1 };
66
- context.Sequences.Add(seqValue);
41
+ context.Sequences.Add(seq1);
67
42
  context.SaveChanges();
68
- } else {
43
+ // OK: 1 が出力される
44
+ Console.WriteLine(seq1.Value);
45
+
46
+ using (var transaction = context.Database.BeginTransaction())
47
+ {
48
+ var seq2 = context.Sequences.Find(seqName);
69
- seqValue.Value++;
49
+ seq2.Value++;
50
+ context.SaveChanges();
51
+ // OK: 2 が出力される
52
+ Console.WriteLine(seq2.Value);
53
+ transaction.Rollback();
54
+ }
55
+
56
+ var seq3 = context.Sequences.Find(seqName);
57
+ seq3.Value++;
70
58
  context.SaveChanges();
59
+
71
- }
60
+ // NG:ローバックされているので 2 が出力されるはず:実際には3が出力される
72
- return seqValue.Value;
61
+ Console.WriteLine(seq3.Value);
73
62
  }
74
63
 
75
- [TestMethod] public void TestMethod1() {
64
+ testDB.Close();
76
- using (var context = new TestDBContext(InMemoryDB)) {
65
+ }
77
- Assert.AreEqual(1, Next(context)); // OK.
66
+ ```
78
- using (var transaction = context.Database.BeginTransaction()) {
79
- Assert.AreEqual(2, Next(context)); // OK.
80
67
 
68
+ #### SequenceValueクラス
69
+ DB上の SEQUENCES テーブルに対応する、Entity Framework のエンティティクラス
70
+ ```C#
71
+ [Table("SEQUENCES")]
72
+ class SequenceValue
73
+ {
81
- // Expected: current value in the table is rolled back, from 2 to 1...
74
+ [Key, Column("NAME")] public string Name { get; set; }
82
- transaction.Rollback();
75
+ [Column("VALUE")] public int Value { get; set; }
83
- }
76
+ }
77
+ ```
78
+
79
+ #### TestContext クラス
80
+ ```C#
81
+ public class TestDBContext : DbContext
82
+ {
83
+ private SqliteConnection _conn;
84
- Assert.AreEqual(2, Next(context), "?? Why Failed ??"); // NG! expected 2, actual 3....
84
+ public TestDBContext(SqliteConnection conn) { _conn = conn; }
85
+ protected override void OnConfiguring(DbContextOptionsBuilder builder)
85
- }
86
+ {
87
+ builder.UseSqlite(_conn);
86
88
  }
87
-
88
- }
89
+ public DbSet<SequenceValue> Sequences { get; set; }
89
90
  }
90
91
  ```
91
92
 
92
93
  ### 試したこと
93
94
  以下試しましたが、いずれもテスト結果は変わりません。
94
- (3回目のNext()呼出が3を返す:2をしてほしい。)
95
+ (3回目の出力呼出が3:2を出力してほしい。)
95
96
 
96
- - context.SaveChanges() をNext()の外で呼び出す
97
97
  - context.SaveChanges() を呼び出さない
98
98
  - インクリメント(seqValue.Value++) の後で context.Sequences.Update(seqValue) 実施
99
99
 
100
100
  transaction.Rollback() を transaction.Commit() に変えると3回目のNext()が3を返すのは意図通りの振る舞いなんですが、Rollback() しても3回目のNext()が3を返すのは不思議です。
101
101
 
102
+ また context.Dtabase 経由で SQL を直接実行したときは transaction.Rollback()
103
+ が利くことを確認しています:
104
+ ```C#
105
+ using (var context = new TestDBContext(testDB))
106
+ {
107
+ context.Database.ExecuteSqlRaw("INSERT INTO SEQUENCES VALUES ('SEQ1', 1)");
108
+ using(var transaction = context.Database.BeginTransaction())
109
+ {
110
+ context.Database.ExecuteSqlRaw("UPDATE SEQUENCES SET VALUE = 2 WHERE NAME='SEQ1'");
111
+ transaction.Rollback();
112
+ }
113
+ using (var cmd3 = context.Database.GetDbConnection().CreateCommand())
114
+ {
115
+ cmd3.CommandText = "SELECT VALUE FROM SEQUENCES WHERE NAME='SEQ1'";
116
+ var reader = cmd3.ExecuteReader();
117
+ reader.Read();
118
+ // コンソールには1が出力される:意図通りロールバックできている
119
+ Console.WriteLine(reader.GetInt32(0));
120
+ }
121
+ }
122
+ ```
123
+
102
124
  ### 補足情報(FW/ツールのバージョンなど)
103
125
  Visual Studio 2019、
104
- MSTest を使う単体テストプロジェクトを作成。
126
+ ~~MSTest を使う単体テストプロジェクトを作成。~~
127
+ コンソールアプリケーションを作成。
128
+ ターゲットフレームワークは「.NET 5.0 (現在)」です。
105
129
  また NuGet で以下のパッケージを入手しました。
106
130
   - Microsoft.Data.Sqlite.Core, 5.0.10
107
131
   - Microsoft.EntityFrameworkCore.Sqlite, 5.0.10