Linq中的动态订单

时间:2017-08-18 16:40:28

标签: c# linq

我有一个访问数据库的应用程序,必须根据输入按不同的字段对结果进行排序。

这是我的排序功能:

IQueryable<Entity> GetSortedData(IQueryable<Entity> result, String orderby, bool desc)
{
   switch (orderby.ToLower())
   {
     case "id":
       result = result.OrderBy(c => c.Id);
       break;

     case "code":
       result = result.OrderBy(c => c.Code);
       break;

     case "active":
       result = result.OrderBy(c => c.Active);
       break;

     default:
       result = result.OrderBy(c => c.Name);
       break;
   }

   if (pageData.SortDesc)
   {
     var res = result.ToList();
     res.Reverse();
     return res.AsQueryable();
   }

   return result;      
}

此代码存在一些我不喜欢的问题:

  1. 重复的代码太多了。如果它是纯粹的SQL&#34;查询,它看起来像

    SELECT * FROM data_table   订购    CASE @OrderBy      什么时候&#39; id&#39;那么      什么时候编码&#39;然后代码
         什么时候活跃&#39;那么活跃      ELSE名称
       结束

  2. 转换为列表并返回以进行倒车。我无法更改返回类型,我绝对不想编写更多无用的代码(基本上用switch加倍case OrderByDescending

  3. 有人可以提出一些方法来使这个功能更好看,最好还是使用LINQ吗?

5 个答案:

答案 0 :(得分:10)

嗯,你肯定想要使用OrderByDescending而不是倒车。它不会像SQL一样简短,但你至少可以使用:

IQueryable<Entity> GetSortedData(IQueryable<Entity> result, String orderby, bool desc)
{
   switch (orderby.ToLowerInvariant())
   {
     case "id":
         return desc ? result.OrderByDescending(c => c.Id) : result.OrderBy(c => c.Id);
     case "code":
         return desc ? result.OrderByDescending(c => c.Code) : result.OrderBy(c => c.Code);
     case "active":
         return desc ? result.OrderByDescending(c => c.Active) : result.OrderBy(c => c.Active);
     default:
         return desc ? result.OrderByDescending(c => c.Name) : result.OrderBy(c => c.Name);
   }
}

您可以使用自己的扩展方法删除重复:

public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, TKey>> keySelector,
    bool descending) =>
    descending ? source.OrderByDescending(keySelector) : source.OrderBy(keySelector);

然后写:

IQueryable<Entity> GetSortedData(IQueryable<Entity> result, String orderby, bool desc)
{
   switch (orderby.ToLowerInvariant())
   {
     case "id": return result.OrderBy(c => c.Id, desc);
     case "code": return result.OrderBy(c => c.Code, desc);
     case "active": return result.OrderBy(c => c.Active, desc);
     default: return result.OrderBy(c => c.Name, desc);
   }
}

答案 1 :(得分:1)

您可以使用IQueryable的此扩展程序:

using System.Linq.Dynamic;

   public static class IQuerableExtensions
    {
        public static IQueryable<T> ApplySort<T>(this IQueryable<T> source, string sort)
        {
            if(source == null)
            {
                throw new ArgumentNullException("source");
            }

            if(sort == null)
            {
                return source;
            }

            var lsSort = sort.Split(',');

            // run through the sorting options and create a sort expression string from them
            string completeSortExpression = "";
            foreach (var sortOption in lsSort)
            {
                // if the sort option starts with "-", we order descending, otherwise ascending
                if(sortOption.StartsWith("-"))
                {
                    completeSortExpression = completeSortExpression + sortOption.Remove(0, 1) + " descending,";
                }
                else
                {
                    completeSortExpression = completeSortExpression + sortOption + ",";
                }
            }

            if (!string.IsNullOrWhiteSpace(completeSortExpression))
            {
                source = source.OrderBy(completeSortExpression.Remove(completeSortExpression.Count() - 1));
            }

            return source;
        }
    }

要使用它,只需输入:

yourList.AsQueryable().ApplySort("thefieldName")

如果在fieldName之前加上 - ,那么order by将会降序。

yourList.AsQueryable().ApplySort("-thefieldName")

答案 2 :(得分:1)

// this is will work with any class
IQueryable<Entity> GetSortedData(
        IQueryable<Entity> result, String orderby, bool desc)
{
    return result.OrderBy(orderby, desc);
}


// custom order by extension method
// which will work with any class
public static class QueryableHelper
{

   public static IQueryable<TModel> OrderBy<TModel>
     (this IQueryable<TModel> q, string name, bool desc)
   {
      Type entityType = typeof(TModel);
      PropertyInfo p = entityType.GetProperty(name);
      MethodInfo m = typeof(QueryableHelper)
       .GetMethod("OrderByProperty")
       .MakeGenericMethod(entityType, p.PropertyType);
      return(IQueryable<TModel>) m.Invoke(null, new object[] { 
         q, p , desc });
   }


    public static IQueryable<TModel> OrderByProperty<TModel, TRet>
       (IQueryable<TModel> q, PropertyInfo p, bool desc)
    {
        ParameterExpression pe = Expression.Parameter(typeof(TModel));
        Expression se = Expression.Convert(Expression.Property(pe, p), typeof(object));
        var exp = Expression.Lambda<Func<TModel, TRet>>(se, pe);
        return desc ? q.OrderByDescending(exp) : q.OrderBy(exp);
    }

}

答案 3 :(得分:0)

虽然我更喜欢@Jon Skeet提供的强类型方法,但如果你仍然希望以字符串形式进行,你可能会做这样的事情:

public static IQueryable<Entity> Sort(this IQueryable<Entity> collection, string sortBy, bool desc = false)
{
    return collection.OrderBy(sortBy + (desc ? " descending" : ""));
}

这需要动态LINQ库,您可以通过

安装
install-package System.Linq.Dynamic

答案 4 :(得分:0)

我不确定String orderby的来源是什么,但如果这是您在代码中指定的内容,那么最好只指定delegate而不是字符串。这样,您的代码将如下所示

IQueryable<Entity> GetSortedData(IQueryable<Entity> result, Func orderSelector, bool desc) {
    return result.OrderBy(orderSelector, desc);
}

就像Jon建议的那样,你可以使用扩展来简化升序和降序。

public static IOrderedQueryable<TSource> OrderBy<TSource, TKey>(
    this IQueryable<TSource> source,
    Expression<Func<TSource, TKey>> keySelector,
    bool desc) =>
    desc ? source.OrderByDescending(keySelector) : source.OrderBy(keySelector);

所以,如果你的电话看起来像这样

GetSortedData(result, "Id", true)

现在看起来应该是这样的

GetSortedData(result, (e) => e.Id, true)

PS:有时,从UI控件中选择要排序的字段。在这种情况下,您可以使用他们的.Tag或类似属性来存储代表。