Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/linq/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 组合两个表达式(表达式<;Func<;T,bool>;)_C#_Linq_Lambda_Expression - Fatal编程技术网

C# 组合两个表达式(表达式<;Func<;T,bool>;)

C# 组合两个表达式(表达式<;Func<;T,bool>;),c#,linq,lambda,expression,C#,Linq,Lambda,Expression,我有两个Expression类型的表达式,我想取它们中的或,和或不,得到一个相同类型的新表达式 Expression<Func<T, bool>> expr1; Expression<Func<T, bool>> expr2; ... //how to do this (the code below will obviously not work) Expression<Func<T, bool>> andExpress

我有两个
Expression
类型的表达式,我想取它们中的或,和或不,得到一个相同类型的新表达式

Expression<Func<T, bool>> expr1;
Expression<Func<T, bool>> expr2;

...

//how to do this (the code below will obviously not work)
Expression<Func<T, bool>> andExpression = expr AND expr2
表达式expr1;
表达expr2;
...
//如何做到这一点(下面的代码显然不起作用)
表达式andExpression=expr和expr2

好吧,您可以使用
表达式。也可以使用
/
/
等组合逻辑表达式,但问题是参数;您是否在expr1和expr2中使用相同的
参数expression
?如果是这样,则更容易:

var body = Expression.AndAlso(expr1.Body, expr2.Body);
var lambda = Expression.Lambda<Func<T,bool>>(body, expr1.Parameters[0]);
在某个地方,我得到了一些代码,这些代码重新编写了一个表达式树来替换节点,从而消除了调用
的需要,但它相当长(我记不起我把它放在哪里了…)


选择最简单路线的通用版本:

static Expression<Func<T, bool>> AndAlso<T>(
    this Expression<Func<T, bool>> expr1,
    Expression<Func<T, bool>> expr2)
{
    // need to detect whether they use the same
    // parameter instance; if not, they need fixing
    ParameterExpression param = expr1.Parameters[0];
    if (ReferenceEquals(param, expr2.Parameters[0]))
    {
        // simple version
        return Expression.Lambda<Func<T, bool>>(
            Expression.AndAlso(expr1.Body, expr2.Body), param);
    }
    // otherwise, keep expr1 "as is" and invoke expr2
    return Expression.Lambda<Func<T, bool>>(
        Expression.AndAlso(
            expr1.Body,
            Expression.Invoke(expr2, param)), param);
}

如果提供程序不支持Invoke,并且需要组合两个表达式,则可以使用ExpressionVisitor将第二个表达式中的参数替换为第一个表达式中的参数

class ParameterUpdateVisitor : ExpressionVisitor
{
    private ParameterExpression _oldParameter;
    private ParameterExpression _newParameter;

    public ParameterUpdateVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
    {
        _oldParameter = oldParameter;
        _newParameter = newParameter;
    }

    protected override Expression VisitParameter(ParameterExpression node)
    {
        if (object.ReferenceEquals(node, _oldParameter))
            return _newParameter;

        return base.VisitParameter(node);
    }
}

static Expression<Func<T, bool>> UpdateParameter<T>(
    Expression<Func<T, bool>> expr,
    ParameterExpression newParameter)
{
    var visitor = new ParameterUpdateVisitor(expr.Parameters[0], newParameter);
    var body = visitor.Visit(expr.Body);

    return Expression.Lambda<Func<T, bool>>(body, newParameter);
}

