在Linq-To实体查询中混合函数谓词和表达式谓词

本文关键字:谓词 函数 表达式 混合 Linq-To 实体 查询 | 更新日期: 2023-09-27 18:21:38

给定一个名为Fruit:的实体

public class Fruit
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Family { get; set; }
    public bool Edible { get; set; }
}

以下Linq To实体查询:

var familiesWithAllEdibleFruits = context
    .Fruits
    .GroupBy(fruit => fruit.Family)
    .Where(group => group.All(fruit => fruit.Edible));

生成一个选择正确记录的SQL语句:

SELECT 
    [Project4].[C1] AS [C1], 
    [Project4].[Family] AS [Family], 
    [Project4].[C2] AS [C2], 
    [Project4].[Id] AS [Id], 
    [Project4].[Name] AS [Name], 
    [Project4].[Family1] AS [Family1], 
    [Project4].[Edible] AS [Edible]
    FROM ( SELECT 
        [Project2].[Family] AS [Family], 
        [Project2].[C1] AS [C1], 
        [Project2].[Id] AS [Id], 
        [Project2].[Name] AS [Name], 
        [Project2].[Family1] AS [Family1], 
        [Project2].[Edible] AS [Edible], 
        CASE WHEN ([Project2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
        FROM ( SELECT 
            [Distinct1].[Family] AS [Family], 
            1 AS [C1], 
            [Extent2].[Id] AS [Id], 
            [Extent2].[Name] AS [Name], 
            [Extent2].[Family] AS [Family1], 
            [Extent2].[Edible] AS [Edible]
            FROM   (SELECT DISTINCT 
                [Extent1].[Family] AS [Family]
                FROM [dbo].[Fruits] AS [Extent1] ) AS [Distinct1]
            LEFT OUTER JOIN [dbo].[Fruits] AS [Extent2] ON ([Distinct1].[Family] = [Extent2].[Family]) OR (([Distinct1].[Family] IS NULL) AND ([Extent2].[Family] IS NULL))
        )  AS [Project2]
        WHERE  NOT EXISTS (SELECT 
            1 AS [C1]
            FROM [dbo].[Fruits] AS [Extent3]
            WHERE (([Project2].[Family] = [Extent3].[Family]) OR (([Project2].[Family] IS NULL) AND ([Extent3].[Family] IS NULL))) AND ([Extent3].[Edible] <> cast(1 as bit))
        )
    )  AS [Project4]
    ORDER BY [Project4].[Family] ASC, [Project4].[C2] ASC

但是下面的代码中的内部谓词是一个表达式:

Expression<Func<Fruit, bool>> innerPredicate = fruit => fruit.Edible;
var familiesWithAllEdibleFruits = context
    .Fruits
    .GroupBy(fruit => fruit.Family)
    .Where(group => group.All(innerPredicate));

陷入编译器的困境:

'System.Linq.IGrouping<string,Fruit>'不包含"All"和最佳扩展方法重载"System.Linq.Enumerable.All<TSource>(System.Collections.Generic.IEnumerable<TSource>,System.Func<TSSource,bool>)'具有一些无效参数

然而,当外部谓词封装在表达式中时:

Expression<Func<IGrouping<string, Fruit>, bool>> outerPredicate =
    group => group.All(fruit => fruit.Edible);
var familiesWithAllEdibleFruits = context
    .Fruits
    .GroupBy(fruit => fruit.Family)
    .Where(outerPredicate);

事情运转正常。

我想更好地理解我在这里看到的行为。外部谓词内部对"All"的调用似乎不允许使用Expression参数。是否可以很容易地将查询与函数和表达式互换组合(如第二个示例中所示),或者这是一个固有的限制?

在Linq-To实体查询中混合函数谓词和表达式谓词

您将能够管理的最好的方法是在此处使用LINQKit:

Expression<Func<Fruit, bool>> innerPredicate = fruit => fruit.Edible;
var familiesWithAllEdibleFruits = context
    .Fruits
    .GroupBy(fruit => fruit.Family)
    .Where(group => group.All(fruit => innerPredicate.Invoke(fruit)))
    .Expand();

至于原因,您的第一个代码片段是一个表示Func的表达式,而您想要的是一个仅表示Func的表达式。你需要一种"打开"表达式的方法。简而言之,这并不容易。与编写常规委托相比,编写表达式需要做更多的工作,因为您需要展开每个表达式的主体并将其内联到外部表达式中。