You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Lambda表达式中获取属性名以消除IQueryable排序代码冗余

核心实现思路

IQueryable的排序方法要求传入强类型的表达式树Expression<Func<TSource, TKey>>,直接在Lambda中写反射逻辑无法被EF等查询提供者解析,也无法动态适配不同属性的返回类型,所以需要通过动态构建表达式树的方式实现通用排序。

针对当前场景的改造代码

using System; 
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace TestRedundance
{
    public class EntityRedundance
    {
        public class Entity
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public string Description { get; set; }
            public DateTime ValidFrom { get; set; }
        }

        private void AddSorting(IQueryable<Entity> query, List<string> sortingColumns, bool orderDescending)
        {
            if (!sortingColumns.Any())
            {
                query = query.OrderBy(x => x.Name);
                return;
            }

            // 区分首次排序和后续排序,修复原代码多列排序仅最后一列生效的问题
            bool isFirstSort = true;
            foreach (var column in sortingColumns)
            {
                // 校验属性是否存在,避免非法列名报错
                PropertyInfo prop = typeof(Entity).GetProperty(column, 
                    BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                if (prop == null)
                {
                    // 可根据业务需求调整为抛异常或走默认逻辑
                    continue;
                }

                // 动态构建排序表达式 x => x.[属性名]
                ParameterExpression param = Expression.Parameter(typeof(Entity), "x");
                MemberExpression propAccess = Expression.MakeMemberAccess(param, prop);
                LambdaExpression orderExpr = Expression.Lambda(propAccess, param);

                // 选择对应的排序方法
                string sortMethod = isFirstSort 
                    ? (orderDescending ? "OrderByDescending" : "OrderBy") 
                    : (orderDescending ? "ThenByDescending" : "ThenBy");

                // 反射调用排序方法
                MethodInfo method = typeof(Queryable).GetMethods()
                    .First(m => m.Name == sortMethod && m.GetParameters().Length == 2)
                    .MakeGenericMethod(typeof(Entity), prop.PropertyType);

                query = (IQueryable<Entity>)method.Invoke(null, new object[] { query, orderExpr });
                isFirstSort = false;
            }
        }
    }
}

全项目通用泛型扩展方案

可以把排序逻辑抽成通用扩展方法,给任意实体类使用,不需要重复编写排序逻辑:

public static class QueryableSortExtensions
{
    public static IQueryable<T> AddDynamicSort<T>(this IQueryable<T> query, 
        List<string> sortColumns, bool isDescending, string defaultSortColumn = "Id")
    {
        if (!sortColumns.Any())
        {
            // 处理默认排序逻辑
            var defaultProp = typeof(T).GetProperty(defaultSortColumn, 
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
            if (defaultProp == null) return query;
            
            var param = Expression.Parameter(typeof(T), "x");
            var propAccess = Expression.MakeMemberAccess(param, defaultProp);
            var lambda = Expression.Lambda<Func<T, object>>(Expression.Convert(propAccess, typeof(object)), param);
            
            return isDescending ? query.OrderByDescending(lambda) : query.OrderBy(lambda);
        }

        bool isFirstSort = true;
        foreach (var column in sortColumns)
        {
            PropertyInfo prop = typeof(T).GetProperty(column, 
                BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
            if (prop == null) continue;

            ParameterExpression param = Expression.Parameter(typeof(T), "x");
            MemberExpression propAccess = Expression.MakeMemberAccess(param, prop);
            LambdaExpression orderExpr = Expression.Lambda(propAccess, param);

            string sortMethod = isFirstSort 
                ? (isDescending ? "OrderByDescending" : "OrderBy") 
                : (isDescending ? "ThenByDescending" : "ThenBy");

            MethodInfo method = typeof(Queryable).GetMethods()
                .First(m => m.Name == sortMethod && m.GetParameters().Length == 2)
                .MakeGenericMethod(typeof(T), prop.PropertyType);

            query = (IQueryable<T>)method.Invoke(null, new object[] { query, orderExpr });
            isFirstSort = false;
        }
        return query;
    }
}

调用方式非常简单:

query = query.AddDynamicSort(sortingColumns, orderDescending, "Name");

优化点说明

  • 完全消除了switch分支冗余,后续新增实体属性不需要修改排序逻辑
  • 修复了原代码多列排序仅最后一列生效的问题,区分OrderBy和ThenBy实现多字段排序
  • 增加了属性合法性校验,避免传入非法列名导致程序崩溃
  • 泛型扩展方案支持所有实体类复用,进一步减少重复代码

内容的提问来源于stack exchange,提问作者Guilherme Alves

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 11:36:03