【実務・中級編】上級プロフェッショナル向け:VB.NETでの「Expression(Of TDelegate)」を用いた動的クエリ構築:実行時の条件変化に対応するLINQ式ツリーの生成 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

文字列結合の時代は終わった:VB.NETで実現する「型安全な動的クエリ」の極意

現場でよく見る「最悪の光景」がある。ユーザーの検索条件に合わせて、`WHERE string &= ” AND Name = ‘” & input & “‘”` といった文字列結合を繰り返すコードだ。これはSQLインジェクションの温床であり、コンパイラの恩恵を一切受けられない、保守性ゼロの遺物である。

真のエンジニアは、実行時に「何が検索条件になるか」を決定し、それをExpression Tree(式ツリー)という抽象構文木として動的に構築する。今日は、VB.NETにおいて`Expression(Of TDelegate)`を駆使し、堅牢かつ高速なクエリ生成を実現する極限のテクニックを伝授する。

なぜ「Expression」を使うのか?

LINQ to Entities (EF Core) を使用する場合、コンパイル時に確定するクエリは `IQueryable` として最適化される。しかし、実務の現場では「検索条件が可変である」ことが常識だ。

文字列でSQLを組み立てるのではなく、Expression Treeを組み立てれば、.NETはそれをSQLへ変換する過程で、型チェックとパラメータ化(SQLインジェクション対策)を自動的に行ってくれる。つまり、実行時に組み立てているのに、コンパイル時と同じ型安全性を享受できるのだ。

プロダクション環境で動く「動的フィルタ生成」の実装

以下のコードは、任意のプロパティ名と値を受け取り、動的に `WHERE` 句を構築する汎用的なジェネリック関数である。

Imports System.Linq.Expressions
Imports System.Reflection

Public Class QueryBuilder
”’

”’ 指定したプロパティ名と値で動的にExpressionを構築する
”’

Public Shared Function BuildEqualsExpression(Of T)(propertyName As String, value As Object) As Expression(Of Func(Of T, Boolean))
Dim parameter = Expression.Parameter(GetType(T), “x”)
Dim propertyAccess = Expression.Property(parameter, propertyName)
Dim constantValue = Expression.Constant(value, propertyAccess.Type)

‘ x.PropertyName == value を構築
Dim equality = Expression.Equal(propertyAccess, constantValue)

Return Expression.Lambda(Of Func(Of T, Boolean))(equality, parameter)
End Function
End Class

応用:複数の条件をAND/ORで結合する

実際の業務では「名前がAかつ年齢が20以上」といった複合条件が必須となる。これを実現するには `Expression.AndAlso` や `Expression.OrElse` を使用する。

‘ 複数のExpressionをANDで結合する拡張メソッド

Public Function AndAlso(Of T)(left As Expression(Of Func(Of T, Boolean)), right As Expression(Of Func(Of T, Boolean))) As Expression(Of Func(Of T, Boolean))
Dim parameter = left.Parameters(0)

‘ パラメータを統合して新しいBodyを作る
Dim combined = Expression.AndAlso(
Expression.Invoke(left, parameter),
Expression.Invoke(right, parameter)
)

Return Expression.Lambda(Of Func(Of T, Boolean))(combined, parameter)
End Function

現場で「バグ」を生ませないための3つの鉄則

1. プロパティ名の存在確認を怠るな
`Expression.Property` で指定する文字列が、クラス内に存在しない場合、実行時に例外が発生する。開発時に `GetType(T).GetProperty(propertyName)` で存在を確認し、Nullならば早期リターンする設計を徹底すること。

2. 型変換(Boxing)のオーバーヘッドを意識せよ
`Expression.Constant` に渡す値がプロパティの型と一致しない場合、クエリ変換時に予期せぬエラーが起きる。`Convert.ChangeType` を使い、必ずターゲットの型に合わせる「型変換処理」を挟むのがプロの仕事だ。

3. 実行計画のキャッシュを考慮する
動的にクエリを生成すると、LINQプロバイダが毎回「新しいクエリ」だと認識し、実行計画のキャッシュが効かないことがある。もし頻繁に実行される検索であれば、結果をキャッシュする層を設けるか、複雑すぎる動的生成は避けるという「引き際」の見極めも重要だ。

最後に:エンジニアとしての矜持

文字列を結合してSQLを作るのは、初心者でもできる。だが、式ツリーを制御し、型安全かつ堅牢なクエリ生成基盤を構築するのは、コードの「ライフサイクル」と「パフォーマンス」を知り尽くした者のみに許された領域だ。

この手法を導入すれば、UI側でどのような検索画面が追加されても、バックエンド側のロジックを変更する必要はなくなる。変更に強く、バグを生まない。これこそが、我々エンジニアが目指すべき「真の自動化」である。

さあ、今すぐプロジェクトの `String &= …` を検索し、すべてこの「Expression Tree」へと書き換える準備を始めよう。君のコードが変われば、現場の生産性は劇的に向上するはずだ。

タイトルとURLをコピーしました