[TestMethod]
public void ExpressionText()
{
    string text = "test";

    Expression<Func<Coco, bool>> expr1 = p => p.Item1.Contains(text);
    Expression<Func<Coco, bool>> expr2 = q => q.Item2.Contains(text);
    Expression<Func<Coco, bool>> expr3 = UpdateParameter(expr2, expr1.Parameters[0]);

    var expr4 = Expression.Lambda<Func<Recording, bool>>(
        Expression.OrElse(expr1.Body, expr3.Body), expr1.Parameters[0]);

    var func = expr4.Compile();

    Assert.IsTrue(func(new Coco { Item1 = "caca", Item2 = "test pipi" }));
}
类参数UpdateVisitor:ExpressionVisitor
{
私有参数expression\u oldParameter;
私有参数expression\u newParameter;
公共参数UpdateVisitor(ParameterExpression oldParameter、ParameterExpression newParameter)
{
_oldParameter=oldParameter;
_newParameter=newParameter;
}
受保护的重写表达式VisitParameter(ParameterExpression节点)
{
if(object.ReferenceEquals(节点,_oldParameter))
返回_newParameter;
返回基本访问参数(节点);
}
}
静态表达式更新参数(
表达式表达式,
参数表达式(新参数)
{
var visitor=new参数updatevisitor(expr.Parameters[0],newParameter);
变量body=访问者访问(expr.body);
返回表达式.Lambda(body,newParameter);
}
[测试方法]
public void ExpressionText()
{
string text=“测试”;
表达式expr1=p=>p.Item1.Contains(文本);
表达式expr2=q=>q.Item2.Contains(文本);
表达式expr3=UpdateParameter(expr2,expr1.Parameters[0]);
var expr4=表达式.Lambda(
Expression.OrElse(expr1.Body,expr3.Body),expr1.Parameters[0]);
var func=expr4.Compile();
IsTrue(func(newcoco{Item1=“caca”,Item2=“test pipi”});
}

您可以使用Expression.AndAlso/OrElse组合逻辑表达式,但必须确保参数表达式相同

我在使用EF时遇到了问题,因此我在不使用调用的情况下创建了自己的,我可以这样使用:

var filterC = filterA.And(filterb);
Expression<Func<Result, bool>> noFilterExpression = item => filters == null;

Expression<Func<Result, bool>> laptopFilterExpression = item => item.x == ...
Expression<Func<Result, bool>> dateFilterExpression = item => item.y == ...

var combinedFilterExpression = noFilterExpression.Or(laptopFilterExpression.AndAlso(dateFilterExpression));
    
efQuery.Where(combinedFilterExpression);
my PredicateBuilder的源代码:

public static class PredicateBuilder {

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {    

        ParameterExpression p = a.Parameters[0];

        SubstExpressionVisitor visitor = new SubstExpressionVisitor();
        visitor.subst[b.Parameters[0]] = p;

        Expression body = Expression.AndAlso(a.Body, visitor.Visit(b.Body));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> a, Expression<Func<T, bool>> b) {    

        ParameterExpression p = a.Parameters[0];

        SubstExpressionVisitor visitor = new SubstExpressionVisitor();
        visitor.subst[b.Parameters[0]] = p;

        Expression body = Expression.OrElse(a.Body, visitor.Visit(b.Body));
        return Expression.Lambda<Func<T, bool>>(body, p);
    }   
}
公共静态类谓词生成器{
公共静态表达式和(此表达式a,表达式b){
参数表达式p=a.Parameters[0];
subexpressionvisitor=新建subexpressionvisitor();
visitor.subst[b.Parameters[0]]=p;
表达式body=Expression.AndAlso(a.body,visitor.Visit(b.body));
返回表达式.Lambda(body,p);
}
公共静态表达式或(此表达式a,表达式b){
参数表达式p=a.Parameters[0];
subexpressionvisitor=新建subexpressionvisitor();
visitor.subst[b.Parameters[0]]=p;
Expression body=Expression.OrElse(a.body,visitor.Visit(b.body));
返回表达式.Lambda(body,p);
}   
}
以及替换lambda中参数的实用程序类:

internal class SubstExpressionVisitor : System.Linq.Expressions.ExpressionVisitor {
        public Dictionary<Expression, Expression> subst = new Dictionary<Expression, Expression>();

        protected override Expression VisitParameter(ParameterExpression node) {
            Expression newValue;
            if (subst.TryGetValue(node, out newValue)) {
                return newValue;
            }
            return node;
        }
    }
内部类subexpressionvisitor:System.Linq.Expressions.ExpressionVisitor{
公共字典subst=新字典();
受保护的重写表达式VisitParameter(ParameterExpression节点){
表达新价值;
if(subst.TryGetValue(节点,out newValue)){
返回新值;
}
返回节点;
}
}

我认为这很好用,不是吗

Func<T, bool> expr1 = (x => x.Att1 == "a");
Func<T, bool> expr2 = (x => x.Att2 == "b");
Func<T, bool> expr1ANDexpr2 = (x => expr1(x) && expr2(x));
Func<T, bool> expr1ORexpr2 = (x => expr1(x) || expr2(x));
Func<T, bool> NOTexpr1 = (x => !expr1(x));
Func expr1=(x=>x.Att1==“a”);
Func expr2=(x=>x.Att2==“b”);
Func expr1和expr2=(x=>expr1(x)和&expr2(x));
Func expr1或expr2=(x=>expr1(x)| | expr2(x));
Func NOTexpr1=(x=>!expr1(x));

我建议对PredicateBuilder和
ExpressionVisitor
解决方案进行进一步改进。我把它叫做UnifyParametersByName,你可以在我的麻省理工学院图书馆找到它:。它允许组合任意lambda表达式。通常这些问题都是关于谓词表达式的,但这种想法也扩展到了投影表达式

下面的代码使用方法
ExprAdres
,该方法使用内联lambda创建复杂的参数化表达式。多亏了
LinqExprHelper
mini库,这个复杂的表达式只编码一次,然后重新使用

public IQueryable<UbezpExt> UbezpFull
{
    get
    {
        System.Linq.Expressions.Expression<
            Func<UBEZPIECZONY, UBEZP_ADRES, UBEZP_ADRES, UbezpExt>> expr =
            (u, parAdrM, parAdrZ) => new UbezpExt
            {
                Ub = u,
                AdrM = parAdrM,
                AdrZ = parAdrZ,
            };

        // From here an expression builder ExprAdres is called.
        var expr2 = expr
            .ReplacePar("parAdrM", ExprAdres("M").Body)
            .ReplacePar("parAdrZ", ExprAdres("Z").Body);
        return UBEZPIECZONY.Select((Expression<Func<UBEZPIECZONY, UbezpExt>>)expr2);
    }
}
public IQueryable UbezpFull
{
得到
{
System.Linq.Expressions.Expression<
函数>表达式=
(u、parAdrM、parAdrZ)=>新的UbezpExt
{
Ub=u,
AdrM=parAdrM,
AdrZ=parAdrZ,
};
//从这里调用表达式生成器ExprAdres。
var expr2=expr
.ReplacePar(“paradrarm”,ExprAdres(“M”).Body)
.ReplacePar(“parAdrZ”,ExprAdres(“Z”)。主体);
返回UBEZPIECZONY.Select((表达式)expr2);
}
}
这是子表达式建筑代码:

public static Expression<Func<UBEZPIECZONY, UBEZP_ADRES>> ExprAdres(string sTyp)
{
    return u => u.UBEZP_ADRES.Where(a => a.TYP_ADRESU == sTyp)
        .OrderByDescending(a => a.DATAOD).FirstOrDefault();
}
公共静态表达式ExprAdres(字符串sTyp)
{
返回u=>u.UBEZP_地址,其中(a=>a.TYP_地址==sTyp)
.OrderByDescending(a=>a.DATAOD).FirstOrDefault();
}

我试图实现的是执行参数化查询,而不需要复制粘贴,并且能够使用内联lambda,这非常漂亮。如果没有这些辅助表达式,我将被迫一次性创建整个查询。

我需要获得相同的结果,但使用更通用的方法(因为类型未知)。
public IQueryable<UbezpExt> UbezpFull
{
    get
    {
        System.Linq.Expressions.Expression<
            Func<UBEZPIECZONY, UBEZP_ADRES, UBEZP_ADRES, UbezpExt>> expr =
            (u, parAdrM, parAdrZ) => new UbezpExt
            {
                Ub = u,
                AdrM = parAdrM,
                AdrZ = parAdrZ,
            };

        // From here an expression builder ExprAdres is called.
        var expr2 = expr
            .ReplacePar("parAdrM", ExprAdres("M").Body)
            .ReplacePar("parAdrZ", ExprAdres("Z").Body);
        return UBEZPIECZONY.Select((Expression<Func<UBEZPIECZONY, UbezpExt>>)expr2);
    }
}
public static Expression<Func<UBEZPIECZONY, UBEZP_ADRES>> ExprAdres(string sTyp)
{
    return u => u.UBEZP_ADRES.Where(a => a.TYP_ADRESU == sTyp)
        .OrderByDescending(a => a.DATAOD).FirstOrDefault();
}
    public static LambdaExpression CombineOr(Type sourceType, LambdaExpression exp, LambdaExpression newExp) 
    {
        var parameter = Expression.Parameter(sourceType);

        var leftVisitor = new ReplaceExpressionVisitor(exp.Parameters[0], parameter);
        var left = leftVisitor.Visit(exp.Body);

        var rightVisitor = new ReplaceExpressionVisitor(newExp.Parameters[0], parameter);
        var right = rightVisitor.Visit(newExp.Body);

        var delegateType = typeof(Func<,>).MakeGenericType(sourceType, typeof(bool));
        return Expression.Lambda(delegateType, Expression.Or(left, right), parameter);
    }
public static class ExpressionExtensions
{
    public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
    {
        ParameterExpression parameter1 = expr1.Parameters[0];
        var visitor = new ReplaceParameterVisitor(expr2.Parameters[0], parameter1);
        var body2WithParam1 = visitor.Visit(expr2.Body);
        return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(expr1.Body, body2WithParam1), parameter1);
    }

    private class ReplaceParameterVisitor : ExpressionVisitor
    {
        private ParameterExpression _oldParameter;
        private ParameterExpression _newParameter;

        public ReplaceParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
        {
            _oldParameter = oldParameter;
            _newParameter = newParameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (ReferenceEquals(node, _oldParameter))
                return _newParameter;

            return base.VisitParameter(node);
        }
    }
}
public static class ExpressionExtensions
{
    public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> leftExpression,
        Expression<Func<T, bool>> rightExpression) =>
        Combine(leftExpression, rightExpression, Expression.AndAlso);

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> leftExpression,
        Expression<Func<T, bool>> rightExpression) =>
        Combine(leftExpression, rightExpression, Expression.Or);

    public static Expression<Func<T, bool>> Combine<T>(Expression<Func<T, bool>> leftExpression, Expression<Func<T, bool>> rightExpression, Func<Expression, Expression, BinaryExpression> combineOperator)
    {
        var leftParameter = leftExpression.Parameters[0];
        var rightParameter = rightExpression.Parameters[0];

        var visitor = new ReplaceParameterVisitor(rightParameter, leftParameter);

        var leftBody = leftExpression.Body;
        var rightBody = visitor.Visit(rightExpression.Body);

        return Expression.Lambda<Func<T, bool>>(combineOperator(leftBody, rightBody), leftParameter);
    }

    private class ReplaceParameterVisitor : ExpressionVisitor
    {
        private readonly ParameterExpression _oldParameter;
        private readonly ParameterExpression _newParameter;

        public ReplaceParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
        {
            _oldParameter = oldParameter;
            _newParameter = newParameter;
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            return ReferenceEquals(node, _oldParameter) ? _newParameter : base.VisitParameter(node);
        }
    }
}
Expression<Func<Result, bool>> noFilterExpression = item => filters == null;

Expression<Func<Result, bool>> laptopFilterExpression = item => item.x == ...
Expression<Func<Result, bool>> dateFilterExpression = item => item.y == ...

var combinedFilterExpression = noFilterExpression.Or(laptopFilterExpression.AndAlso(dateFilterExpression));
    
efQuery.Where(combinedFilterExpression);