2017-06-09 76 views
-1

我正在尝试为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方法(扩展方法或不)?

+0

你确定它与简单的查询完全正常:一个完整​​的描述,可以发现?你检查了生成的SQL吗? –

+0

是的,我已验证。我正在使用:context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s)来打印查询。 – Soto

回答

-1

我终于通过使用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); 
    } 
} 
2

这是因为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表达式树来完成此操作。

Here是你的(但没有一个答案) Here是表达式树

只有当你将它传递到lambda表达式失败的原因是微软的文档了类似的问题,因为你所传递不能被编译成一个真正的表达式树

+1

感谢您对lambda语句细微差别的快速反应和洞察力。我会检查你提供的链接。 -cheers – Soto

+0

@Soto如果这符合您的需求,请考虑将其标记为答案 –

+0

我仍然没有一个明确的解决方案,缺少创建自己的框架来构建Expression Tress。我仍然在努力,但如果我使用链接提出解决方案,或者如果知道其他答案并提供明确的解决方案,我会给你提供观点。 – Soto