如何将多个表达式传递给OrderBy for EF?
我使用的是EF 4.2,但我希望这也适用于EF 4和4.1。
我想将IQueryable
和多个Expression<Func>
传递给一个方法,并让该方法适当地将OrderBy
和ThenBy
应用于IQueryable
。
我找到了这个答案 ,并根据以下内容编写了以下方法:
public IQueryable ApplyOrderBy(IQueryable query, IEnumerable<Expression<Func>> orderBy) { if (orderBy == null) { return query; } IOrderedQueryable output = null; foreach(var expression in orderBy) { if (output == null) { output = query.OrderBy(expression); } else { output = output.ThenBy(expression); } } return output ?? query; }
只要我订购的属性是string
s,这样就可以正常工作,但是当我尝试按int
属性排序时,我得到一个exception:
无法将类型“System.Int32”强制转换为“System.IComparable”类型。 LINQ to Entities仅支持转换实体数据模型基元类型。
有任何建议可以解决这个问题,或者完全采用不同的方法吗? 我考虑过传入一个IEnumerable
,但后来需要弄清楚如何转换回特定类型(例如Expression<Func
)来调用OrderBy
。
我无法解释为什么使用Int32
不起作用但使用string
。 是不是EDM都是“原始”类型而且都没有实现IComparable
? 我不明白不同的行为。
无论如何,似乎有必要传递集合中的每个表达式,并使用具体类型对其进行排序以避免失败的类型转换。 换句话说,不是IComparable
,而是int
, string
, DateTime
等。
我已经成功地在这个答案中实现了这个想法: 如何检查ObjectQuery
定义一个不依赖于要排序的类型但仅依赖于实体类型的接口。 (下面的示例推广到任意实体。如果您只想为User
删除generics参数并在User
替换可查询中的TEntity
。)
public interface IOrderByExpression where TEntity : class { IOrderedQueryable ApplyOrderBy(IQueryable query); IOrderedQueryable ApplyThenBy(IOrderedQueryable query); }
定义该接口的实现,现在将该类型作为第二个通用参数进行排序:
public class OrderByExpression : IOrderByExpression where TEntity : class { private Expression> _expression; private bool _descending; public OrderByExpression(Expression> expression, bool descending = false) { _expression = expression; _descending = descending; } public IOrderedQueryable ApplyOrderBy( IQueryable query) { if (_descending) return query.OrderByDescending(_expression); else return query.OrderBy(_expression); } public IOrderedQueryable ApplyThenBy( IOrderedQueryable query) { if (_descending) return query.ThenByDescending(_expression); else return query.ThenBy(_expression); } }
然后ApplyOrderBy
看起来像这样:
public IQueryable ApplyOrderBy (IQueryable query, params IOrderByExpression [] orderByExpressions) where TEntity : class { if (orderByExpressions == null) return query; IOrderedQueryable 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(u => u.UserName), // a string, asc new OrderByExpression(u => u.UserId, true)); // an int, desc var result = queryWithOrderBy.ToList(); // didn't throw an exception for me
在OrderByExpression
实例中明确指定generics类型参数的OrderByExpression
并不好,但我找不到一种方法,以便编译器推断出类型。 (我希望它会,因为编译器将User
作为TEntity
从ApplyOrderBy
方法的query
推断出来,然后我希望它知道OrderByExpression
的TEntity
(也等于User
)。所以lambda参数u
应该被称为User
然后编译器可以从UserName
派生类型为string
,从UserId
派生为int
。但这个理论显然是错误的。编译器抱怨并希望明确地使用generics类型。)