gpt4 book ai didi

c# - 如何将多个表达式传递给 EF 的 OrderBy?

转载 作者:可可西里 更新时间:2023-11-01 08:07:08 25 4
gpt4 key购买 nike

我使用的是 EF 4.2,但我希望这也适用于 EF 4 和 4.1。

我想传递一个 IQueryable<T>和多个 Expression<Func<TSource, TKey>>到一个方法并应用该方法 OrderByThenByIQueryable<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;
}

只要我订购的属性是 string 就可以正常工作s,但是当我尝试通过 int 订购时属性(property),我得到一个异常(exception):

Unable to cast the type 'System.Int32' to type 'System.IComparable'. LINQ to Entities only supports casting Entity Data Model primitive types.

有任何解决此问题或完全不同方法的建议吗?我考虑过传递 IEnumerable<Expression> , 但随后需要弄清楚如何转换回特定类型(例如 Expression<Func<User, int> )以调用 OrderBy .

最佳答案

我无法解释为什么使用 Int32 但使用 string 不起作用。难道 EDM“原始”类型和不都实现了 IComparable 吗?我不明白不同的行为。

无论如何,似乎有必要将集合中的每个表达式都传递给它应该作为排序依据的具体类型,以避免类型转换失败。换句话说,不是 IComparable,而是 intstringDateTime 等。

我成功地按照这个答案中的想法实现了这一点:How to check for the presence of an OrderBy in a ObjectQuery<T> expression tree

定义一个接口(interface),它依赖于排序依据的类型,但仅依赖于实体类型。 (下面的示例被推广到任意实体。如果您只想为 User 移除通用参数,并将可查询对象中的 TEntity 替换为 User .)

public interface IOrderByExpression<TEntity> where TEntity : class
{
IOrderedQueryable<TEntity> ApplyOrderBy(IQueryable<TEntity> query);
IOrderedQueryable<TEntity> ApplyThenBy(IOrderedQueryable<TEntity> query);
}

定义该接口(interface)的一个实现,它现在将要排序的类型作为第二个通用参数:

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 实例中明确指定泛型类型参数并不好,但我找不到让编译器推断类型的方法。 (我希望它会,因为编译器从 ApplyOrderBy 方法的 query 中将 User 推断为 TEntity,然后我希望它知道 OrderByExpressionTEntity(也等于 User)。所以 lambda 参数 u应该被称为 User,然后编译器可以从 UserName 派生类型为 string,从 UserId 派生类型为int。但这个理论显然是错误的。编译器提示并希望显式拥有泛型。)

关于c# - 如何将多个表达式传递给 EF 的 OrderBy?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8190480/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com