質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
87.20%
Entity Framework

Entity Frameworkは、.NET Framework 3.5より追加されたデータアクセス技術。正式名称は「ADO.NET Entity Framework」です。データベースエンジンに依存しておらず、データプロバイダの変更のみで様々なデータベースに対応できます。

.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

解決済

[C#,.NET5,EFCore5+Microsfot.Data.Sqlite] トランザクションのロールバックが意図通りに動かない

Yas.T
Yas.T

総合スコア6

Entity Framework

Entity Frameworkは、.NET Framework 3.5より追加されたデータアクセス技術。正式名称は「ADO.NET Entity Framework」です。データベースエンジンに依存しておらず、データプロバイダの変更のみで様々なデータベースに対応できます。

.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

3回答

0評価

0クリップ

696閲覧

投稿2021/09/16 09:27

編集2021/09/17 03:03

前提・実現したいこと

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#

static void Main(string[] args) { // test.db 作成済、SEQUENCE も作成済み:CREATE TABLE SEQUENCES ( NAME TEXT NOT NULL PRIMARY KEY, VALUE INT ) var testDB = new SqliteConnection("Data Source=./test.db;Mode=ReadWrite"); testDB.Open(); using (var cmd = testDB.CreateCommand()) { cmd.CommandText = "DELETE FROM SEQUENCES"; cmd.ExecuteNonQuery(); } var seqName = "SEQ1"; using (var context = new TestDBContext(testDB)) { var seq1 = new SequenceValue() { Name = seqName, Value = 1 }; context.Sequences.Add(seq1); context.SaveChanges(); // OK: 1 が出力される Console.WriteLine(seq1.Value); using (var transaction = context.Database.BeginTransaction()) { var seq2 = context.Sequences.Find(seqName); seq2.Value++; context.SaveChanges(); // OK: 2 が出力される Console.WriteLine(seq2.Value); transaction.Rollback(); } var seq3 = context.Sequences.Find(seqName); seq3.Value++; context.SaveChanges(); // NG:ローバックされているので 2 が出力されるはず:実際には3が出力される Console.WriteLine(seq3.Value); } testDB.Close(); }

SequenceValueクラス

DB上の SEQUENCES テーブルに対応する、Entity Framework のエンティティクラス

C#

[Table("SEQUENCES")] class SequenceValue { [Key, Column("NAME")] public string Name { get; set; } [Column("VALUE")] public int Value { get; set; } }

TestContext クラス

C#

public class TestDBContext : DbContext { private SqliteConnection _conn; public TestDBContext(SqliteConnection conn) { _conn = conn; } protected override void OnConfiguring(DbContextOptionsBuilder builder) { builder.UseSqlite(_conn); } public DbSet<SequenceValue> Sequences { get; set; } }

試したこと

以下試しましたが、いずれもテスト結果は変わりません。
(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#

using (var context = new TestDBContext(testDB)) { context.Database.ExecuteSqlRaw("INSERT INTO SEQUENCES VALUES ('SEQ1', 1)"); using(var transaction = context.Database.BeginTransaction()) { context.Database.ExecuteSqlRaw("UPDATE SEQUENCES SET VALUE = 2 WHERE NAME='SEQ1'"); transaction.Rollback(); } using (var cmd3 = context.Database.GetDbConnection().CreateCommand()) { cmd3.CommandText = "SELECT VALUE FROM SEQUENCES WHERE NAME='SEQ1'"; var reader = cmd3.ExecuteReader(); reader.Read(); // コンソールには1が出力される:意図通りロールバックできている Console.WriteLine(reader.GetInt32(0)); } }

補足情報(FW/ツールのバージョンなど)

Visual Studio 2019、
MSTest を使う単体テストプロジェクトを作成。
コンソールアプリケーションを作成。
ターゲットフレームワークは「.NET 5.0 (現在)」です。

良い質問の評価を上げる

以下のような質問は評価を上げましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

気になる質問をクリップする

クリップした質問は、後からいつでもマイページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

  • プログラミングに関係のない質問
  • やってほしいことだけを記載した丸投げの質問
  • 問題・課題が含まれていない質問
  • 意図的に内容が抹消された質問
  • 過去に投稿した質問と同じ内容の質問
  • 広告と受け取られるような投稿

評価を下げると、トップページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

SurferOnWww

2021/09/16 23:00 編集

単体テストが期待通り動かないことを解決するのが課題なのか、EF Core + SQLite の DbContext.SaveChanges でロールバックすることが課題なのか、どっちでしょう? 後者なら単体テストを外したサンプルコードもアップできませんか? あと、インメモリーデータベースではなく普通にファイルベースにしてください。
Yas.T

2021/09/16 23:47

指摘ありがとうございます。ロールバックが課題です。 ファイルベースで、ということは「ファイルには書き込まれていない(ロールバックされている)」可能性がある、ということでしょうか。 確認してみます。
SurferOnWww

2021/09/17 00:11

> ファイルベースで、ということは「ファイルには書き込まれていない(ロールバックされている)」可能性がある、ということでしょうか。 分かりませんが、切り分けのため、単体テストのやり方の問題、インメモリーデータベースの影響など普通でないことはすべて排除して試してみるということは必要だと思いますが。 それから、「後者なら単体テストを外したサンプルコードもアップできませんか?」と書いた理由ですが、単体テストとか質問者さん独自の環境・問題含みでは調べてみようと思う人が少なくてレスが付きにくいのではと思うからです。実際自分もコードを読む気力は沸いてきませんし、他の人も同様でそれゆえレスがついてないのではと思います。
Yas.T

2021/09/17 03:05

ご指摘ありがとうございます…そのとおりですね。 元の質問の意図を変えない範囲でコードを見直しました。 また必要と思われるEntityやDbCotnextの定義は、別コードに分けました。

まだ回答がついていません

会員登録して回答してみよう

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
87.20%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問

同じタグがついた質問を見る

Entity Framework

Entity Frameworkは、.NET Framework 3.5より追加されたデータアクセス技術。正式名称は「ADO.NET Entity Framework」です。データベースエンジンに依存しておらず、データプロバイダの変更のみで様々なデータベースに対応できます。

.NET

.NETとは、主に.NET Frameworkと呼ばれるアプリケーションまたは開発環境を指します。CLR(共通言語ランタイム)を搭載し、入力された言語をCIL(共通中間言語)に変換・実行することが可能です。そのため、C#やPythonなど複数の言語を用いることができます。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。