我有一个这样的表达:
Expression<Func<int, bool>> exp = i => i<15 && i>10;
我想在exp后面添加一个条件。我该怎么做?
exp
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。
exp = i => exp.Compile()(i) && i % 2 == 0; //example additional condition
StackOverflowException
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]);
三个点表示要作为第二部分插入的表达式。您可以使用编译器创建的另一个表达式,但随后必须替换参数。
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或任何可访问的地方,并且不存在于标准包中。
ExpressionParameterReplacer
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()函数:
.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();
3条答案
按热度按时间mzillmmw1#
简单地说:
请注意,您不能这样做:
exp = i => exp.Compile()(i) && i % 2 == 0; //example additional condition
因为
exp
将通过引用添加到闭包中,因此,调用它将导致StackOverflowException
。wwwo4jvm2#
你有两个选择。第一个是BartoszKP的版本,将第一个表达式设为黑盒,然后使用它。然而,虽然这有很好的语法支持,但这也意味着像Entity Framework这样的系统不能真正使用表达式,因为它是黑盒的。如果这个表达式被用于数据库查询,EF不能在服务器上检查这个 predicate ,但是如果它工作的话,必须将所有数据检索到客户端。
因此,如果您想使用表达式,例如用于数据库查询,则必须使用Expression API,即
三个点表示要作为第二部分插入的表达式。您可以使用编译器创建的另一个表达式,但随后必须替换参数。
7fyelxc53#
我在https://entityframework.net/ for .Net Framework 6.0中找到了答案
这也适用于我与.net核心
注意:
ExpressionParameterReplacer
是一个helper类,你应该把它放在你的helper或任何可访问的地方,并且不存在于标准包中。自己场景示例
我通常的用法是在我的一个服务中添加多个条件,我不能直接访问Query,所以我不能使用多个
.Where()
函数: