【发布时间】:2016-02-26 15:46:04
【问题描述】:
我使用以下代码将 order by 表达式转换为可以为 null 的列也可以排序。
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 异常。
我知道排序导航属性不能正常工作,但这不是问题。按“常规”属性排序会导致问题。
对此有什么想法吗?
【问题讨论】:
-
从这里开始吧。 Order by 表达式不能像谓词一样组合,但需要与
OrderBy/ThenBy链接。这意味着您需要一种不同的方法 -IQueryable<T>扩展方法而不是表达式返回方法。应用 order by 表达式时,无需将它们转换为类型object。 -
我同意你的第一个说法,但你的第二种方法似乎并不正确。您需要为排序定义一个 TKey 类型。因为我的场景必须能够接受任何类型,所以我只使用了 object.但这两个陈述都与我的问题无关。我已经为这个问题创建了一个解决方法,但我仍然很好奇这是否可行。
-
问题是
Expression<Func<T, int>>不一样(不能转换为)Expression<Func<T, object>>。此外,当您使用类似Expression<T, object>> e = x => x.IntProperty的东西时,生成的表达式主体不是从上面可以预期的成员访问,而是Convert。你为什么认为OrderBy/ThenBy声明第二个通用参数很容易忽略它。 -
这是有道理的,但不知何故,排序确实以这种方式工作,特别是因为我想要进行转换(因为可以为空的属性)。但也许这可能是导航属性不再包含在流程中的原因?
-
如果你提供一个小而完整的例子会更容易。当您手动构建查询时,它被转换为 SQL,并且在这种情况下不需要特殊的
null处理,因为数据库自然支持null(在order by、select等中),并且在那种情况下没有真正的导航属性“加载”。如果您从选择器中获取 NRE,很可能在早些时候您已经切换到 LINQ to Objects。同样,如果您提供显示问题的示例模型和手动查询,这将有所帮助。
标签: c# entity-framework sorting lambda expression