Create predicate with a BinaryExpression containing multiple parameters

前端 未结 3 587
旧巷少年郎
旧巷少年郎 2021-01-27 10:52

is it possible to dynamically generate such a predicate using LambdaExpressions?

Expression> predicate = t =>
    t         


        
3条回答
  •  囚心锁ツ
    2021-01-27 11:04

    As pointed out in the answers by @Matt Warren if yow want to combine lambdas you will need to do it by hand and will need to set the correct expression parameters.

    Firstlly, you will need a ExpressionVisitor that can replace node that you want:

        private class SwapVisitor : ExpressionVisitor
        {
            public readonly Expression _from;
            public readonly Expression _to;
    
            public SwapVisitor(Expression from, Expression to)
            {
                _from = from;
                _to = to;
            }
    
            public override Expression Visit(Expression node) => node == _from ? _to : base.Visit(node);
        }
    

    Secondly, you will need to combine lambdas by hand:

        private static Expression> CreatePredicate()
        {
            Expression> left = ld => ld.LevelDate;
            // I didn't include EF, so I did test it just use directly Test.TestDate
            //Expression> right = t => t.TestDate;
            Expression> right = t => DbFunctions.AddDays(t.TestDate, 1);
    
            var testParam = Expression.Parameter(typeof(Test), "test_par");
            var levelParam = Expression.Parameter(typeof(Level), "level_par");
            var detailParam = Expression.Parameter(typeof(LevelDetail), "detail_par");
    
            // Swap parameters for right and left operands to the correct parameters
            var swapRight = new SwapVisitor(right.Parameters[0], testParam);
            right = swapRight.Visit(right) as Expression>;
    
            var swapLeft = new SwapVisitor(left.Parameters[0], detailParam);
            left = swapLeft.Visit(left) as Expression>;
    
            BinaryExpression comparer = Expression.GreaterThan(left.Body, right.Body);
            var lambdaComparer = Expression.Lambda>(comparer, detailParam);
    
            // Well, we created here the lambda for ld => ld.LevelDate > DbFunctions.AddDays(t.TestDate, 1)
    
            var anyInfo = typeof(Enumerable).GetMethods().Where(info => info.Name == "Any" && info.GetParameters().Length == 2).Single();
    
            // Will create **l.LevelDetails.Any(...)** in the code below
    
            var anyInfoDetail = anyInfo.MakeGenericMethod(typeof(LevelDetail));
            var anyDetailExp = Expression.Call(anyInfoDetail, Expression.Property(levelParam, "LevelDetails"), lambdaComparer);
            var lambdaAnyDetail = Expression.Lambda>(anyDetailExp, levelParam);
    
            // Will create **t.Levels.Any(...)** in the code below and will return the finished lambda
    
            var anyInfoLevel = anyInfo.MakeGenericMethod(typeof(Level));
            var anyLevelExp = Expression.Call(anyInfoLevel, Expression.Property(testParam, "Levels"), lambdaAnyDetail);
            var lambdaAnyLevel = Expression.Lambda>(anyLevelExp, testParam);
    
            return lambdaAnyLevel;
        }
    

    And the code below contains usage of this:

        var predicate = CreatePredicate();
    
        var levelDetail = new LevelDetail { LevelDate = new DateTime(2017, 08, 19) };
        var level = new Level { LevelDetails = new List { levelDetail } };
        var test = new Test { TestDate = new DateTime(2027, 08, 19), Levels = new List { level } };
    
        var result = predicate.Compile()(test);
    

提交回复
热议问题