我使用的是EF 4.2,但我希望这也适用于EF 4和4.1。
我想传递一个 IQueryable<T>
和多个 Expression<Func<TSource, TKey>>
方法并适用该方法 OrderBy
和 ThenBy
到了 IQueryable<T>
作为适当的。
我发现 这个答案,并根据以下内容编写了以下方法:
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;
}
只要我订购的属性是,这就可以正常工作 string
s,但是当我尝试按顺序排序时 int
财产,我得到一个例外:
无法将类型“System.Int32”强制转换为“System.IComparable”类型。 LINQ to Entities仅支持转换实体数据模型基元类型。
有任何建议可以解决这个问题,或者完全采用不同的方法吗?我考虑过去了 IEnumerable<Expression>
,但随后需要弄清楚如何回归到特定类型(例如 Expression<Func<User, int>
)打电话 OrderBy
。
我无法解释为什么使用 Int32
不起作用但使用了 string
。不是EDM“原始”类型,也不是两者都实现 IComparable
?我不明白不同的行为。
无论如何,似乎有必要传递集合中的每个表达式,并使用具体类型对其进行排序以避免失败的类型转换。换句话说,不是 IComparable
,而是一个 int
, 一个 string
, 一个 DateTime
等
在这个答案中,我成功地实现了这个想法: 如何检查ObjectQuery <T>表达式树中是否存在OrderBy
定义一个接口 不 取决于要排序的类型,但只取决于实体类型。 (下面的例子推广到任意实体。如果你只想要它 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
实例并不好,但我找不到方法,以便编译器推断出类型。 (我希望它会,因为编译器会推断出 User
如 TEntity
从 query
为了 ApplyOrderBy
方法,然后我预计它知道了 TEntity
的 OrderByExpression
(等于 User
以及)。所以lambda参数 u
应该被称为a User
然后编译器可以从中派生类型 UserName
如 string
从 UserId
如 int
。但这个理论显然是错误的。编译器抱怨并希望明确地拥有泛型类型。)
我无法解释为什么使用 Int32
不起作用但使用了 string
。不是EDM“原始”类型,也不是两者都实现 IComparable
?我不明白不同的行为。
无论如何,似乎有必要传递集合中的每个表达式,并使用具体类型对其进行排序以避免失败的类型转换。换句话说,不是 IComparable
,而是一个 int
, 一个 string
, 一个 DateTime
等
在这个答案中,我成功地实现了这个想法: 如何检查ObjectQuery <T>表达式树中是否存在OrderBy
定义一个接口 不 取决于要排序的类型,但只取决于实体类型。 (下面的例子推广到任意实体。如果你只想要它 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
实例并不好,但我找不到方法,以便编译器推断出类型。 (我希望它会,因为编译器会推断出 User
如 TEntity
从 query
为了 ApplyOrderBy
方法,然后我预计它知道了 TEntity
的 OrderByExpression
(等于 User
以及)。所以lambda参数 u
应该被称为a User
然后编译器可以从中派生类型 UserName
如 string
从 UserId
如 int
。但这个理论显然是错误的。编译器抱怨并希望明确地拥有泛型类型。)