尝试在 Entity Framework 中创建自定义(命名)OrderBy 扩展的异常

Exception trying to create custom (named) OrderBy extension in Entity Framework

考虑这些扩展:

public static IOrderedEnumerable<Thing> OrderByDateAndNumber(this IEnumerable<Thing> source)
{
    return source.OrderByDescending(x => x.ThingDate).ThenByDescending(x => x.ThingNumber);
}

public static IOrderedQueryable<Thing> OrderByDateAndNumber(this IQueryable<Thing> source)
{
    return source.OrderByDescending(x => x.ThingDate).ThenByDescending(x => x.ThingNumber);
}

这个有效:

var x = context.Things.OrderByDateAndNumber().ToList();

但事实并非如此:

var y = context.Widgets.Select(w => w.Things.OrderByDateAndNumber().Count());

产生异常:

[NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IOrderedEnumerable1[Namespace.Thing] OrderDateAndNumber(System.Collections.Generic.IEnumerable1[Namespace.Thing])' method, and this method cannot be translated into a store expression.]

我知道 C# 代码无法转换为存储表达式。我不明白的是为什么它在第一个实例中起作用(假设这是通过我的扩展调用仅调用支持的 OrderBy 方法实现的)但在第二个实例中不起作用,我试图将它合并到一个语句中使用导航 属性,即 w.Things)

简单地说。

Context.Things

Context 在您的 c# 方法范围内被引用,它可以访问您的扩展方法。

w => w.Things

W 只是一个 lambda 引用,它将在 later 中被评估 later in Linq To entities assembly which not have access to您的扩展方法范围。

我发现 LinqKit 在这些情况下很有帮助,它具有 "Expand" 功能。

它所做的只是在将扩展方法发送到 Linq-to-Entities 之前先翻译您的扩展方法。

如果您删除扩展方法的语法糖并使用经典方法调用,这将变得很明显:

这个有效:

var x = LinqExtensions.OrderByDateAndNumber(context.Things).ToList();

但事实并非如此:

var y = context.Widgets.Select(w => 
            LinqExtensions.OrderByDateAndNumber(w.Things)).Count());

第一个方法解析为接受 IQueryable 的重载,它只是 returns 附加了排序表达式的 IQueryable

另一种表达方式是:在第一个片段中,OrderByDateAndNumber 表达式之外。

在第二个片段中,OrderByDateAndNumber 解析为 IEnumerable 重载,因为导航 属性 w.Things(可能)是一个 ICollection。此外 OrderByDateAndNumber 现在 表达式中,因此,应该翻译成 SQL.

的结构的一部分