我有一个这样的表达式:
Expression<Func<int, bool>> exp = i => i<15 && i>10;
我想在此行之后向exp
添加条件。我怎么能这样做?
答案 0 :(得分:11)
只需这样:
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
。
答案 1 :(得分:4)
您有两种选择。第一个是BartoszKP的版本,黑盒子第一个表达式并在之后使用它。但是,虽然这有很好的语法支持,但它也意味着像Entity Framework这样的系统无法真正使用表达式,因为它是黑盒子的。如果在数据库查询中使用此表达式,则EF无法在服务器上检查此谓词,但必须将所有数据检索到客户端(如果它完全有效)。
因此,如果你想使用表达式,例如对于数据库查询,您必须使用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]);
三个点表示要作为第二部分插入的表达式。您可以使用编译器创建的另一个表达式,但您必须替换参数。
答案 2 :(得分:0)
我从 https://entityframework.net/ 找到了 .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
是一个辅助类,你应该把它放在你的辅助类或任何可访问的地方,并且不存在于标准包中。
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);
}
}
我对此的正常用法是在我的一项服务中添加多个条件,我无法直接访问查询,因此无法使用多个 .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();
谢谢:
@Georg 的回答在这件事上帮助了我。谢谢。
另外@BartoszKP asnwer 很酷很简单,但不适用于 EF 和查询,所以我认为它是内存数据的一个很好的解决方案......