在这个例子中,我使用的是 MS 提供的 NorthWind 数据库。
我正在尝试使用谓词构建器来组装多个
Expression<Func<T,bool>>
到单个表达式树中。在我看来,它主要工作得很好,但有一个我似乎无法解决的主要缺陷。
我目前有两个这样定义的表达式:
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 的通用谓词构建器来 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)))))));
问题是我们创建了两个 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))))
)
))
我可以以某种方式将导航路径存储和重用为表达式,然后将这两个条件及其适当的用户提供的运算符和值注入其中吗?
或者使用一些表达式访问者实现...我不知道究竟是什么,查找和替换?
感谢您阅读我相当冗长的问题:)