この例では、MS が提供する NorthWind データベースを使用しています。
述語ビルダーを使用して複数を組み立てようとしています
Expression<Func<T,bool>>
単一の式ツリーに。私の意見では、ほとんどうまく機能しますが、対処できないと思われる大きな欠陥が1つあります.
私は現在、そのように定義された2つの式を持っています:
Expression<Func<Customer, bool>> UnitPriceLessThan2 = c =>
c.Orders.Any(o => o.Order_Details.Any(d => d.Product.UnitPrice <= 2));
Expression<Func<Customer, bool>> UnitPriceGreaterThan5 = c =>
c.Orders.Any(o => o.Order_Details.Any(d => d.Product.UnitPrice >= 5));
私はPete Montgomery のユニバーサル述語ビルダーを使用して、これら 2 つを次のように OR します。
Expression<Func<Customer,bool>> PriceMoreThan5orLessThan2 =
UnitPriceLessThan2.Or(UnitPriceGreaterThan5);
これらの式はどちらも、同じパスを介して Product エンティティに移動する必要があるため、両方の条件に対して同じサブクエリを再利用することは理にかなっています。条件を手動で記述した場合、次のようになります。
Expression<Func<Customer,bool>> PriceMoreThan5orLessThan2 = c =>
c.Orders.Any(o =>
o.Order_Details.Any(d => d.Product.UnitPrice >= 5 ||
d.Product.UnitPrice <= 2));
ただし、これらの述語を動的に構築する必要があるため、何百もの可能な組み合わせがあるため、それを行うことはできません...またはそれ以上です。
したがって、私の質問は、LINQ to Entities が次のようなクエリを作成しないようにするにはどうすればよいかということです。
SELECT
/*all the customer columns*/
FROM [dbo].[Customers] AS [Extent1]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM ( SELECT
[Extent2].[OrderID] AS [OrderID]
FROM [dbo].[Orders] AS [Extent2]
WHERE [Extent1].[CustomerID] = [Extent2].[CustomerID]
) AS [Project1]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Order Details] AS [Extent3]
INNER JOIN [dbo].[Products] AS [Extent4] ON [Extent3].[ProductID] = [Extent4].[ProductID]
WHERE ([Project1].[OrderID] = [Extent3].[OrderID]) AND ([Extent4].[UnitPrice] <= cast(2 as decimal(18)))
)
)) OR ( EXISTS (SELECT
1 AS [C1]
FROM ( SELECT
[Extent5].[OrderID] AS [OrderID]
FROM [dbo].[Orders] AS [Extent5]
WHERE [Extent1].[CustomerID] = [Extent5].[CustomerID]
) AS [Project4]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Order Details] AS [Extent6]
INNER JOIN [dbo].[Products] AS [Extent7] ON [Extent6].[ProductID] = [Extent7].[ProductID]
WHERE ([Project4].[OrderID] = [Extent6].[OrderID]) AND (([Extent7].[UnitPrice] >= cast(5 as decimal(18)))))));
問題は、本当に必要なのは 1 つだけだったのに、2 つの EXISTS サブクエリを作成してしまったことです。
代わりに、クエリを次のようにしたいと思います。
SELECT
/*all the customer columns*/
FROM [dbo].[Customers] AS [Extent1]
WHERE( EXISTS (SELECT
1 AS [C1]
FROM ( SELECT
[Extent5].[OrderID] AS [OrderID]
FROM [dbo].[Orders] AS [Extent5]
WHERE [Extent1].[CustomerID] = [Extent5].[CustomerID]
) AS [Project4]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Order Details] AS [Extent6]
INNER JOIN [dbo].[Products] AS [Extent7] ON [Extent6].[ProductID] = [Extent7].[ProductID]
WHERE ([Project4].[OrderID] = [Extent6].[OrderID]) AND (([Extent7].[UnitPrice] >= cast(5 as decimal(18))) OR ([Extent7].[UnitPrice] <= cast(2 as decimal(18))))
)
))
何らかの方法でナビゲーション パスを式として保存して再利用し、2 つの条件を適切なユーザー指定の演算子と値と共に挿入することはできますか?
または、いくつかの式のビジター実装を使用して...正確には何を見つけて置換するのかわかりませんか?
私のかなり長い質問を読んでくれてありがとう:)