Linq to Entities - 自定义方法(OrderBy)在lambda语句中失败

时间:2017-06-09 23:33:52

标签: c# lambda linq-to-entities extension-methods

我正在尝试为Linq to Entities编写自定义OrderBy扩展方法,我可以根据参数对ASC或DESC进行排序。我的第一次尝试如下:

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可以理解的内容。

我一直在做一些谷歌搜索,但我还没有看到任何明确表示这是或不可能的事情。所以我的问题是..有没有办法制作一个自定义的OrderBy方法(扩展方法与否),它将在lambda表达式中工作?

2 个答案:

答案 0 :(得分: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是关于表达式树的Microsoft文档

仅当您将其传递给lambda表达式时失败的原因是因为您传入的内容无法编译为真正的表达式树

答案 1 :(得分:-1)

我终于通过使用NuGet包找到了解决方案: LinqKit.EntityFramework

它的工作方式是你首先调用.AsExpandable()这会在IQueryable周围放置一个包装器。然后你必须在内部lambda表达式上调用.Invoke。 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);
    }
}