Вызов этих лямбда-выражений, безусловно, то, что вы не хотите делать. Что вы должны делать, это переписывать выражения. Вам просто понадобится способ привязать значения к лямбда-выражениям, как если бы вы вызывали их. Для этого перепишите тела выражений, заменив параметры значениями, к которым вы привязываетесь. Вы можете использовать это SubstitutionVisitor
, чтобы помочь сделать это:
public class SubstitutionVisitor : ExpressionVisitor
{
public Expression OldExpr { get; set; }
public Expression NewExpr { get; set; }
public override Expression Visit(Expression node)
{
return (node == OldExpr) ? NewExpr : base.Visit(node);
}
}
Учитывая эти выражения, например:
Expression<Func<EntityA, EntityB>> selector =
entityA => entityA.EntityB;
Expression<Func<EntityB, bool>> predicate =
entityB => entityB.IsDeleted && entityB.Name == "AAA";
Цель состоит в том, чтобы эффективно переписать его, чтобы оно выглядело так:
Expression<Func<EntityA, bool>> composed =
entity => entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA";
static Expression<Func<TSource, bool>> Compose<TSource, TProp>(
Expression<Func<TSource, TProp>> selector,
Expression<Func<TProp, bool>> predicate)
{
var parameter = Expression.Parameter(typeof(TSource), "entity");
var property = new SubstitutionVisitor
{
OldExpr = selector.Parameters.Single(),
NewExpr = parameter,
}.Visit(selector.Body);
var body = new SubstitutionVisitor
{
OldExpr = predicate.Parameters.Single(),
NewExpr = property,
}.Visit(predicate.Body);
return Expression.Lambda<Func<TSource, bool>>(body, parameter);
}
Чтобы понять, что здесь происходит, приведем построчное объяснение:
Создайте новый параметр для новой лямбды, которую мы создаем.
entity => ...
Учитывая селектор, замените все экземпляры исходного параметра entityA
на наш новый параметр entity
из тела лямбды, чтобы получить свойство.
entityA => entityA.EntityB
// becomes
entity.EntityB
С учетом предиката замените все экземпляры исходного параметра entityB
на ранее полученное свойство entity.EntityB
из тела лямбды, чтобы получить тело нашего нового лямбды.
entityB => entityB.IsDeleted && entityB.Name == "AAA"
// becomes
entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA"
Соберите все вместе в новую лямбду.
entity => entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA"