净核心Linq动态排序

时间:2020-07-03 17:08:10

标签: c# linq sorting asp.net-core core

我有一项服务,必须按升序或降序排序。为此,我收到两件事:排序顺序,必须在其上进行排序的列。 我尝试使用linq来实现这一点,以便可以直接使用EF Core对BD查询进行排序 警告:我可以对其进行排序的列可能包含子对象:property1.subProperty.subProperty

我发现了这个:

public static class IQueryableExtensions
    {
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
        {
            return CallOrderedQueryable(query, "OrderBy", propertyName, comparer);
        }

        public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
        {
            return CallOrderedQueryable(query, "OrderByDescending", propertyName, comparer);
        }

        public static IOrderedQueryable<T> ThenBy<T>(this IOrderedQueryable<T> query, string propertyName, IComparer<object> comparer = null)
        {
            return CallOrderedQueryable(query, "ThenBy", propertyName, comparer);
        }

        public static IOrderedQueryable<T> ThenByDescending<T>(this IOrderedQueryable<T> query, string propertyName, IComparer<object> comparer = null)
        {
            return CallOrderedQueryable(query, "ThenByDescending", propertyName, comparer);
        }

        /// <summary>
        /// Builds the Queryable functions using a TSource property name.
        /// </summary>
        public static IOrderedQueryable<T> CallOrderedQueryable<T>(this IQueryable<T> query, string methodName, string propertyName,
                IComparer<object> comparer = null)
        {
            var param = Expression.Parameter(typeof(T), "x");

            var body = propertyName.Split('.').Aggregate<string, Expression>(param, Expression.PropertyOrField);

            return comparer != null
                ? (IOrderedQueryable<T>)query.Provider.CreateQuery(
                    Expression.Call(
                        typeof(Queryable),
                        methodName,
                        new[] { typeof(T), body.Type },
                        query.Expression,
                        Expression.Lambda(body, param),
                        Expression.Constant(comparer)
                    )
                )
                : (IOrderedQueryable<T>)query.Provider.CreateQuery(
                    Expression.Call(
                        typeof(Queryable),
                        methodName,
                        new[] { typeof(T), body.Type },
                        query.Expression,
                        Expression.Lambda(body, param)
                    )
                );
        }
    }

但是我想根据排序值(ASC或DESC)和属性值(property1或property1.subproperty或property1.subproperty.subsubproperty,...)动态调用正确的方法

1 个答案:

答案 0 :(得分:0)

这可能会对您有所帮助。这也将为您提供可用于其他项目中动态排序的内容。

用于包含排序信息的类:

public class SortRequest
{
   /// <summary>
   /// What field/column to sort by
   /// </summary>
   public string Field { get; set; }

   /// <summary>
   /// Whether to sort by descending or not.
   /// Defaults to false
   /// </summary>
   public bool SortDescending { get; set; } = false;
 }//Cls

