質問編集履歴

2

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

2021/09/17 03:03

投稿

Yas.T
Yas.T

スコア6

test CHANGED
File without changes
test CHANGED
@@ -18,19 +18,27 @@
18
18
 
19
19
 
20
20
 
21
+ 使っているNuGetパッケージは下記、ターゲットフレームワークは .NET5 です。
22
+
23
+  - Microsoft.Data.Sqlite.Core, 5.0.10
24
+
25
+  - Microsoft.EntityFrameworkCore.Sqlite, 5.0.10
26
+
27
+
28
+
21
29
  ### 発生している問題・エラーメッセージ
22
30
 
23
- テストの期待動作は下記。3回目のNext()呼出で3が返ってくるのが問題点です。 
31
+ テストの期待動作は下記。3回目のコンソール書き込み内容が3、というのが問題点です。
24
32
 
25
33
  - 初期状態:DBにテーブルはあるが、エンティティは登録されていない
26
34
 
27
-  - Next(1回め):新規エンティティ作成、1をテーブルに書き込んで1を返す
35
+  - 1回め:新規エンティティ作成、1をテーブルに書き込んで1を返す
28
-
36
+
29
-  - Next(2回目):テーブルから1を読み込み、+1した値2を書き込んで2を返す
37
+  - 2回目:テーブルから1を読み込み、+1した値2を書き込んで2を返す
30
38
 
31
39
  - transaction.Rollback() により書き込んだ値が1に戻るはず
32
40
 
33
-  - Next(3回目):テーブルから1を読み込み、+1した2を書き込んで2を返すはず
41
+  - 3回目:テーブルから1を読み込み、+1した2を書き込んで2を返すはず
34
42
 
35
43
    ⇔実際にはテーブルから読み取った値が2、+1した3を書き込んで3が返ってくる
36
44
 
@@ -253,9 +261,3 @@
253
261
  コンソールアプリケーションを作成。
254
262
 
255
263
  ターゲットフレームワークは「.NET 5.0 (現在)」です。
256
-
257
- また NuGet で以下のパッケージを入手しました。
258
-
259
-  - Microsoft.Data.Sqlite.Core, 5.0.10
260
-
261
-  - Microsoft.EntityFrameworkCore.Sqlite, 5.0.10

1

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

2021/09/17 03:03

投稿

Yas.T
Yas.T

スコア6

test CHANGED
File without changes
test CHANGED
@@ -1,12 +1,8 @@
1
1
  ### 前提・実現したいこと
2
2
 
3
-
4
-
5
-
6
-
7
3
  Entity Framework Core の勉強を兼ねて、
8
4
 
9
- C# + SQLite + MSTest で DB アクセスのラーニングテストをしているんですが、
5
+ C# + SQLite ~~+ MSTest~~ で DB アクセスのラーニングテストをしているんですが、
10
6
 
11
7
  トランザクションのロールバックができない、という状況ではまってます。
12
8
 
@@ -24,17 +20,15 @@
24
20
 
25
21
  ### 発生している問題・エラーメッセージ
26
22
 
27
- 下記コードで、3つめのAssertionが失敗する:テスト実行結果がRED
28
-
29
- テスト対象簡単な「DBシーケンスオブジェクトもどき」
23
+ テストの期待動作下記。3回目Next()呼出3が返ってくるのが問題点です。 
30
-
24
+
31
- ストの期待動作下記。
25
+ - 初期状態:DBにーブルあるが、エンティティは登録されていない
32
26
 
33
27
   - Next(1回め):新規エンティティ作成、1をテーブルに書き込んで1を返す
34
28
 
35
29
   - Next(2回目):テーブルから1を読み込み、+1した値2を書き込んで2を返す
36
30
 
37
- - transaction.Rollback() により書き込んだ値が1に戻る
31
+ - transaction.Rollback() により書き込んだ値が1に戻るはず
38
32
 
39
33
   - Next(3回目):テーブルから1を読み込み、+1した2を書き込んで2を返すはず
40
34
 
@@ -44,135 +38,143 @@
44
38
 
45
39
  ### 該当のソースコード
46
40
 
47
- とりあえず1セット。適宜NuGetパッケそろえればビルドが通るはずです。
41
+ .NET5 のコンソルアプリ作成しMainテストコードを実行しています。
42
+
43
+ 見通しが悪いのでコードを分割しました。
44
+
45
+
46
+
47
+ #### テストコード:
48
48
 
