【问题标题】:Is there a way to order by an IQueryable with a different amount of ordering有没有办法通过 IQueryable 订购不同数量的订购
【发布时间】:2014-06-06 00:08:34
【问题描述】:

例如,我想尝试这样的事情。排序标准可能没有、1 个或多个按不同顺序排列的列。但是我不能使用ThenBy 方法,因为它只能在OrderBy 之后使用。下面的代码将继续将顺序重置为排序列表中的最后一项。我也不想更改方法签名。非常感谢您的帮助,谢谢。

public IQueryable<Person> FilterList(string forename, List<Sorting> sorting)
{
    IQueryable<Person> query = dc.Set<Person>();

    if(!string.IsNullOrEmpty(forename)){
        query = query.Where(w=>w.Forename.Contains(forename));

    foreach(var sort in sorting)
    {
        switch(sort.By)
        {
            case "asc":
               switch(sort.Sort)
               {
                   case "forename":
                       query = query.OrderBy(o=>o.Forename);
                       break;

                   case "surname":
                       query = query.OrderBy(o=>o.Surname);
                       break;
               }
               break;

            case "desc":
               switch(sort.Sort)
               {
                   case "forename":
                       query = query.OrderByDescending(o=>o.Forename);
                       break;

                   case "surname":
                       query = query.OrderByDescending(o=>o.Surname);
                       break;
               }
               break;
        }
    }

    return query;
}

public class Sorting
{
    public string Sort{get;set;}
    public string By{get;set;}
}

【问题讨论】:

标签: c# linq entity-framework sql-order-by


【解决方案1】:

AgentFire 的回答实际上是适当的,但在每种情况下都有些冗长。我会添加一个扩展方法:

编辑:显然,下面的代码不能正确确定查询是否已经排序;显然,即使是 unordered 版本也实现了IOrderedQueryable&lt;T&gt;。使用source.Expression.Type 来检查IOrderedQueryable&lt;T&gt; 显然是可行的,但我把这段代码留在这里作为更逻辑 的方法——而且这个“修复”听起来比我想要的更脆弱用我的名字回答:)

public static IOrderedQueryable<T> AddOrdering<T, TKey>(
    this IQueryable<T> source,
    Expression<Func<T, TKey>> keySelector,
    bool descending)
{
    IOrderedQueryable<T> ordered = source as IOrderedQueryable<T>;
    // If it's not ordered yet, use OrderBy/OrderByDescending.
    if (ordered == null)
    {
        return descending ? source.OrderByDescending(keySelector)
                          : source.OrderBy(keySelector);
    }
    // Already ordered, so use ThenBy/ThenByDescending
    return descending ? ordered.ThenByDescending(keySelector)
                      : ordered.ThenBy(keySelector);
}

那么你的调用代码就变成了:

foreach(var sort in sorting)
{
    bool descending = sort.By == "desc";
    switch (sort.Sort)
    {
        case "forename":
            query = query.AddOrdering(o => o.Forename, descending);
            break;
        case "surname":
            query = query.AddOrdering(o => o.Surname, descending);
            break;
    }
}

这样每个额外的排序选项只会增加很小的代码开销。

【讨论】:

  • 当我至少在两个地方需要算法时,我会选择编写一个扩展。
  • @AgentFire:使用它的代码已经在同一个方法中的两个不同位置调用了它。我不清楚使用 Expression&lt;Func&lt;T, object&gt;&gt; 是否总是合适的 - 这取决于提供者翻译是否使用类型参数来确定如何处理它。
  • 嗨乔恩,代码不适合我。在扩展方法内部,有序变量永远不会为空,因此总是调用 ThenBy。有什么想法吗? :-D
  • @RobWhite:这表明它一开始就已经订购了……这很奇怪,但我不认为它会做错事。你得到了什么结果?
  • 它会中断,因为它从不进行第一次订购,尽管我现在使用您的解决方案和另一个无法找到的解决方案的组合来阻止它中断。它涉及检查源的 Expression.Type 以查看它是否为 IOrderedQueryable 而不是使用 as
【解决方案2】:

这是我正在使用、尝试和测试的代码。基于 Jon Skeets 的解决方案,感谢他,我只是为了我的目的进行了调整。

