C#中LINQ动态OrderBy排序抛出InvalidOperationException异常
问题描述
在C#开发中使用LINQ实现泛型集合动态OrderBy排序功能时,触发System.InvalidOperationException异常,具体异常提示为:
((System.RuntimeType)type).DeclaringMethod' threw an exception of type 'System.InvalidOperationException'
触发异常的原始实现代码如下:
public static List<T> Sort<T>(SortingDto? request, List<T> entity) { if (request != null && request.SortKey != null) { var type = typeof(T); string? key = request.SortKey; var keyInfo = type.GetProperty(key); entity = request.SortOrder.ToLower() == Constants.Assending ? entity.OrderBy(x => keyInfo.GetValue(x, null)).ToList() : entity.OrderByDescending(x => keyInfo.GetValue(x, null)).ToList(); } return entity; }
异常根因
- 当排序逻辑用于
IQueryable场景(如EF Core数据库查询)时,传入OrderBy的lambda会被解析为表达式树,直接嵌入PropertyInfo.GetValue()反射调用生成的表达式节点不被标准查询提供程序支持,解析阶段访问运行时类型的DeclaringMethod属性时就会抛出无效操作异常。 - 纯内存集合(LINQ to Objects)场景下,该实现的lambda返回值为
object类型,值类型排序会出现逻辑错误(如数字按字符串规则比较),且缺少属性存在性校验,传入非法排序字段时会触发空引用异常。
正确实现方案
通过动态构建强类型属性访问表达式树实现排序,同时兼容内存集合、ORM数据库查询场景,修复后代码如下:
using System.Linq.Expressions; public static List<T> Sort<T>(SortingDto? request, List<T> entity) { // 参数合法性校验 if (request == null || string.IsNullOrWhiteSpace(request.SortKey)) { return entity; } var entityType = typeof(T); var sortProperty = entityType.GetProperty(request.SortKey); // 校验排序字段是否存在 if (sortProperty == null) { return entity; } // 构建x => x.SortKey 强类型lambda表达式 var parameterExpr = Expression.Parameter(entityType, "x"); var propertyAccessExpr = Expression.Property(parameterExpr, sortProperty); var sortLambda = Expression.Lambda( typeof(Func<,>).MakeGenericType(entityType, sortProperty.PropertyType), propertyAccessExpr, parameterExpr ); // 匹配排序方向对应的方法 var sortMethodName = request.SortOrder.ToLower() == Constants.Assending ? nameof(Enumerable.OrderBy) : nameof(Enumerable.OrderByDescending); var sortMethod = typeof(Enumerable) .GetMethods() .First(method => method.Name == sortMethodName && method.GetParameters().Length == 2) .MakeGenericMethod(entityType, sortProperty.PropertyType); // 执行排序并返回结果 var sortedResult = sortMethod.Invoke(null, new object[] { entity, sortLambda.Compile() }); return ((IEnumerable<T>)sortedResult).ToList(); }
适配说明
- 如果需要支持EF Core等ORM的
IQueryable查询场景,只需要将代码中Enumerable替换为Queryable,调用排序方法时去掉sortLambda.Compile(),直接传入sortLambda即可,生成的表达式树可被ORM直接翻译为数据库的ORDER BY语句。 - 如果需要支持嵌套属性排序(如
User.Address.City格式的SortKey),只需修改属性访问表达式构建逻辑,按.分割属性名逐层生成Expression.Property节点即可,核心排序逻辑无需调整。 - 如果需要支持多级排序,首次排序后可按相同逻辑调用
ThenBy/ThenByDescending方法实现后续排序规则追加。
内容的提问来源于stack exchange,提问作者Rohit Sharma
相关产品推荐
相关产品推荐

