试图结合2个linq表达式

时间:2016-11-18 10:43:58

标签: c# entity-framework linq

我对实体框架模型有一些疑问,并试图巧妙地整理我的查询。

我已经有了一个通用存储库。

这是我尝试做的事情的关键 - 它不起作用,但我不确定为什么以及如何解决它。

代码中的第二个功能有效,但第一个问题还有几个 - 我已经将它包括在内,以便您可以了解我尝试做的事情。

    IEnumerable<Product> QueryLive(Expression<Func<Product, bool>> predicate)
    {
        var exp = new Expression<Func<Product, bool>>(x => x.IsLive);

        var combined = Expression.AndAlso(exp, predicate);

        return QueryPublished(combined);
    }

    IEnumerable<Product> QueryPublished(Expression<Func<Product, bool>> predicate)
    {
        using (var uow = new UnitOfWork(Connections.ProductComparision))
        {
            var r = new Repository<Product>(uow.Context);

            return r.Find(predicate).ToList();
        }
    }

我收到2个语法错误:

行:var exp =&#39; System.Linq.Expressions.Expression&#39;不包含带有1个参数的构造函数

返回:&#39;合并&#39; var是二进制表达式,与QueryPublished()的参数要求冲突。

3 个答案:

答案 0 :(得分:1)

以下是我在您的代码中看到的一些问题:

  • var exp = new Expression<Func<Product, bool>>(x => x.IsLive);无法编译,定义表达式,只需使用以下语法:

    Expression<Func<Product, bool>> exp = x => x.IsLive;

  • combined的类型为ExpressionQueryPublished期望参数类型为Expression<Func<Product, bool>>。这也不会编译。

  • predicate中的参数(例如x中的x =>)与lambda参数exp不同,因此您不能简单地将它们组合在一起方式。

您可以使用LinqKit轻松地组合表达式,如下所示:

IEnumerable<Product> QueryLive(Expression<Func<Product, bool>> predicate)
{
    Expression<Func<Product, bool>> exp = x => x.IsLive;

    Expression<Func<Product, bool>> combined = x => exp.Invoke(x) && predicate.Invoke(x);

    return QueryPublished(combined.Expand());
}

答案 1 :(得分:0)

<强>第一

更改

var exp = new Expression<Func<Product, bool>>(x => x.IsLive);

Expression<Func<Product, bool>> exp = x => x.IsLive;

<强>第二

更改

var combined = Expression.AndAlso(exp, predicate);

var combined = Expression.Lambda<Func<Product, bool>>(Expression.AndAlso(exp.Body, predicate.Body), exp.Parameters);

答案 2 :(得分:0)

大多数Expression类方法用于构建表达式树(例如lambda表达式Body,不能直接用于组合lambda表达式。

为了在你的情况下组合所谓的谓词表达式(Expression<Func<T, bool>>),人们通常会使用一些自定义帮助扩展方法,称为谓词构建器。最着名的是来自LinqKitPredicateBuilder,但它与EF不兼容,需要来自包的Expand / AsExpandable`服务。我个人使用我自己的,Applying LINQ filters based on a multi-dimensional arrayEstablish a link between two lists in linq to entities where clauseGert Arnold的评论中指出的与universal PredicateBuilder非常相似。两者都产生EF兼容表达式,这是我的:

public static class PredicateUtils
{
    sealed class Predicate<T>
    {
        public static readonly Expression<Func<T, bool>> True = item => true;
        public static readonly Expression<Func<T, bool>> False = item => false;
    }
    public static Expression<Func<T, bool>> Null<T>() { return null; }
    public static Expression<Func<T, bool>> True<T>() { return Predicate<T>.True; }
    public static Expression<Func<T, bool>> False<T>() { return Predicate<T>.False; }
    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
    {
        if (Equals(left, right)) return left;
        if (left == null || Equals(left, True<T>())) return right;
        if (right == null || Equals(right, True<T>())) return left;
        if (Equals(left, False<T>()) || Equals(right, False<T>())) return False<T>();
        var body = Expression.AndAlso(left.Body, right.Body.Replace(right.Parameters[0], left.Parameters[0]));
        return Expression.Lambda<Func<T, bool>>(body, left.Parameters);
    }
    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
    {
        if (Equals(left, right)) return left;
        if (left == null || Equals(left, False<T>())) return right;
        if (right == null || Equals(right, False<T>())) return left;
        if (Equals(left, True<T>()) || Equals(right, True<T>())) return True<T>();
        var body = Expression.OrElse(left.Body, right.Body.Replace(right.Parameters[0], left.Parameters[0]));
        return Expression.Lambda<Func<T, bool>>(body, left.Parameters);
    }

    static Expression Replace(this Expression expression, Expression source, Expression target)
    {
        return new ExpressionReplacer { Source = source, Target = target }.Visit(expression);
    }

    class ExpressionReplacer : ExpressionVisitor
    {
        public Expression Source;
        public Expression Target;
        public override Expression Visit(Expression node)
        {
            return node == Source ? Target : base.Visit(node);
        }
    }
}

一旦你有这样的帮助者,所讨论的方法很简单:

IEnumerable<Product> QueryLive(Expression<Func<Product, bool>> predicate)
{
    return QueryPublished(predicate.And(x => x.IsLive));
}