3

我正在尝试构建一个将对查询进行分页的扩展方法。但为了避免异常:System.NotSupportedException 未被用户代码处理 Message=方法“Skip”仅支持 LINQ to Entities 中的排序输入。必须在方法“Skip”之前调用方法“OrderBy”。

我想检查 OrderBy 是否被应用,如果不只是返回查询......像这样的东西:

public static IQueryable<T> Paginate<T>(this IQueryable<T> query, int page, int pageSize = 50, int total = -1)
{
    // check if OrderBy was applied

    // THIS DOES NOT WORK!!!
    //try
    //{
    //    var orderedQueryable = query as IOrderedQueryable<T>;
    //}
    //catch (Exception)
    //{
    //    // if the cast throws OrderBy was not applied <-- DOES NOT WORK!!!
    //    return query;
    //}
    page = (page < 1) ? 1 : page;
    var limit = (pageSize <= 0 || (total >= 0 && pageSize > total)) ? 50 : pageSize;
    var skip = (page - 1)*limit;

    return query.Skip(skip).Take(limit);
}

为了让事情更有趣,我使用 Mycrosoft 的动态表达式 API(又名动态 LINQ),所以我的调用代码看起来像

return query
         .OrderBy("Customer.LastName DESC, Customer.FirstName")
         .Paginate(1,25, 2345)
         .ToArray();

或者我可以使用像这样的强类型表达式来调用它

return query
        .OrderByDescending(c=>c.LastName)
        .ThenBy(c=>c.FirstName)
        .Paginate(1,25,2345)
        .ToArray();

这种检查可以吗?我厌倦了使用in 方法签名,但是当您使用 OrderBy(string expression) 时IOrderableQueryable<T>Dynamic Linq 不会返回,因此扩展将不适用...IOrderableQueryable

更新

使用建议(@GertArnold 指出)唯一可行的解​​决方案是检查表达式树。但是,我没有使用整个解决方案,而是ExpressionVisitor通过要求必须在OrderByor之后调用 Paginate 方法来简化我的解决方案OrderByDescending。这允许我只检查表达式树中的当前节点,而不是搜索整个树。所以这就是我所做的:

// snip....class level 
private static readonly string[] PaginationPrerequisiteMehods = new[] { "OrderBy", "OrderByDescending" };

// snip Paginate method
public static IQueryable<T> Paginate<T>(this IQueryable<T> query, int page, int pageSize = 50, int total = -1)
    {
        // require that either OrderBy or OrderByDescending was applied just before calling Paginate....
        if (query.Expression.NodeType != ExpressionType.Call)
        {
            //TODO: logging -> "You have to apply OrderBy() or OrderByDescending() just before calling Paginate()"
            return query;
        }
        var methodName = ((MethodCallExpression) query.Expression).Method.Name;
        if (!Array.Exists(PaginationPrerequisiteMehods, s => s.Equals(methodName, StringComparison.InvariantCulture)))
        {
            //TODO: logging -> "You have to apply OrderBy() or OrderByDescending() just before calling Paginate()"
            return query;
        }

        page = (page < 1) ? 1 : page;
        var limit = (pageSize <= 0 || (total >= 0 && pageSize > total)) ? 50 : pageSize;
        var skip = (page - 1)*limit;

        return query.Skip(skip).Take(limit);
    }
4

2 回答 2

2

您可以检查表达式本身,如此所述,或检查编译时类型,如此所述。我认为前者应该适合你,因为动态 linq 也会在表达式中添加OrderBy(or )。OrderByDescending

于 2012-06-05T20:39:34.710 回答
1

不知何故,您似乎正在尝试解决一些没有意义的问题。

您不想在使用您的方法时出现运行时异常。好的!但在这种情况下,不要使用 Dynamic Linq,因为这正是导致运行时异常并IOrderedQueryable用于您的Paginate方法的东西。

您想使用动态 Linq。好的!但是在这种情况下,您需要一些测试(我的意思是集成测试),它们将在运行时测试您的代码,并且您的Paginate方法应该以相同的方式进行测试。

在运行时探索表达式树仍然与运行时异常相同——它没有通知程序员对无序数据使用 Paginate 方法。

于 2012-06-05T20:43:52.160 回答