尝试在 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.IEnumerable
1[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.
的结构的一部分
考虑这些扩展:
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.IOrderedEnumerable
1[Namespace.Thing] OrderDateAndNumber(System.Collections.Generic.IEnumerable
1[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.