Lambda表达式排序时关联类为Null的报错及System.Void类型问题求助
解决动态关联属性排序的空引用与Void类型错误
问题根源
- 空引用错误:当关联对象(如
Supplier)为null时,直接访问其属性(如Name)会触发空引用异常,原代码未对中间对象的null情况做安全处理。 - System.Void类型错误:原代码使用
Expression.IfThenElse构建分支逻辑,但这个方法返回的是无返回值的表达式(Void类型),而Queryable.OrderBy等方法要求传入返回可比较类型的Lambda表达式,因此泛型参数无法接受Void类型,导致报错。
修复方案
核心修改点:
- 用
Expression.Condition替代Expression.IfThenElse:前者返回带类型的结果表达式,符合排序方法的参数要求 - 逐层处理嵌套属性的null检查:遍历属性路径时,每一层都添加null判断,确保访问子属性前父对象不为null
- 统一处理不同类型的默认值:针对字符串、值类型等提供对应的默认值,避免排序时出现类型不匹配
修改后的完整函数代码:
Public Function CallOrderedQueryable(Of T)(query As IQueryable(Of T), methodName As String, propertyName As String) As IOrderedQueryable(Of T) Dim param = Expression.Parameter(GetType(T), "x") Dim Compar As Int64? Dim currentExpr As Expression = param ' 处理flag筛选逻辑 If propertyName.Contains("?") Then Dim parts = propertyName.Split("?"c) Compar = CInt(parts(1)) propertyName = parts(0) End If ' 逐层构建带null检查的属性访问表达式 For Each member In propertyName.Split("."c) ' 先获取当前成员的访问表达式 Dim memberExpr = Expression.PropertyOrField(currentExpr, member) ' 构建当前父对象的null判断 Dim nullCheck = Expression.Equal(currentExpr, Expression.Constant(Nothing, currentExpr.Type)) ' 根据成员类型设置默认值 Dim defaultValue As Expression If memberExpr.Type Is GetType(String) Then defaultValue = Expression.Constant(String.Empty, GetType(String)) ElseIf memberExpr.Type.IsValueType Then ' 值类型返回默认值(如Int32返回0,Nullable类型返回Nothing) defaultValue = Expression.Default(memberExpr.Type) Else ' 引用类型返回null defaultValue = Expression.Constant(Nothing, memberExpr.Type) End If ' 用Condition构建安全访问逻辑:父对象为null则返回默认值,否则访问成员 currentExpr = Expression.Condition(nullCheck, defaultValue, memberExpr) Next Dim condPart As Expression = currentExpr ' 处理flag位运算逻辑(原逻辑保留) If (condPart.Type.FullName.Contains("Int32") OrElse condPart.Type.FullName.Contains("Int64")) AndAlso Compar IsNot Nothing Then Dim toLong = Expression.Convert(condPart, GetType(Int64)) Dim flag = Expression.Convert(Expression.Constant(Compar), GetType(Int64)) condPart = Expression.Equal(Expression.And(toLong, flag), flag) End If ' 构建排序调用表达式 Return query.Provider.CreateQuery(Of T)( Expression.Call( GetType(Queryable), methodName, New Type() {GetType(T), condPart.Type}, query.Expression, Expression.Lambda(condPart, param) ) ) End Function
关键修改说明
- 逐层null检查:遍历属性路径(如
Article.Supplier.Name)时,每一层都会判断当前对象是否为null,比如先检查Article.Supplier是否为null,是则返回默认值,否则继续访问Supplier.Name,彻底避免空引用。 - Expression.Condition的使用:这个方法会返回与分支结果同类型的表达式,比如字符串类型返回空字符串或实际Name值,确保最终的
condPart类型始终是可用于排序的有效类型(不会出现Void)。 - 默认值适配:针对不同类型(字符串、值类型、引用类型)设置对应的默认值,保证排序逻辑的一致性,比如值类型返回默认值,字符串返回空字符串,引用类型返回null。
使用验证
当你用Article.Supplier.Name作为排序字段时:
- 如果
Supplier为null,会自动使用空字符串作为排序值,不会触发空引用异常 - 最终传入排序方法的Lambda表达式返回的是字符串类型,符合泛型参数要求,不会再出现System.Void类型错误
内容的提问来源于stack exchange,提问作者YannickIngenierie
相关产品推荐
相关产品推荐

