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

前端之家收集整理的这篇文章主要介绍了c# – 如何将多个表达式传递给EFB的OrderBy?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我使用EF 4.2,但我预计这将适用于EF 4和4.1.

我想传递一个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>).

解决方法

我无法解释为什么使用Int32不起作用但使用字符串.不是EDM都是“原始”类型,并不能同时实现IComparable?我不明白不同的行为.

无论如何,似乎有必要使用具体类型传递集合中的每个表达式,以避免出现失败的类型转换.换句话说,不是一个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,但是这个理论显然是错误的,编译器抱怨并希望明确地显示泛型类型)

猜你在找的C#相关文章