49
49
  ``` C#
50
50
 
51
- using Microsoft.Data.Sqlite; // Microsoft.Data.Sqlite.Core, 5.0.10
52
-
53
- using Microsoft.EntityFrameworkCore; // Microsoft.EntityFrameworkCore.Sqlite, 5.0.10
54
-
55
- using Microsoft.VisualStudio.TestTools.UnitTesting; // MSTest.TestFramework, 2.2.7
56
-
57
-
58
-
59
- using System.ComponentModel.DataAnnotations;
60
-
61
- using System.ComponentModel.DataAnnotations.Schema;
62
-
63
-
64
-
65
- namespace Project1 {
66
-
67
- [TestClass] public class FooTest {
68
-
69
- private SqliteConnection InMemoryDB { get; set; }
70
-
71
- [Table("SEQUENCES")] private class SequenceValue {
72
-
73
- [Key,Column("NAME")] public string Name { get; set; }
74
-
75
- [Column("VALUE")] public int Value { get; set; }
76
-
77
- }
78
-
79
- private class TestDBContext : DbContext {
80
-
81
- private SqliteConnection _conn;
82
-
83
- public TestDBContext(SqliteConnection conn) { _conn = conn; }
84
-
85
- protected override void OnConfiguring(DbContextOptionsBuilder builder) {
51
+ static void Main(string[] args)
52
+
53
+ {
54
+
55
+ // test.db 作成済、SEQUENCE も作成済み:CREATE TABLE SEQUENCES ( NAME TEXT NOT NULL PRIMARY KEY, VALUE INT )
56
+
57
+ var testDB = new SqliteConnection("Data Source=./test.db;Mode=ReadWrite");
58
+
59
+ testDB.Open();
60
+
61
+ using (var cmd = testDB.CreateCommand())
62
+
63
+ {
64
+
65
+ cmd.CommandText = "DELETE FROM SEQUENCES";
66
+
67
+ cmd.ExecuteNonQuery();
68
+
69
+ }
70
+
71
+
72
+
73
+ var seqName = "SEQ1";
74
+
75
+ using (var context = new TestDBContext(testDB))
76
+
77
+ {
78
+
79
+ var seq1 = new SequenceValue() { Name = seqName, Value = 1 };
80
+
81
+ context.Sequences.Add(seq1);
82
+
83
+ context.SaveChanges();
84
+
85
+ // OK: 1 が出力される
86
+
87
+ Console.WriteLine(seq1.Value);
88
+
89
+
90
+
91
+ using (var transaction = context.Database.BeginTransaction())
92
+
93
+ {
94
+
95
+ var seq2 = context.Sequences.Find(seqName);
96
+
97
+ seq2.Value++;
98
+
99
+ context.SaveChanges();
100
+
101
+ // OK: 2 が出力される
102
+
103
+ Console.WriteLine(seq2.Value);
104
+
105
+ transaction.Rollback();
106
+
107
+ }
108
+
109
+
110
+
111
+ var seq3 = context.Sequences.Find(seqName);
112
+
113
+ seq3.Value++;
114
+
115
+ context.SaveChanges();
116
+
117
+
118
+
119
+ // NG:ローバックされているので 2 が出力されるはず:実際には3が出力される
120
+
121
+ Console.WriteLine(seq3.Value);
122
+
123
+ }
124
+
125
+
126
+
127
+ testDB.Close();
128
+
129
+ }
130
+
131
+ ```
132
+
133
+
134
+
135
+ #### SequenceValueクラス
136
+
137
+ DB上の SEQUENCES テーブルに対応する、Entity Framework のエンティティクラス
138
+
139
+ ```C#
140
+
141
+ [Table("SEQUENCES")]
142
+
143
+ class SequenceValue
144
+
145
+ {
146
+
147
+ [Key, Column("NAME")] public string Name { get; set; }
148
+
149
+ [Column("VALUE")] public int Value { get; set; }
150
+
151
+ }
152
+
153
+ ```
154
+
155
+
156
+
157
+ #### TestContext クラス
158
+
159
+ ```C#
160
+
161
+ public class TestDBContext : DbContext
162
+
163
+ {
164
+
165
+ private SqliteConnection _conn;
166
+
167
+ public TestDBContext(SqliteConnection conn) { _conn = conn; }
168
+
169
+ protected override void OnConfiguring(DbContextOptionsBuilder builder)
170
+
171
+ {
86
172
 
87
173
  builder.UseSqlite(_conn);
88
174
 
89
- }
175
+ }
90
-
176
+
91
- public DbSet<SequenceValue> Sequences { get; set; }
177
+ public DbSet<SequenceValue> Sequences { get; set; }
92
-
93
- }
94
-
95
- [TestInitialize] public void Setup() {
96
-
97
- InMemoryDB = new SqliteConnection("Data Source=:memory:");
98
-
99
- InMemoryDB.Open();
100
-
101
- using (var cmd = InMemoryDB.CreateCommand())
102
-
103
- {
104
-
105
- cmd.CommandText = "CREATE TABLE SEQUENCES ( NAME TEXT NOT NULL PRIMARY KEY, VALUE INT )";
106
-
107
- cmd.ExecuteNonQuery();
108
-
109
- }
110
-
111
- }
112
-
113
- [TestCleanup] public void Cleanup() {
114
-
115
- InMemoryDB.Close();
116
-
117
- }
118
-
119
-
120
-
121
- private int Next(TestDBContext context) {
122
-
123
- var seqName = "SEQ1";
124
-
125
- var seqValue = context.Sequences.Find(seqName);
126
-
127
- if (seqValue == null) {
128
-
129
- seqValue = new SequenceValue() { Name = seqName, Value = 1 };
130
-
131
- context.Sequences.Add(seqValue);
132
-
133
- context.SaveChanges();
134
-
135
- } else {
136
-
137
- seqValue.Value++;
138
-
139
- context.SaveChanges();
140
-
141
- }
142
-
143
- return seqValue.Value;
144
-
145
- }
146
-
147
-
148
-
149
- [TestMethod] public void TestMethod1() {
150
-
151
- using (var context = new TestDBContext(InMemoryDB)) {
152
-
153
- Assert.AreEqual(1, Next(context)); // OK.
154
-
155
- using (var transaction = context.Database.BeginTransaction()) {
156
-
157
- Assert.AreEqual(2, Next(context)); // OK.
158
-
159
-
160
-
161
- // Expected: current value in the table is rolled back, from 2 to 1...
162
-
163
- transaction.Rollback();
164
-
165
- }
166
-
167
- Assert.AreEqual(2, Next(context), "?? Why Failed ??"); // NG! expected 2, actual 3....
168
-
169
- }
170
-
171
- }
172
-
173
-
174
-
175
- }
176
178
 
177
179
  }
178
180
 
@@ -184,11 +186,9 @@
184
186
 
185
187
  以下試しましたが、いずれもテスト結果は変わりません。
186
188
 
187
- (3回目のNext()呼出が3を返す:2をしてほしい。)
189
+ (3回目の出力呼出が3:2を出力してほしい。)
188
-
189
-
190
-
191
- - context.SaveChanges() をNext()の外で呼び出す
190
+
191
+
192
192
 
193
193
  - context.SaveChanges() を呼び出さない
194
194
 
@@ -200,11 +200,59 @@
200
200
 
201
201
 
202
202
 
203
+ また context.Dtabase 経由で SQL を直接実行したときは transaction.Rollback()
204
+
205
+ が利くことを確認しています:
206
+
207
+ ```C#
208
+
209
+ using (var context = new TestDBContext(testDB))
210
+
211
+ {
212
+
213
+ context.Database.ExecuteSqlRaw("INSERT INTO SEQUENCES VALUES ('SEQ1', 1)");
214
+
215
+ using(var transaction = context.Database.BeginTransaction())
216
+
217
+ {
218
+
219
+ context.Database.ExecuteSqlRaw("UPDATE SEQUENCES SET VALUE = 2 WHERE NAME='SEQ1'");
220
+
221
+ transaction.Rollback();
222
+
223
+ }
224
+
225
+ using (var cmd3 = context.Database.GetDbConnection().CreateCommand())
226
+
227
+ {
228
+
229
+ cmd3.CommandText = "SELECT VALUE FROM SEQUENCES WHERE NAME='SEQ1'";
230
+
231
+ var reader = cmd3.ExecuteReader();
232
+
233
+ reader.Read();
234
+
235
+ // コンソールには1が出力される:意図通りロールバックできている
236
+
237
+ Console.WriteLine(reader.GetInt32(0));
238
+
239
+ }
240
+
241
+ }
242
+
243
+ ```
244
+
245
+
246
+
203
247
  ### 補足情報(FW/ツールのバージョンなど)
204
248
 
205
249
  Visual Studio 2019、
206
250
 
207
- MSTest を使う単体テストプロジェクトを作成。
251
+ ~~MSTest を使う単体テストプロジェクトを作成。~~
252
+
253
+ コンソールアプリケーションを作成。
254
+
255
+ ターゲットフレームワークは「.NET 5.0 (現在)」です。
208
256
 
209
257
  また NuGet で以下のパッケージを入手しました。
210
258