public static IOrderedQueryable<T> AddOrdering<T, TKey>(
this IQueryable<T> source,
Expression<Func<T, TKey>> keySelector,
bool descending)
{

// If it's not ordered yet, use OrderBy/OrderByDescending.
if(source.Expression.Type != typeof(IOrderedQueryable<T>))
{
    return descending ? source.OrderByDescending(keySelector)
                      : source.OrderBy(keySelector);
}
// Already ordered, so use ThenBy/ThenByDescending
return descending ? ((IOrderedQueryable<T>)source).ThenByDescending(keySelector)
                  : ((IOrderedQueryable<T>)source).ThenBy(keySelector);
}

那么你的调用代码就变成了:

foreach(var sort in sorting)
{
bool descending = sort.By == "desc";
switch (sort.Sort)
{
    case "forename":
        query = query.AddOrdering(o => o.Forename, descending);
        break;
    case "surname":
        query = query.AddOrdering(o => o.Surname, descending);
        break;
}
}

【讨论】:

  • 谢谢,@Rob 很好的回答。
【解决方案3】:

您可能想使用演员表:

var sortedQueryable = query as IOrderedQueryable<Person>;

if (sortedQueryable != null)
{
    query = sortedQueryable.ThenBy(o => o.Forename);
}
else
{
    query = query.OrderBy(o => o.Forename);
}

在每次迭代中都会这样做。


另外,我会将魔术字符串“asc”和“desc”更改为枚举(如enum SortMode),并将List&lt;Sorting&gt; 参数更改为:

List<Tuple<Expression<Func<Person, object>>, SortMode>> parameter;

因此,在您的foreach 循环中,您只需将if 用于SortMode 并将元组的Item2 传递给订购者。

【讨论】:

  • 我实际上使用的是枚举,但为了方便起见,我的示例是在字符串中。不过我很高兴你提到它,很高兴能像其他人一样了解你的编码。
  • AgentFire 为此欢呼。你的回答与我给出的例子很相配。不过,我必须将 Jon 的答案作为标记答案,因为这是我可以重复使用的东西。再次感谢。 +1
【解决方案4】:

首先按常量排序,这样您就有了IOrderedQueryable&lt;T&gt;,然后您可以通过ThenBy 方法进行所有自定义排序。

public IQueryable<Person> FilterList(string forename, List<Sorting> sorting) {
    IQueryable<Person> query = dc.Set<Person>();
    if(!string.IsNullOrEmpty(forename)){
        query = query.Where(w=>w.Forename.Contains(forename));

    var orderedQuery = query.OrderBy(x => 1);

    foreach(var sort in sorting) {
        switch(sort.By) {
            case "asc":
               switch(sort.Sort) {
                   case "forename":
                       orderedQuery = orderedQuery.ThenBy(o=>o.Forename);
                   break;
                   case "surname":
                       orderedQuery = orderedQuery.ThenBy(o=>o.Surname);
                   break;
               }
            break;
            case "desc":
               switch(sort.Sort) {
                   case "forename":
                       orderedQuery = orderedQuery.ThenByDescending(o=>o.Forename);
                   break;
                   case "surname":
                       orderedQuery = orderedQuery.ThenByDescending(o=>o.Surname);
                   break;
               }
            break;
        }
    }

    return orderedQuery;
}

【讨论】:

  • 不必要的订单“1”?
  • 是的,这仍然可能生成按单个数字排序的 SQL 代码。
【解决方案5】:

有很多选项可以做到这一点。例如:

bool isSorted = false;

foreach(var sort in sorting)
{
    switch(sort.By)
    {
        case "asc":

           switch(sort.Sort)
           {
               case "forename":
                   if (!isSorted)
                   {
                       query = query .OrderBy(o => o.Forename);
                       isSorted = true;
                   }
                   else
                   {
                       query = ((IOrderedQueryable<Person>)query).ThenBy(o => o.Forename);
                   }
               ...
           }
        break;
        case "desc":
               ...
    }
}

编辑

感谢AgentFire 指出我的错误。

【讨论】:

  • ThenBy 不会编译,因为它在 IQueryable&lt;T&gt; 中不存在。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-06
  • 2023-03-02
  • 1970-01-01
  • 2016-01-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多