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

回答編集履歴

3

追記&訂正

2021/09/18 01:05

投稿

退会済みユーザー
answer CHANGED
@@ -29,7 +29,7 @@
29
29
 
30
30
  テストコードを見直して、上に紹介した記事の「トランザクションを制御する」のサンプルコードのようにして、例えば 2 つ目の SaveChanges で失敗するように細工するとロールバックされて、一回目の SaveChanges は無かったことになるはずです。
31
31
 
32
- **【追記】**
32
+ **【2021/9/17 追記】**
33
33
 
34
34
  下のコメント欄で「やってみましたけど、最終結果は 3 ですね。ロールバックはされないようです。後で回答欄に詳細を書いておきます」と書いた件です。
35
35
 
@@ -110,4 +110,4 @@
110
110
 
111
111
  ![イメージ説明](ac835097f610d79d316a5208aebe9b72.jpeg)
112
112
 
113
- ロールバックはされてないという結果です。
113
+ ~~ロールバックはされてないという結果です。~~ ← 間違ってました。上の【2021/9/18 訂正&追記】を見てください。ロールバックはされているのですが、キャッシュの問題で最後に 3 に UPDATE されたというのが上の結果です。

2

訂正&追記

2021/09/18 01:05

投稿

退会済みユーザー
answer CHANGED
@@ -9,8 +9,24 @@
9
9
 
10
10
  "既定では、データベース プロバイダーがトランザクションをサポートしている場合は、SaveChanges への 1 回の呼び出しに含まれるすべての変更がトランザクションに適用されます。 いずれかの変更が失敗した場合、トランザクションはロールバックされ、変更は、データベースにまったく適用されません。 つまり、SaveChanges は、完全に成功するか、エラーが発生した場合はデータベースを未変更のままにすることが保証されます。"
11
11
 
12
- 質問者さんのコードで 3 つある context.SaveChanges() はすべて「完全に成功」していて、ロールバックは効かないはずです。
12
+ 質問者さんのコードで 3 つある context.SaveChanges() はすべて「完全に成功」していて、~~ロールバックは効かないはずです。~~
13
13
 
14
+ **【2021/9/18 訂正&追記】ここから**
15
+
16
+ 間違ってました。context.SaveChanges() はすべて「完全に成功」していても、2 つ目の SaveChanges を囲っている transaction は SaveChanges でコミットされない(保留中の状態にある)ので transaction.Rollback(); でロールバックされるようです。
17
+
18
+ コンテキストのキャッシュの問題でした。質問のコードおよび下の【追記】のコードの、
19
+
20
+ context.Sequences.Add(seq1); 
21
+
22
+ でキャッシュされ、その後 2 箇所ある、
23
+
24
+ context.Sequences.Find(seqName)
25
+
26
+ ではキャッシュから取得される。ロールバックはキャッシュに反映されないが、コードの Value++ の結果の値(1 回目は 2, 2回目は 3)はキャッシュに反映され Modified マークがついて、SaveChanges によって最終的に 3 に UPDATE されたということでした。
27
+
28
+ **【2021/9/18 訂正&追記】ここまで**
29
+
14
30
  テストコードを見直して、上に紹介した記事の「トランザクションを制御する」のサンプルコードのようにして、例えば 2 つ目の SaveChanges で失敗するように細工するとロールバックされて、一回目の SaveChanges は無かったことになるはずです。
15
31
 
16
32
  **【追記】**

1

追記

2021/09/18 00:55

投稿

退会済みユーザー
answer CHANGED
@@ -11,4 +11,87 @@
11
11
 
12
12
  質問者さんのコードで 3 つある context.SaveChanges() はすべて「完全に成功」していて、ロールバックは効かないはずです。
13
13
 
14
- テストコードを見直して、上に紹介した記事の「トランザクションを制御する」のサンプルコードのようにして、例えば 2 つ目の SaveChanges で失敗するように細工するとロールバックされて、一回目の SaveChanges は無かったことになるはずです。
14
+ テストコードを見直して、上に紹介した記事の「トランザクションを制御する」のサンプルコードのようにして、例えば 2 つ目の SaveChanges で失敗するように細工するとロールバックされて、一回目の SaveChanges は無かったことになるはずです。
15
+
16
+ **【追記】**
17
+
18
+ 下のコメント欄で「やってみましたけど、最終結果は 3 ですね。ロールバックはされないようです。後で回答欄に詳細を書いておきます」と書いた件です。
19
+
20
+ コードは以下の通りで質問者さんのものと同じです。EF Code First でプロジェクトのフォルダに DB を生成したのでその関係がほんのちょっと異なるぐらいです。Visual Studio 2019 のテンプレートで作った .NET 5.0 のコンソールアプリです。
21
+
22
+ ```
23
+ using System;
24
+ using System.ComponentModel.DataAnnotations;
25
+ using System.ComponentModel.DataAnnotations.Schema;
26
+ using Microsoft.EntityFrameworkCore;
27
+ using Microsoft.Data.Sqlite;
28
+
29
+ namespace ConsoleAppSQLite
30
+ {
31
+ class Program
32
+ {
33
+ static void Main(string[] args)
34
+ {
35
+ var seqName = "SEQ1";
36
+ using (var context = new TestDBContext())
37
+ {
38
+ var seq1 = new SequenceValue() { Name = seqName, Value = 1 };
39
+ context.Sequences.Add(seq1);
40
+ context.SaveChanges();
41
+ // OK: 1 が出力される
42
+ Console.WriteLine(seq1.Value);
43
+
44
+ using (var transaction = context.Database.BeginTransaction())
45
+ {
46
+ var seq2 = context.Sequences.Find(seqName);
47
+ seq2.Value++;
48
+ context.SaveChanges();
49
+ // OK: 2 が出力される
50
+ Console.WriteLine(seq2.Value);
51
+ transaction.Rollback();
52
+ }
53
+
54
+ var seq3 = context.Sequences.Find(seqName);
55
+ seq3.Value++;
56
+ context.SaveChanges();
57
+
58
+ // NG:ローバックされているので 2 が出力されるはず:実際には3が出力される
59
+ Console.WriteLine(seq3.Value);
60
+ }
61
+ }
62
+ }
63
+
64
+
65
+ [Table("SEQUENCES")]
66
+ public class SequenceValue
67
+ {
68
+ [Key, Column("NAME")] public string Name { get; set; }
69
+ [Column("VALUE")] public int Value { get; set; }
70
+ }
71
+
72
+ public class TestDBContext : DbContext
73
+ {
74
+ //private SqliteConnection _conn;
75
+ //public TestDBContext(SqliteConnection conn) { _conn = conn; }
76
+ protected override void OnConfiguring(DbContextOptionsBuilder builder)
77
+ {
78
+ var path = @"C:\Users\surfe\Documents\Visual Studio 2019\Core5App\ConsoleAppSQLite\ConsoleAppSQLite\test.db";
79
+ var connStr = "Data Source=" + path;
80
+
81
+ builder.UseSqlite(connStr);
82
+
83
+ }
84
+ public DbSet<SequenceValue> Sequences { get; set; }
85
+ }
86
+ }
87
+ ```
88
+
89
+ 実行結果は以下の通り(質問者さんと同じ):
90
+
91
+ ![イメージ説明](6f2d027ea9708472bdcd4c1350240323.jpeg)
92
+
93
+ DB Browser for SQLite で結果を見ると 3 です(実行する前は NAME, VALUE ともカラです):
94
+
95
+ ![イメージ説明](ac835097f610d79d316a5208aebe9b72.jpeg)
96
+
97
+ ロールバックはされてないという結果です。