2016-01-22 59 views
4

我返回一个IOrderedQueryable<T>像这样:为什么我不能在IOrderedQueryable上调用Skip?

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; 
     } 
} 

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

{"The method 'Skip' is only supported for sorted input in LINQ to Entities. The method 'OrderBy' must be called before the method 'Skip'."}

注意,目前有发生没有各种各样;然而,ApplySorts<T>()的返回值还应该是返回Skip()所期望的类型,否?

回答

8

您需要默认排序。虽然在大多数数据库中,你可以做TOP,LIMITBETWEEN没有排序,它不真的有道理(前两个实际上适用于Take,但理论是相同的)。

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

为了进一步解释:Skip(10)不无Order是有意义的,它是功能上等同于不存在。如果我们没有订购,我们是否跳过10行是无关紧要的。如果你洗牌一套,丢弃前10名,然后拿5分,这与仅仅丢弃5分是否有区别?在这两种情况下,你会得到5张随机卡片 - 丢弃是无关紧要的。

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

+1

这是我怀疑过。我希望有一种方法可以在不知道实体密钥的情况下应用这种方法。你知道吗?可能类似'var key = t.GetProperties()。其中​​(prop => Attribute.IsDefined(prop,typeof(Key)));' – user1477388

+1

@ user1477388没有反射(或者让所有实体都继承一个暴露主键),我不认为这是可能的。你可以尝试'.OrderBy(i => 1)',但即使这样做,它会引发关于“Skip”和“Take”是否合理的问题。在我看来,你是分页数据,所以请求*应该*提供一个排序。否则,您的分页功能将无法正常工作。 – Rob

+0

@ user1477388是的,如果你的属性定义了属性,那肯定有可能 – Rob

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; 
} 
+1

这只是简单地抑制SQL给你的*完全有效的错误*,告诉你跳过无序数据不是一个明智的操作(因为它不是)。提供不实际排序数据的排序可防止出现错误*而不修复问题*。 – Servy

+0

@Servy好点;感谢您的答复。用工作代码更新以通过反射获得'T'的密钥并传递给动态'OrderBy()'。我应该让它变得安全...... – user1477388

+0

顺便说一下,你正在使用的'OrderBy'方法是什么 - 显然不是标准方法?看起来像'System.Linq.Dynamic'中的一个,但是你错误地使用了它,它只会按** last **字段排序。 –

相关问题