Linq to Entities - 自定义方法(OrderBy)在lambda语句中失败
我正在尝试为Linq to Entities编写自定义的OrderBy扩展方法,其中我可以根据参数对ASC或DESC进行排序。我的第一次尝试如下:Linq to Entities - 自定义方法(OrderBy)在lambda语句中失败
public static class LinqExtensions
{
public static IOrderedQueryable<TSource> OrderByExtension<TSource, TKey>(this IQueryable<TSource> source, Expression<Func<TSource, TKey>> keySelector, bool isDescending = true)
{
return (isDescending) ? source.OrderByDescending(keySelector) : source.OrderBy(keySelector);
}
}
这很好用......只要我不在lambda表达式中使用这个方法。玩具的例子是:
using (var dbContext = new FooDBEntities())
{
var foo = dbContext.Fubars.SelectMany(x => dbContext.Fubars.OrderByExtension(y => y.Foo, true)).ToList();
}
当我运行上面的代码程序吹灭,我得到以下错误:
Unhandled Exception: System.NotSupportedException: LINQ to Entities does not recognize the method 'System.Linq.IOrderedQueryable`1[ExpressionTreeFoo.Fubar] OrderByExtension[Fubar,String](System.Linq.IQueryable`1[ExpressionTreeFoo.Fubar], System.Linq.Expressions.Expression`1[System.Func`2[ExpressionTreeFoo.Fubar,System.String]], Boolean)' method, and this method cannot be translated into a store expression.
错误几乎说明了一切。该框架不知道如何将我的自定义扩展方法转换为SQL可以理解的东西。
我一直在做一些谷歌搜索,但我没有看到任何明确表示这是或不可能的东西。所以我的问题是..有什么办法可以在一个lambda表达式中工作的自定义OrderBy方法(扩展方法或不)?
我终于通过使用NuGet包找到了解决办法:LinqKit.EntityFramework
它的工作方式是你冷杉t调用.AsExpandable()这将围绕IQueryable包装。然后你必须调用。调用内部lambda表达式。 Invoke是LinqKit的扩展方法。当调用这个查询时,LinqKit会递归扫描表达式树,当它碰到一个调用扩展Invoke的表达式时,它将移除对Invoke的调用,并以CLR引擎可以理解的方式重写表达式树。 http://www.albahari.com/nutshell/linqkit.aspx
static void EFTest()
{
using (var dbContext = new DBEntities())
{
// debug - print query
dbContext.Database.Log = Console.WriteLine;
// get order by expression
var orderByExpression = GetOrderByExpression(dbContext, true);
// execute query
var result = dbContext.FooTables
.AsExpandable()
.SelectMany(x => orderByExpression.Invoke(x))
.ToList();
}
}
private static Expression<Func<FooTable, IOrderedQueryable<FooTable>>> GetOrderByExpression(DBEntities dbContext, bool isDesending)
{
if (isDesending)
{
return x => dbContext.FooTables.OrderByDescending(y => y.Date);
}
else
{
return x => dbContext.FooTables.OrderBy(y => y.Date);
}
}
这是因为lambda不是直接与SQL有已知接口的编译表达式树。从你创建的意义上说,lambda是一个匿名的C#方法,它只能由CLR执行。虽然他们共享相同的语法,但他们是框架的不同元素。如果你想实现你自己定制的Linq-To-Entities表达式,你将需要研究楼宇Expression Trees
。
要简单地解释它 - 你怎么把这个:
(isDescending) ? source.OrderByDescending(keySelector) : source.OrderBy(keySelector);
进入这个?
SELECT * FROM [MyTable] ORDER BY [MyTable].[MyColumn] DESC
您必须正确定义如何使用Linq表达式树来完成此操作。
只有当你将它传递到lambda表达式失败的原因是微软的文档了类似的问题,因为你所传递不能被编译成一个真正的表达式树
你确定它与简单的查询完全正常:一个完整的描述,可以发现?你检查了生成的SQL吗? –
是的,我已验证。我正在使用:context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s)来打印查询。 – Soto