我想传递一个IQueryable< T>和多个表达式< Func< TSource,TKey>>一种方法,并且该方法将OrderBy和ThenBy应用于IQueryable< T>作为适当的.
我发现了this answer,并根据下面的方法写下:
public IQueryable<User> ApplyOrderBy(IQueryable<User> query,IEnumerable<Expression<Func<User,IComparable>>> orderBy) { if (orderBy == null) { return query; } IOrderedQueryable<User> output = null; foreach(var expression in orderBy) { if (output == null) { output = query.OrderBy(expression); } else { output = output.ThenBy(expression); } } return output ?? query; }
只要我订单的属性是字符串,但是当我尝试通过int属性排序时,我会得到一个例外:
Unable to cast the type ‘System.Int32’ to type ‘System.IComparable’. LINQ to Entities only supports casting Entity Data Model primitive types.
要解决这个问题的任何建议,还是一个不同的方法呢?我认为传递一个IEnumerable< Expression>但是然后将需要弄清楚如何转回到调用OrderBy的特定类型(例如,Expression< Func< User,int>).
解决方法
无论如何,似乎有必要使用具体类型传递集合中的每个表达式,以避免出现失败的类型转换.换句话说,不是一个IComparable,而是一个int,一个字符串,一个DateTime等.
在这个答案中,我已经成功地实现了这一点:How to check for the presence of an OrderBy in a ObjectQuery<T> expression tree
定义一个不依赖于类型进行排序但不仅仅依赖实体类型的接口. (下面的例子被推广到任意实体,如果只需要用户删除通用参数,并用User替换可查询的TEntity.)
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实例中明确指定泛型类型参数的需求不是很好,但是我找不到一种方法,以便编译器推断类型. (我希望这样做,因为编译器将User作为TEntity从ApplyOrderBy方法的查询中推断出来,所以我预计它知道OrderByExpression的TEntity(等于User),所以lambda参数u应该被称为用户然后编译器可以从UserName中导出类型为string,从UserId导出为int,但是这个理论显然是错误的,编译器抱怨并希望明确地显示泛型类型)