我无法解释为什么使用Int32 不起作用但使用string。两种 EDM 都不是“原始”类型,也不是都实现IComparable?我不明白不同的行为。
无论如何,似乎有必要将集合中的每个表达式都传递给它应该作为排序依据的具体类型,以避免类型转换失败。换句话说,不是IComparable,而是int、string、DateTime 等。
我按照这个答案中的想法成功地实现了这一点:How to check for the presence of an OrderBy in a ObjectQuery<T> expression tree
定义一个接口,它不依赖于排序依据的类型,但仅依赖于实体类型。 (下面的示例推广到任意实体。如果您只希望 User 删除通用参数并将可查询对象中的 TEntity 替换为 User。)
public interface IOrderByExpression<TEntity> where TEntity : class
{
IOrderedQueryable<TEntity> ApplyOrderBy(IQueryable<TEntity> query);
IOrderedQueryable<TEntity> ApplyThenBy(IOrderedQueryable<TEntity> query);
}
定义该接口的实现,该接口现在将要排序的类型作为第二个泛型参数:
public class OrderByExpression<TEntity, TOrderBy> : IOrderByExpression<TEntity>
where TEntity : class
{
private Expression<Func<TEntity, TOrderBy>> _expression;
private bool _descending;
public OrderByExpression(Expression<Func<TEntity, TOrderBy>> expression,
bool descending = false)
{
_expression = expression;
_descending = descending;
}
public IOrderedQueryable<TEntity> ApplyOrderBy(
IQueryable<TEntity> query)
{
if (_descending)
return query.OrderByDescending(_expression);
else
return query.OrderBy(_expression);
}
public IOrderedQueryable<TEntity> ApplyThenBy(
IOrderedQueryable<TEntity> query)
{
if (_descending)
return query.ThenByDescending(_expression);
else
return query.ThenBy(_expression);
}
}
那么ApplyOrderBy 将如下所示:
public IQueryable<TEntity> ApplyOrderBy<TEntity>(IQueryable<TEntity> query,
params IOrderByExpression<TEntity>[] orderByExpressions)
where TEntity : class
{
if (orderByExpressions == null)
return query;
IOrderedQueryable<TEntity> output = null;
foreach (var orderByExpression in orderByExpressions)
{
if (output == null)
output = orderByExpression.ApplyOrderBy(query);
else
output = orderByExpression.ApplyThenBy(output);
}
return output ?? query;
}
并且可以通过以下方式使用:
var query = context.Users ... ;
var queryWithOrderBy = ApplyOrderBy(query,
new OrderByExpression<User, string>(u => u.UserName), // a string, asc
new OrderByExpression<User, int>(u => u.UserId, true)); // an int, desc
var result = queryWithOrderBy.ToList(); // didn't throw an exception for me
在OrderByExpression 实例中明确指定泛型类型参数的需要并不好,但我找不到让编译器推断类型的方法。 (我希望它会,因为编译器从query 推断TEntity 为ApplyOrderBy 方法,然后我希望它知道OrderByExpression 的TEntity (等于User 为好吧). 所以 lambda 参数u 应该被称为User 然后编译器可以从UserName 派生类型为string 和从UserId 作为int。但是这个理论显然是错误的. 编译器抱怨并希望明确地拥有泛型。)