为什么我不能在IOrderedQueryable上调用Skip?

时间:2016-01-22 17:08:44

标签: c# entity-framework linq

我正如此返回IOrderedQueryable<T>

response.Data = Expressions
    .ApplySorts(request, result)
    .Skip(request.Skip)
    .Take(request.Take)
    .ToList();

这是我的类,其中包含一个返回IOrderedQueryable<T>的方法。

public static class Expressions
{
    public static IOrderedQueryable<T> ApplySorts<T>(
        DataRequest request, IQueryable<T> items)
        {
            var sorts = request.Sort;

            if (sorts != null)
            {
                foreach (var sort in sorts)
                {
                    items = items.OrderBy(sort.Field + " " + sort.Dir);
                }
            }

            return (IOrderedQueryable<T>)items;
        }
}

但是在运行时我得到了这个错误:

  

{&#34;方法&#39; Skip&#39;只支持LINQ to中的排序输入   实体。方法&#39; OrderBy&#39;必须在方法之前调用   &#39;跳过&#39;&#34;}

请注意,目前没有任何种类;但是,ApplySorts<T>()的返回值仍应返回Skip()期望的类型,不是吗?

2 个答案:

答案 0 :(得分:8)

您需要默认排序。虽然在大多数数据库中,您可以执行TOPLIMITBETWEEN而不进行排序,但确实没有意义(前两个实际适用于{ {1}},但理论是一样的。)

返回行的顺序是 undefined (即使它们通常按插入顺序显示)。因此,您的ORM阻止您在代码中创建错误(即Take不一致,并且没有真正有意义)。

进一步解释:Skip没有Skip(10)没有意义,它在功能上等同于不存在。如果我们没有订购,我们是否跳过10行是无关紧要的。如果你洗牌一副牌,丢弃前10名,然后拿5,是不是只是简单地拿5而不丢弃?在这两种情况下,你都会得到5张随机牌 - 弃牌是无关紧要的。

如果没有提供排序,您应该有一个默认的排序案例。合适的候选人是表的主键。

答案 1 :(得分:0)

使用反射获取T的键将作为默认排序参数传递,因为SQL不会提供其他方法:

public static IOrderedQueryable<T> ApplySorts<T>(DataRequest request, IQueryable<T> items)
{
    var sorts = request.Sort;

    var keyProperty = typeof(T).GetProperties().FirstOrDefault(prop => 
        prop.IsDefined(typeof(System.ComponentModel.DataAnnotations.KeyAttribute), false));

    if (sorts != null)
    {
        foreach (var sort in sorts)
        {
            items = items.OrderBy(sort.Field + " " + sort.Dir);
        }
    }
    else
    {
        items = items.OrderBy(keyProperty.Name); // set a default sort if there are no sorts provided
    }

    return (IOrderedQueryable<T>)items;
}