ひっそりと生きるプログラマのブログ

日頃気になった事なりを書き留めるブログです。関心ごとは多くもう少し更新頻度を上げたいところです。

【Entity Framework】LINQ と Oracle の関数でマップされていないものを利用する(1)

LINQ 正規関数にマッピングされていない Oracle 関数の使い方。
幾つか実装方法はありますが、簡単な方法から。

環境

  • Visual Studio 2022
  • .net 6.0
  • EF Core 6.0.29
  • Oracle EF Core 6.21.1.40
  • NLog (ログを確認の為、出力)

実装

主要部分のみで、内容に意味は無いです。
"こうすれば動くよ!"という事を抜粋して記載します。

流れとしては以下の通りです。

  1. .net のメソッドを定義(Oracle 関数とマッピング用)
  2. .net で定義したメソッドを Oracle 関数とマッピング
  3. .net のメソッドを利用し SQL を発行し結果を確認

.net のメソッドを定義(Oracle 関数とマッピング用)

.net のメソッドをサクッと定義
平方根を算出する "SQRT" とマッピング予定

public static class StringExtension
{
    public static int Sqrt(this int? value) => 
        throw new NotImplementedException();
}

.net で定義したメソッドを Oracle 関数とマッピング

DbContext を継承し OnModelCreating を override します。
.net で定義したメソッドの MethodInfo を取得します。
HasDbFunction の引数に渡して、HasName で何の関数にマッピングするか設定。
※.net のメソッドと Oracle 関数が 1:1 なのでこれで十分

シグネチャが同じであれば、"SQRT" 以外のOracle関数でもマッピング可能。
.net のメソッドと Oracle の関数は役割が近く名前も似ていた方が把握し易くなりますね。

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    var sqrt = typeof(StringExtension).GetMethod("Sqrt", new Type[] { typeof(int) })!;
    modelBuilder.HasDbFunction(sqrt).HasName("SQRT");
    base.OnModelCreating(modelBuilder);
}

.net のメソッドを利用し SQL を発行し結果を確認

エンティティのメンバとか全無視して期待した SQL が発行されるか確認。

// SampleNumber の値に、 2, 16, 40, 6423 が設定されたレコード有
// 平方根の結果が、3より大きく 7未満の場合
var context = new SampleDbContext();
var items = (from m in context.SampleTables 
             where 
             m.SampleNumber.Sqrt() > 3 && 
             m.SampleNumber.Sqrt() < 7 
             select m);
foreach (var item in items)
{
    // 16
    // 40
    Console.WriteLine(item.SampleNumber);
}

上記で発行された SQL をログ出力した結果

SELECT "s"."ID", "s"."SAMPLENUMBER"
FROM "SAMPLETABLE" "s"
WHERE (("SQRT"("s"."SAMPLENUMBER") > 3) AND ("SQRT"("s"."SAMPLENUMBER") < 7)) 

マッピングされ期待した結果が無事取得出来ました。
ユーザーが独自に定義した関数も可能だったり、
.net のメソッドと Oracle 関数を 1:n でマッピングだったり出来るので気分が向いたら追記します。