C# 实体框架&x27;的导航属性在按nullable排序后为null
我使用下面的代码来转换order by表达式,以便也可以对可为空的列进行排序C# 实体框架&x27;的导航属性在按nullable排序后为null,c#,entity-framework,sorting,lambda,expression,C#,Entity Framework,Sorting,Lambda,Expression,我使用下面的代码来转换order by表达式,以便也可以对可为空的列进行排序 protected virtual Expression<Func<T, object>> GetSorting(string ordering) { Expression<Func<T, object>> expression = default(Expression<Func<T, object>>); IEnu
protected virtual Expression<Func<T, object>> GetSorting(string ordering)
{
Expression<Func<T, object>> expression = default(Expression<Func<T, object>>);
IEnumerable<Order> sortObjects = string.IsNullOrEmpty(ordering) ? null : JsonConvert.DeserializeObject<IEnumerable<Order>>(ordering);
if (sortObjects != null)
{
foreach (Order sortObject in sortObjects)
{
Expression<Func<T, object>> currentExpression = this.GetExpression(sortObject.Property);
expression = this.CombineExpressions(expression, currentExpression);
}
}
return expression;
}
private Expression<Func<T, object>> GetExpression(string propertyName)
{
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "x");
MemberExpression propertyReference = Expression.Property(parameter, propertyName);
Expression conversion = Expression.Convert(propertyReference, typeof(object));
Expression<Func<T, object>> currentExpression = Expression.Lambda<Func<T, object>>(conversion, new[] { parameter });
return currentExpression;
}
private Expression<Func<T, object>> CombineExpressions(Expression<Func<T, object>> expression, Expression<Func<T, object>> currentExpression)
{
if (expression == default(Expression<Func<T, object>>))
{
expression = currentExpression;
}
else
{
// Combine the two expressions' body together
BinaryExpression body = Expression.AndAlso(expression.Body, currentExpression.Body);
ParameterExpression[] parameters = new ParameterExpression[1] { Expression.Parameter(typeof(T), expression.Parameters.First().Name) };
// Convert the BinaryExpression to the requested type
Expression<Func<T, object>> lambda = Expression.Lambda<Func<T, object>>(body, parameters);
expression = lambda;
}
return expression;
}
受保护的虚拟表达式GetSorting(字符串排序)
{
表达式=默认值(表达式);
IEnumerable sortObjects=string.IsNullOrEmpty(排序)?null:JsonConvert.DeserializeObject(排序);
if(排序对象!=null)
{
foreach(订单sortObject in sortObject)
{
表达式currentExpression=this.GetExpression(sortObject.Property);
expression=this.CombineExpressions(expression,currentExpression);
}
}
返回表达式;
}
私有表达式GetExpression(字符串属性名称)
{
类型=类型(T);
ParameterExpression参数=表达式参数(类型为“x”);
MemberExpression propertyReference=Expression.Property(参数,propertyName);
表达式转换=Expression.Convert(propertyReference,typeof(object));
表达式currentExpression=Expression.Lambda(转换,新[]{parameter});
返回电流表达式;
}
专用表达式组合表达式(表达式表达式,表达式currentExpression)
{
if(表达式==默认值(表达式))
{
表达式=当前表达式;
}
其他的
{
//将两个表达式的主体组合在一起
BinaryExpression body=Expression.AndAlso(Expression.body,currentExpression.body);
ParameterExpression[]parameters=新的ParameterExpression[1]{Expression.Parameter(typeof(T),Expression.parameters.First().Name)};
//将BinaryExpression转换为请求的类型
表达式lambda=表达式.lambda(主体,参数);
表达式=λ;
}
返回表达式;
}
此代码适用于所有非导航属性,但似乎不再查询导航属性。我使用Select表达式加载导航属性,如下所示:
protected override Expression<Func<Resource, ResourceViewModel>> Selector
{
get
{
return (x) => new ResourceViewModel()
{
ResourceId = x.ResourceId,
DisplayName = x.DisplayName,
ResourceType = x.ResourceType != null ? x.ResourceType.Name : string.Empty,
}
}
}
受保护的重写表达式选择器
{
得到
{
return(x)=>newResourceViewModel()
{
ResourceId=x.ResourceId,
DisplayName=x.DisplayName,
ResourceType=x.ResourceType!=null?x.ResourceType.Name:string.Empty,
}
}
}
如果我没有任何订购依据,则会加载导航属性。但只要有任何东西需要订购,导航属性就为空。如果我跳过三元操作,直接转到ResourceType.Name属性,我会得到一个异常,告诉我lambda_方法抛出了一个NullReference异常
我知道订购导航属性本身不起作用,但这不是问题所在。按“常规”属性排序会导致此问题
对此有什么想法吗?原来问题并不像我想的那么复杂。问题是我创建了错误的表达式树。可以嵌套表达式,以便导航到属性的属性 下面的解决方案应该可以解释这一点(这不是我实际解决问题的方式,但应该清楚):
私有表达式GetExpression(字符串父类,字符串属性名称)
{
类型=类型(T);
ParameterExpression参数=表达式参数(类型为“x”);
//获取父类表达式
//将导致(x)=>x.myNavigationPropertyHisaClass
MemberExpression propertyReference1=Expression.Property(参数,父类);
//导航到导航属性类的属性
//将导致(x)=>x.MyNavigationPropertyHichisaClass.MyPropertyIWantToSort
MemberExpression propertyReference2=Expression.Property(PropertyReference1,propertyName);
表达式转换=Expression.Convert(propertyReference2,typeof(object));
表达式currentExpression=Expression.Lambda(转换,新[]{parameter});
返回电流表达式;
}
让我们从这里开始。Order by表达式不能像谓词一样组合,但需要与OrderBy/ThenBy
链接。这意味着您需要一种不同的方法-IQueryable
扩展方法,而不是表达式返回方法。在应用order by表达式时,不需要将它们转换为类型object
。我同意您的第一句话,但您的第二种方法似乎不正确。您需要为订购定义TKey类型。因为我的场景必须能够接受任何类型,所以我只使用了object。但这两种说法都与我的问题无关。我已经为这个问题创建了一个解决方案,但我仍然很好奇这是否可能。问题是,表达式
与表达式不同(不能转换为)表达式
。另外,当您使用类似这样的Expression>e=x=>x.IntProperty
时,生成的表达式体不是从上面可以预期的成员访问,而是Convert
。如果很容易忽略,OrderBy/ThenBy
为什么要声明第二个泛型参数呢?这是有道理的,但是排序确实是这样工作的,特别是因为我想进行转换(因为可以为null的属性)。但也许这就是为什么导航属性不再包含在流程中的原因?如果您提供一个小而完整的示例,会更容易。当您手动构建查询时,它被转换为SQL,在该上下文中不需要特殊的null
处理,因为数据库自然地支持null
(在orderby
、select
等中),并且在该上下文中没有真正的导航属性“load”。如果您从sele获得NRE
private Expression<Func<T, object>> GetExpression(string parentClass, string propertyName)
{
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "x");
// Get parent class expression
// Will result in (x) => x.MyNavigationPropertyWhichIsAClass
MemberExpression propertyReference1 = Expression.Property(parameter, parentclass);
// Navigate to the property of the navigation property class
// Will result in (x) => x.MyNavigationPropertyWhichIsAClass.MyPropertyIWantToSort
MemberExpression propertyReference2 = Expression.Property(propertyRefernce1, propertyName);
Expression conversion = Expression.Convert(propertyReference2, typeof(object));
Expression<Func<T, object>> currentExpression = Expression.Lambda<Func<T, object>>(conversion, new[] { parameter });
return currentExpression;
}