Entity Framework 的导航属性在按可为空排序后为空
Entity Framework's navigation properties are null after sort by nullable
我使用以下代码按表达式转换顺序,因此也可以对可为空的列进行排序。
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;
}
此代码适用于所有非导航属性,但似乎不再查询导航属性。我使用 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,
}
}
}
如果我没有任何可订购的东西,则会加载导航属性。但是只要有任何东西可以订购,导航 属性 就是空的。如果我跳过三元运算并直接转到 ResourceType.Name 属性,我会得到一个异常,告诉我 lambda_method 抛出了 NullReference 异常。
我知道排序导航属性不能正常工作,但这不是问题所在。按 'regular' 属性排序会导致问题。
对此有什么想法吗?
事实证明,问题并没有我想象的那么复杂。问题是我创建了错误的表达式树。您可以嵌套表达式,以便导航到 属性.
的 属性
下面的解决方案应该可以解释这一点(这不是我实际解决问题的方式,但应该清楚):
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;
}
我使用以下代码按表达式转换顺序,因此也可以对可为空的列进行排序。
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;
}
此代码适用于所有非导航属性,但似乎不再查询导航属性。我使用 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,
}
}
}
如果我没有任何可订购的东西,则会加载导航属性。但是只要有任何东西可以订购,导航 属性 就是空的。如果我跳过三元运算并直接转到 ResourceType.Name 属性,我会得到一个异常,告诉我 lambda_method 抛出了 NullReference 异常。
我知道排序导航属性不能正常工作,但这不是问题所在。按 'regular' 属性排序会导致问题。
对此有什么想法吗?
事实证明,问题并没有我想象的那么复杂。问题是我创建了错误的表达式树。您可以嵌套表达式,以便导航到 属性.
的 属性下面的解决方案应该可以解释这一点(这不是我实际解决问题的方式,但应该清楚):
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;
}