あるSEのつぶやき・改

ITやシステム開発などの技術に関する話題を、取り上げたりしています。

.NET Core, Entity Framework CoreのDbContext#SaveChangesがトランザクション処理をしていることを確認する

はじめに

前の記事では、SELECT 処理の SQL ログの出力方法をご紹介しましたが、更新系のログをフィルタをかけない状態のものをご紹介していませんでした。

なぜフィルタをかけない更新系のログを見る必要があるかというと、DbContext#SaveChanges メソッドを呼び出すだけでトランザクション処理を行っていることを確認するためです。

DbContext#SaveChanges メソッドを呼び出して例外処理をするだけで、簡単にトランザクション処理を行うことができるということですね。

では、実際に更新系の SQL ログを確認してみましょう。

SQL ログフィルタの解除

前の記事のデータベースコンテキストクラスの以下の部分を変更します。これで SQL ログがフィルタされなくなります。

   public static readonly LoggerFactory MyLoggerFactory
        = new LoggerFactory(new[]
        {
            new ConsoleLoggerProvider((_, __) => true, true) //(変更)
        });

更新処理を記述

Program.cs の Main メソッドを以下のように、新規追加の更新系処理に変更します。

    static void Main(string[] args)
    {
        using (var db = new consoletestContext())
        {
            db.Book.Add(new Book{Title="おはよう", Price=1000});
            db.Book.Add(new Book{Title="こんにちは", Price=500});
            db.Book.Add(new Book{Title="こんばんは", Price=800});
            db.SaveChanges();
        }
    }

SQLログの確認

では、実際のプログラムを実行して SQL ログを確認してみましょう。

$ dotnet run
dbug: Microsoft.EntityFrameworkCore.Infrastructure[10401]
      An 'IServiceProvider' was created for internal use by Entity Framework.
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]
      Entity Framework Core 2.0.1-rtm-125 initialized 'consoletestContext' using provider 'Npgsql.EntityFrameworkCore.PostgreSQL' with options: None
dbug: Microsoft.EntityFrameworkCore.Database.Connection[20000]
      Opening connection to database 'consoletest' on server 'tcp://localhost:5432'.
dbug: Microsoft.EntityFrameworkCore.Database.Connection[20001]
      Opened connection to database 'consoletest' on server 'tcp://localhost:5432'.
dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20200]
      Beginning transaction with isolation level 'ReadCommitted'.
dbug: Microsoft.EntityFrameworkCore.Database.Command[20100]
      Executing DbCommand [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?'], CommandType='Text', CommandTimeout='30']
      INSERT INTO "book" ("price", "title")
      VALUES (@p0, @p1)
      RETURNING "id";
      INSERT INTO "book" ("price", "title")
      VALUES (@p2, @p3)
      RETURNING "id";
      INSERT INTO "book" ("price", "title")
      VALUES (@p4, @p5)
      RETURNING "id";
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (59ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p3='?', @p4='?', @p5='?'], CommandType='Text', CommandTimeout='30']
      INSERT INTO "book" ("price", "title")
      VALUES (@p0, @p1)
      RETURNING "id";
      INSERT INTO "book" ("price", "title")
      VALUES (@p2, @p3)
      RETURNING "id";
      INSERT INTO "book" ("price", "title")
      VALUES (@p4, @p5)
      RETURNING "id";
dbug: Microsoft.EntityFrameworkCore.Database.Command[20300]
      A data reader was disposed.
dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20202]
      Committing transaction.
dbug: Microsoft.EntityFrameworkCore.Database.Connection[20002]
      Closing connection to database 'consoletest' on server 'tcp://localhost:5432'.
dbug: Microsoft.EntityFrameworkCore.Database.Connection[20003]
      Closed connection to database 'consoletest' on server 'tcp://localhost:5432'.
dbug: Microsoft.EntityFrameworkCore.Database.Transaction[20204]
      Disposing transaction.

色付けできないので分かりづらいかもしれませんが、10〜11行目の部分でトランザクションが開始されていることが分かります。

下から7〜8行目でコミットしていることも分かりますね。

これで、DbContext#SaveChanges メソッドを呼び出せば自動でトランザクション処理をしていることが分かりました。

このログでもう1つ着目したいのが、INSERT 文が全てパラメータ化されており、SQL インジェクションの脆弱性が発生しないようになっていることです。

Entity Framework Core を使用するだけで SQL インジェクション対策になる訳ですね。

Entity Framework Core は LINQ が使えることで生産性が高いことが有名ですが、セキュリティ的にもオススメです。

おわりに

DbContext#SaveChanges メソッドがトランザクション処理を行うことが確認できましたが、いかがだったでしょうか。

ログを確認しないと分からないのが難点ではありますが、実際にログを確認したので安心して使用することができますね。