複数のテーブルにわたってEF Core 3.1を使用してクエリを作成しようとしていますが、それが機能しません。
いくつかのダミーの例で説明しようと思います。
私のSQLテーブルはすべて、SQL Server DB内で次のように定義されていると仮定します。
ダミーエンティティは次のとおりです。
外部キーの関係:
すべてのエンティティにナビゲーションプロパティが設定されており、DBコンテキストにプライマリキーと外部キーを設定しました。
これらのテーブルの大多数にはビットフィールド「有効」があるため、行を削除せずに無効にすることができます。
だから私が書こうとしているクエリは次のようになります:
var data = await context.Town.AsNoTracking()
.Where(t => t.TownName == request.TownName)
.Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
.ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
.ThenInclude(ic => ic.Cars.Where(c => c.Enabled))
.ThenInclude(c => c.Manufacturer.Where(m => m.Enabled))
.Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
.ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
.ThenInclude(ic => ic.Cars.Where(c => c.Enabled))
.ThenInclude(c => c.Mechanic.Where(m => m.Enabled && m.Name == request.AllowedMechanic))
.ToListAsync().ConfigureAwait(false);
要約すると、「MechanicsAreUs」がサービスを提供する「ロンドン」に住む「ジョンスミス」が運転している車を知りたいのです。
これは私にはかなり長い時間のように思えます、そしてそれが私の問題がある場所かもしれません。
とにかく、後者のThenIncludesの.WHERE
句の.WHERE
はコンパイルされません。コンパイルされるまで1つずつ削除すると、次のようになります。
var data = await context.Town.AsNoTracking()
.Where(t => t.TownName == request.TownName)
.Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
.ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
.ThenInclude(ic => ic.Cars)
.ThenInclude(c => c.Manufacturer)
.Include(t => t.Inhabitants.Where(i => i.Name == request.InhabitantName && i.Enabled)
.ThenInclude(i => i.InhabitantCar.Where(ic => ic.Enabled))
.ThenInclude(ic => ic.Cars)
.ThenInclude(c => c.Mechanic)
.ToListAsync().ConfigureAwait(false);
だから、書かれているように、それは無効にされたエントリーを取り戻すでしょう、そして私はメカニズムを指定していません。しかし、実行すると例外が発生します。
System.InvalidOperationException:Include内で使用されているラムダ式が無効です。
私はさまざまなMicrosoftの例を何年もかけて調べてきましたが、このレベルの複雑さのように思われる例は見つかりません。ほんの一握りの内部結合です。数分以内にストアドプロシージャで実行できる何か。 Entity Frameworkを使用してこれを実行したいだけです。
.Include(...)
熱心なロードをフィルタリングすることはできません。 David Browneが質問へのコメントで述べたように、 Enabled
フラグに基づいてレコードを除外する場合は、 クエリフィルターを使用する必要があります 。例:
modelBuilder.Entity<Car>()
.HasQueryFilter(c => c.Enabled);
私はあなたがCar
エンティティに興味を持っているようですので、クエリを再構成して焦点を合わせましょう:
var query = context.Cars;
あなたは、関連付けられた車たくInhabitant
、特定の関連付けられている特定の名前を持つTown
だけでなく、特定のがサービスを提供されたMechanic
それではフィルタは、その基準でみましょう:
query = query.Where( c =>
c.InhabitantCar.Inhabitant.Name == request.InhabitantName
&& c.InhabitantCar.Inhabitant.Town.TownName == request.TownName
&& c.Mechanic == request.AllowedMechanic );
このクエリは、必要なCar
エンティティを返すようになったので、熱心な負荷を構成しましょう。
query = query.Include( c => c.Manufacturer )
.Include( c => c.Mechanic )
.Include( c => c.InhabitantCar )
.ThenInclude( ic => ic.Inhabitant )
.ThenInclude( i => i.Town );
それを試してみてください。
1つの推奨事項は、クエリフィルターを使用することでした。
この背後にある考え方は素晴らしかった-私のDBのコンテキスト・ファイルで、私は、 例えばフィルタの共通セットを追加することができます
builder.Entity<Town>()
.HasQueryFilter(a => a.Enabled);
builder.Entity<Car>()
.HasQueryFilter(a => a.Enabled);
builder.Entity<Manufacturer>()
.HasQueryFilter(a => a.Enabled);
そして、それは私のサービスファイルによって生成されるすべてのクエリに含まれます-開発者は気にする必要はありません。
私は結果のSQLを分析した場合しかし、私は例えば 、私のコードは、複数のサブクエリが散らばっていることが分かっ
Inner Join (Select...Where ...Enabled = 1)
これらの一元化されたクエリフィルターを削除し、これをLINQステートメントのWHERE句に追加すると、はるかに効率的なクエリになります。