【问题标题】:Linq: Queryable.OrderBy() using a collection of ExpressionsLinq:Queryable.OrderBy() 使用表达式的集合
【发布时间】:2014-07-01 06:52:35
【问题描述】:

我想知道如何将 orderby 表达式存储在列表中。这就是我想写的:

List<Expression<Func<Products,Object>>> list = new List<Expression<Func<Products,Object>>>()
{
  p => p.Name,
  p => p.Id
};

然后:

var expr = list[0];
myProducts.OrderBy( expr );

适用于p.Name,但不适用于p.Id (list[1]),因为它会丢弃以下异常

EntityFramework.SqlServer.dll 中出现“System.NotSupportedException”类型的未处理异常 附加信息:无法将类型“System.Int32”转换为类型“System.Object”。 LINQ to Entities 仅支持转换 EDM 基元或枚举类型。

我必须使用哪种类型的列表?

【问题讨论】:

  • 一种解决方法是写p =&gt; p.Id.ToString(),但我希望它也能与System.Int32一起工作
  • @RobertFricke 这不是一种解决方法,它也会导致 NotSupportedException
  • @RobertFricke:如果您将其作为字符串排序,则从 1 到 12 的范围将是 1, 10, 11, 12, 2, 3, 4, 5, 6, 7, 8, 9,这不是您想要的。
  • 代替 Func 尝试 Func
  • @RandRandom,因为我试过了。

标签: c# linq entity-framework lambda


【解决方案1】:

这是我的解决方案(使用反射并基于DynamicLinq 想法):

定义一个ConvertableExpression 类,以便我们可以拦截对自定义OrderBy() 的调用:

public class ConvertableExpression<T>
{
    public ConvertableExpression(Expression<Func<T, object>> expr)
    {
        this.Expression = expr;
    }

    public Expression<Func<T, object>> Expression { get; private set; }
}

引入一个扩展方法以便于从普通Expression 进行转换:

public static class ExpressionExtensions
{
    public static ConvertableExpression<T> AsConvertable<T>(this Expression<Func<T, object>> expr)
    {
        return new ConvertableExpression<T>(expr);
    }
}

使用基于反射的OrderBy() 实现扩展IQueryable

public static class QueryableExtensions
{
    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, ConvertableExpression<T> expr)
    {
        Expression queryExpr = source.Expression;

        var exprBody = SkipConverts(expr.Expression.Body);
        var lambda = Expression.Lambda(exprBody, expr.Expression.Parameters);
        var quote = Expression.Quote(lambda);

        queryExpr = Expression.Call(typeof(Queryable), "OrderBy", new[] { source.ElementType, exprBody.Type }, queryExpr, quote);

        return (IOrderedQueryable<T>)source.Provider.CreateQuery(queryExpr);
    }

    private static Expression SkipConverts(Expression expression)
    {
        Expression result = expression;

        while (result.NodeType == ExpressionType.Convert || result.NodeType == ExpressionType.ConvertChecked)
            result = ((UnaryExpression)result).Operand;

        return result;
    }
}

用法:

myProducts.OrderBy(expr.AsConvertable());

【讨论】:

  • 为什么“OrderBy”返回一个 IQueryable,而不是像“原始”OrderBy 那样返回预期的 IOrderedQueryable?最后我应该把它投射到这个界面吗?
  • 你是对的。我将方法更改为返回IOrderedQueryable
【解决方案2】:

试试这个

List<Func<Products, Object>> list = new List<Func<Products, Object>>()
{
    new Func<Products,Object>( p => p.Name),
    new Func<Products,Object>( p => p.Id),
};

【讨论】:

  • 现在我们只需要解释一下,为什么它可以作为带有字符串的Expression 而不是 int32 类型,而且你的灵魂在内部会有所不同
  • 看不出你的解决方案和我的区别...主要观点是什么?
  • 此解决方案将调用 Linq to Objects (IEnumerable.OrderBy) 而不是 Linq to Entities (IQueryable.OrderBy),并且不会转换为 store-query(本例中为 SQL)。
【解决方案3】:

所以看起来 EF 的 OrderBy 的实现是通过检查 是结构还是对象来工作的,所以你告诉它调用 OrderBy<... object>(someStructTypeVariable)

作为一种解决方法,我建议您存储整个代表而不是表达式。

试试这个:

internal static class MyExtensions
{
    public static IOrderedQueryable<TSource> OrderBy<TSource, TField>(this IQueryable<TSource> source, Expression<Func<TSource, TField>> selector, bool descending)
    {
        return descending 
            ? source.OrderByDescending(selector)
            : source.OrderBy(selector);
    }

}


var orderers = new List<Func<IQueryable<Products>, IOrderedQueryable<Products>>>()
    {
        source => source.OrderBy(x => x.Id, true),
        source => source.OrderBy(x => x.Id, false), 
        source => source.OrderBy(x => x.Name, false)
    };


// To be replaced with entity source-collection.
IQueryable<Products> dummySource = new EnumerableQuery<MyType>(new List<Products>());

orderers[0](dummySource.Where(x => x.Id != 0));

【讨论】:

  • 抱歉,这对我来说不是一个好的解决方案 :( 因为我的表达式可以在 OrderBy 或 OrderByDescending、ThenBy、ThenByDescending 中使用。
  • 然后编写一个包装器方法,该方法将根据附加参数返回 orderby 或 orderby 降序。要有创意。
  • 尝试有创意,但看不到您的解决方案如何使用包装方法 - 因为您的解决方案直接调用 OrderBy。
  • 我已经为您更新了示例。检查它是否适合您。但我真的不知道你为什么要使用它。您可以创建两个集合:用于升序和降序。
  • 唯一的其他解决方案是通过反射获得正确的方法。
猜你喜欢
  • 2015-06-21
  • 2012-05-28
  • 1970-01-01
  • 2015-07-24
  • 1970-01-01
  • 2016-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多