进行排序的类

 public static class SortingExtensions
  {

        //------------------------------------------------------------------------//

        /// <summary>
        /// Sorts <paramref name="list"/> according to the details in <paramref name="sortRequestList"/>
        /// </summary>
        /// <typeparam name="T">The type of objects being sorted</typeparam>
        /// <param name="list">The items to sort</param>
        /// <param name="sortRequestList">List of fields to sort by</param>
        /// <param name="getPropertySelectorLambda">Function to convert string field to a T property</param>
        /// <returns>The same list with sorting appended to it</returns>
        public static IEnumerable<T> AddSorting<T>(this IEnumerable<T> list, IEnumerable<SortRequest> sortRequestList, Func<string, Func<T, object>> getPropertySelectorLambda)
        {

           if (sortRequestList == null || !sortRequestList.Any() || list == null || !list.Any())
              return list;

           var isFirst = true;

           foreach (var sr in sortRequestList)
           {

              Func<T, object> lmda = getPropertySelectorLambda(sr.Field);

              list = list.AddSortLevel(sr.SortDescending, lmda, isFirst);


              isFirst = false;

           }//foreach

           return list;

        }//SortProducts

        //------------------------------------------------------------------------//

        /// <summary>
        /// Adds a level of sorting to the linq queue
        /// </summary>
        /// <typeparam name="T">Type of objects being sorted</typeparam>
        /// <param name="list">The Items being sorted</param>
        /// <param name="sortDescending">What direction to sort</param>
        /// <param name="propertySelectorLambda">Function dexcribing what property to sort on.</param>
        /// <param name="isFirst">Is this the first item in the LINQ sort queue</param>
        /// <returns>The same list with sorting appended to it</returns>
        private static IOrderedEnumerable<T> AddSortLevel<T>(this IEnumerable<T> list, bool sortDescending, Func<T, object> propertySelectorLambda, bool isFirst)
        {
           
           if (isFirst)
              list = FirstSortLevel(list, sortDescending, propertySelectorLambda);
           else
              list = NextSortLevel(list as IOrderedEnumerable<T>, sortDescending, propertySelectorLambda);

           //Will definitely be IOrderedEnumerable here.
           return list as IOrderedEnumerable<T>;

        }//FirstSortLevel

        //------------------------------------------------------------------------//

        /// <summary>
        /// Adds a level of sorting to the linq queue (OrderBy)
        /// </summary>
        /// <typeparam name="T">Type of objects being sorted</typeparam>
        /// <param name="list">The Items being sorted</param>
        /// <param name="sortDescending">What direction to sort</param>
        /// <param name="propertySelectorLambda">Function dexcribing what property to sort on.</param>
        /// <param name="isFirst">Is this the first item in the LINQ sort queue</param>
        /// <returns>The same list with sorting appended to it</returns>
        private static IOrderedEnumerable<T> FirstSortLevel<T>(this IEnumerable<T> list, bool sortDescending, Func<T, object> propertySelectorLambda)
        {
           if (sortDescending)
              list = list.OrderByDescending(propertySelectorLambda);
           else
              list = list.OrderBy(propertySelectorLambda);

           //Will definitely be IOrderedEnumerable here.
           return list as IOrderedEnumerable<T>;
        }//FirstSortLevel

        //------------------------------------------------------------------------//

        /// <summary>
        /// Adds a level of sorting to the linq queue (ThenBy)
        /// </summary>
        /// <typeparam name="T">Type of objects being sorted</typeparam>
        /// <param name="list">The Items being sorted</param>
        /// <param name="sortDescending">What direction to sort</param>
        /// <param name="propertySelectorLambda">Function dexcribing what property to sort on.</param>
        /// <param name="isFirst">Is this the first item in the LINQ sort queue</param>
        /// <returns>The same list with sorting appended to it</returns>
        private static IOrderedEnumerable<T> NextSortLevel<T>(this IOrderedEnumerable<T> list, bool sortDescending, Func<T, object> propertySelectorLambda)
        {
           if (sortDescending)
              list = list.ThenByDescending(propertySelectorLambda);
           else
              list = list.ThenBy(propertySelectorLambda);

           return list;
        }//NextSortLevel

        //------------------------------------------------------------------------//

  }//Cls

如何使用它: 您传递了sortRequest列表,每个列表都有您的属性名称和排序方向。

然后,getPropertySelectorLambda将为您提供排序中使用的表达式/函数。

以下是我使用的示例:

  private Func<Nurse, object> GetSortingPropertySelectorLambda(string field)
  {

     return (field.CamelToPascal()) switch
     {
        nameof(Nurse.Id) => p => p.Id,
        nameof(Nurse.PinNumber) => p => p.PinNumber,
        nameof(Nurse.FirstName) => p => p.FirstName,
        nameof(Nurse.LastName) => p => p.LastName,
        nameof(Nurse.AddressLine1) => p => p.AddressLine1,
        nameof(Nurse.AddressLine2) => p => p.AddressLine2,
        nameof(Nurse.AddressLine3) => p => p.AddressLine3,
        nameof(Nurse.AddressLine4) => p => p.AddressLine4,
        nameof(Nurse.PhoneNumber) => p => p.PhoneNumber,
        nameof(Nurse.PhoneNumber2) => p => p.PhoneNumber2,
        nameof(Nurse.EmailAddress) => p => p.EmailAddress,
        nameof(Nurse.DateOfBirth) => p => p.DateOfBirth,
        nameof(Nurse.PayrollNumber) => p => p.PayrollNumber,
        nameof(Nurse.Branch) => p => p.Branch.Name,
        _ => p => p.Id,
     };

  }//GetSortingPropertySelectorLambda

您也可以根据sortRequest自己构建该表达式(就像您在上面所做的一样)。

之所以拥有“ FirstSortLevel”方法,是因为第一个排序必须为OrderBy,但随后的所有排序都必须为“ ThenBy”。

所以我们要做的就是创建/获取表达式,该表达式决定要过滤的属性。然后只需使用常规的LINQ进行排序(我们不会将这些查询构建为表达式)。

示例用法:

var sortedData = allData
    .AddSorting(sortList, GetSortingPropertySelectorLambda)
    .ToList();