ごった煮

色々な事を書いてます

EF Core 6 で json_value を使いたい

EF Core では、現状 json_value をそのまま使えないですが、どうしても json_value を使いたい用事があったので、出来るように実装しました。

やり方

  1. 生の SQL を書く
  2. DbFunctions で関数呼び出しを追加する

1番なら何でもできますが、今回は、もうちょっと EF Core を活用したいので、2 番で実装します。

実装する

まず DbFunction を定義します。

public static class DBFunctions
{
    public static string JsonValue(string column, string path)
    {
        throw new NotSupportedException();
    }
}

次に、DbContext を組み立てる OnModelCreating で先ほど定義した DbFunction をセットします。 下記のコードは、文字列を返すようにする設定です

modelBuilder.HasDbFunction(typeof(DBFunctions).GetMethod(nameof(DBFunctions.JsonValue)))
    .HasTranslation(args =>
        new SqlFunctionExpression("JSON_VALUE", args, nullable: true, argumentsPropagateNullability: new[] { false, false }, typeof(string), null));

日付型を返したいといった場合は、以下のような感じ

modelBuilder.HasDbFunction(typeof(DBFunctions).GetMethod(nameof(DBFunctions.JsonValueDateTime)))
    .HasTranslation(args =>
        new SqlFunctionExpression("JSON_VALUE", args, nullable: true, argumentsPropagateNullability: new[] { false, false }, typeof(DateTime), null));

使い方は、以下のような感じ

var customer = await _dbContext
    .Customer
    .Where(_ => DBFunctions.JsonValue(_.Value, "$[0].name") != null)
    .Select(_ => new
    {
        name = DBFunctions.JsonValue(_.Value, "$[0].name")
    })
    .ToListAsync();

このコードを実行すると、下記のような SQL に展開されます。

SELECT JSON_VALUE([c].[Value], N'$[0].name') AS [name]
FROM [customer] AS [c]
WHERE JSON_VALUE([c].[Value], N'$[0].name') IS NOT NULL

まとめ

これで json_value が使えるようになりました。

EF Core 7 では、いい加減 json column が対応するみたいなので、一時しのぎですがこれにて一件落着