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<Func<Test, bool>> CreatePredicate()
{
Expression<Func<LevelDetail, DateTime?>> left = ld => ld.LevelDate;
// I didn't include EF, so I did test it just use directly Test.TestDate
//Expression<Func<Test, DateTime?>> right = t => t.TestDate;
Expression<Func<Test, DateTime?>> 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<Func<Test, DateTime?>>;
var swapLeft = new SwapVisitor(left.Parameters[0], detailParam);
left = swapLeft.Visit(left) as Expression<Func<LevelDetail, DateTime?>>;
BinaryExpression comparer = Expression.GreaterThan(left.Body, right.Body);
var lambdaComparer = Expression.Lambda<Func<LevelDetail, bool>>(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<Func<Level, bool>>(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<Func<Test, bool>>(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> { levelDetail } };
var test = new Test { TestDate = new DateTime(2027, 08, 19), Levels = new List<Level> { level } };
var result = predicate.Compile()(test);
t.Levels.Any(...)
is aMethodCallExpression
on aMemberExpression
ont
... and so on. You can use LINQPad as an aid to get the structure of the expression tree without the expressions replaced, then write code to build the expression tree with replacements. – Justifier