【问题标题】:net core Linq dynamic Sort [closed]net core Linq 动态排序
【发布时间】:2020-07-03 17:08:10
【问题描述】:

我有一项服务必须按升序或降序排序。为此,我收到两件事:排序的顺序,必须对其进行排序的列。 我尝试使用 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,...)动态调用正确的方法

【问题讨论】:

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


【解决方案1】:

这里有一些可以帮助你的东西。这将提供一些您可以在其他项目中用于动态排序的东西。

包含排序信息的类:

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

使用方法: 你传入一个 sortRequests 列表,每一个都会有你的属性的名称和排序的方向。

然后 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();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多