3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Entity Framework Coreで行ロックを取得する方法

Posted at

Entity Framework Coreで行ロックを取得する方法

Entity Framework Core(以下EF Core)でデータ取得と同時に更新ロック(行ロック)をかける方法を紹介します。
SQLで記述した場合、以下のようなクエリに対応する処理です。

-- SQL Server
SELECT * FROM [TABLE_NAME] WITH (UPDLOCK)
-- Oracle
SELECT * FROM [TABLE_NAME] FOR UPDATE

以下の環境で動作確認を行っています。

  • .NET Core 3.1 (C# 8.0)
  • Microsoft SQL Server 2016 Express Edition
  • Entity Framework Core 5.0.4

更新ロックを取得する方法

調べた限りではEF Coreは標準で更新ロックを取得する仕組みを提供していないようです。
その為、生SQLクエリを使用する方法で対応します。

// dbSet は DbContext と紐づけられた DbSet<T> のインスタンスです
dbSet.FromSqlRaw<T>($"SELECT * FROM [{テーブル名}] WITH (UPDLOCK)")

FromSqlRawメソッドはIQueryable<T>を返すため、続けてWhereメソッド等で対象を絞り込むクエリが実現できます。

繰り返し使用することになると思いますので、共通メソッドを定義すると良いと思われます(以下の例は拡張メソッドとして記述しています)。
その際、クエリを作成するためにテーブル名が必要となりますので、何らかの方法で取得する必要があります。
テーブル名がエンティティ型のクラス名と一致する場合は以下のように書けます(Tはエンティティ型とします)。

public static IQueryable<T> ForUpdate<T>(this DbSet<T> dbSet) where T : class
{
    return dbSet.FromSqlRaw<T>($"SELECT * FROM [{typeof(T).Name}] WITH (UPDLOCK)");
}

クラス名がテーブル名と異なる場合は、エンティティクラスにTable属性1でテーブル名を対応づけ、これを参照するのが簡単です。2

public static IQueryable<T> ForUpdate<T>(this DbSet<T> dbSet) where T : class
{
    var table = (TableAttribute)Attribute.GetCustomAttribute(typeof(T), typeof(TableAttribute));
    return dbSet.FromSqlRaw<T>($"SELECT * FROM [{table.Name}] WITH (UPDLOCK)");
}

更新ロックの必要性について

通常、EF CoreはUPDATE等の更新クエリはSaveChangesメソッドを実行した時点で発行される為、ロックを取得する時間が最小限となります。

// context は DbContext のインスタンスです
using var transaction = context.Database.BeginTransaction();
try
{
    var entities = context.Table.ToList();
   // この時点ではロックは取得しない

    // データ編集等

    if (context.SaveChanges() > 0)
    {
        // SaveChangesメソッドで更新系クエリが発行され、更新ロックが取得され、
        // Commitを行うまでの間ロックが保持される
        transaction.Commit();
    }
}
catch
{
    transaction.Rollback();
}

ロックの競合を防ぐ観点ではこのままでも良いのですが、データ編集のコストが高い場合にデータの取得から実際に更新を行うまでの間隔が長くなってしまい、他者の更新等が割り込まれてデータの不整合を引き起こす可能性が高まります。
そういった場合ではデータを取得した時点で更新ロックを取得し、データ編集処理中のDB変更を抑制する必要があります。

  1. Table属性: System.ComponentModel.DataAnnotations.Schema.TableAttribute

  2. エンティティクラスにTable属性を付与する方法は以下を参照ください。
    https://docs.microsoft.com/ja-jp/ef/core/modeling/entity-types?tabs=data-annotations

3
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?