linq 如何在表达式中添加另一个条件?

xxslljrj  于 2023-04-03  发布在  其他
关注(0)|答案(3)|浏览(202)

我有一个这样的表达:

Expression<Func<int, bool>> exp = i => i<15 && i>10;

我想在exp后面添加一个条件。我该怎么做?

mzillmmw

mzillmmw1#

简单地说:

Expression<Func<int, bool>> exp = i => i < 15 && i > 10;
var compiled = exp.Compile();
exp = i => compiled(i) && i % 2 == 0;  //example additional condition

请注意,您不能这样做:
exp = i => exp.Compile()(i) && i % 2 == 0; //example additional condition
因为exp将通过引用添加到闭包中,因此,调用它将导致StackOverflowException

wwwo4jvm

wwwo4jvm2#

你有两个选择。第一个是BartoszKP的版本,将第一个表达式设为黑盒,然后使用它。然而,虽然这有很好的语法支持,但这也意味着像Entity Framework这样的系统不能真正使用表达式,因为它是黑盒的。如果这个表达式被用于数据库查询,EF不能在服务器上检查这个 predicate ,但是如果它工作的话,必须将所有数据检索到客户端。
因此,如果您想使用表达式,例如用于数据库查询,则必须使用Expression API,即

Expression<Func<int, bool>> exp = i => i<15 && i>10;
exp = Expression.Lambda<Func<int, bool>>(Expression.AndAlso(exp.Body, ...), exp.Parameters[0]);

三个点表示要作为第二部分插入的表达式。您可以使用编译器创建的另一个表达式,但随后必须替换参数。

7fyelxc5

7fyelxc53#

我在https://entityframework.net/ for .Net Framework 6.0中找到了答案
这也适用于我与.net核心

class Program
{
    static void Main(string[] args)
    {
        Expression<Func<int, bool>> exprA = a => a == 3;
        Expression<Func<int, bool>> exprB = b => b == 4;
        Expression<Func<int, bool>> exprC =
            Expression.Lambda<Func<int, bool>>(
                Expression.OrElse(
                    exprA.Body,
                    new ExpressionParameterReplacer(exprB.Parameters, exprA.Parameters).Visit(exprB.Body)),
                exprA.Parameters);
        Console.WriteLine(exprA.ToString());
        Console.WriteLine(exprB.ToString());
        Console.WriteLine(exprC.ToString());
        Func<int, bool> funcA = exprA.Compile();
        Func<int, bool> funcB = exprB.Compile();
        Func<int, bool> funcC = exprC.Compile();
        Debug.Assert(funcA(3) && !funcA(4) && !funcA(5));
        Debug.Assert(!funcB(3) && funcB(4) && !funcB(5));
        Debug.Assert(funcC(3) && funcC(4) && !funcC(5));
    }
}

注意:ExpressionParameterReplacer是一个helper类,你应该把它放在你的helper或任何可访问的地方,并且不存在于标准包中。

public class ExpressionParameterReplacer : ExpressionVisitor
{
    public ExpressionParameterReplacer(IList<ParameterExpression> fromParameters, IList<ParameterExpression> toParameters)
    {
        ParameterReplacements = new Dictionary<ParameterExpression, ParameterExpression>();
        for (int i = 0; i != fromParameters.Count && i != toParameters.Count; i++)
            ParameterReplacements.Add(fromParameters[i], toParameters[i]);
    }

    private IDictionary<ParameterExpression, ParameterExpression> ParameterReplacements { get; set; }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        ParameterExpression replacement;
        if (ParameterReplacements.TryGetValue(node, out replacement))
            node = replacement;
        return base.VisitParameter(node);
    }
}

自己场景示例

我通常的用法是在我的一个服务中添加多个条件,我不能直接访问Query,所以我不能使用多个.Where()函数:

Expression<Func<Order, bool>> filter = w => ...;

// Extra complex filters which I do not feed to my request models
Expression<Func<Order, bool>> filter2 = null;
switch (model.PredefinedFilter)
{
    case OrderPredefinedFilterEnum.SupportPending:
        filter2 = w => 
            (
                (w.Cart.CartFlow == CartFlowEnum.Buyer_First_Order_Request && w.Cart.CartStatus == CartStatusEnum.PaidByBuyer) || 
                (w.Cart.CartFlow == CartFlowEnum.Seller_First_Suggestion && w.Cart.CartStatus == CartStatusEnum.WaitingForPaymentConfirmByBuyer)
            ) && 
            w.Cart.CartSupportStatus == CartSupportStatusEnum.Waiting;
        break;
}

if(filter2 != null)
{
    filter = Expression.Lambda<Func<Order, bool>>(
        Expression.AndAlso(filter.Body, new ExpressionParameterReplacer(filter2.Parameters, filter.Parameters).Visit(filter2.Body)), 
        filter.Parameters[0]);
}

result = (await _orderRepository.GetAllAsNoTrackingAsync(
    a => totalCount = a,
    filter,
    selector,
    OrderBy,
    take,
    skip,
    cancellationToken: cancellationToken))
    .ToList();

相关问题