How do you put this together: selector + predicate?

entity-framework expression-trees lambda linq

Question

Consider that there are two classes.

public class EntityA
{
    public EntityB EntityB { get; set; }
}

public class EntityB
{
    public string Name { get; set; }
    public bool IsDeleted { get; set; }
}

and two selector and predicator expressions

Expression<Func<EntityA, EntityB>> selector = c => c.EntityB;
Expression<Func<EntityB, bool>> predicate = c => c.IsDeleted && c.Name == "AAA";

I must develop a mechanism that yields constructed expressions, like as

Expression<Func<TSource, bool>> Compose<TPropType>(Expression<Func<TSource, TPropType>> selector, Expression<Func<TPropType, bool>> predicator)
{
    // Expression API ???
}

The outcome of my example should be

Expression<Func<EntityA, bool>> exp = Compose(selector, predicate);

what is comparable to

Expression<Func<EntityA, bool>> exp = c => c.EntityB.IsDeleted && c.EntityB.Name == "AAA";

I appreciate it.

1
4
1/10/2012 11:03:33 AM

Accepted Answer

You could try these things:

static Expression<Func<TSource, bool>> Compose<TSource, TPropType>(
    Expression<Func<TSource, TPropType>> selector,
    Expression<Func<TPropType, bool>> predicator)
{
    ParameterExpression param = Expression.Parameter(typeof(TSource), "sourceObj");
    Expression invokedSelector = Expression.Invoke(selector, new Expression[] { param });
    Expression invokedPredicate = Expression.Invoke(predicator, new[] { invokedSelector });

    return Expression.Lambda<Func<TSource, bool>>(invokedPredicate, new[] { param });
}

This is how to apply it:

static void Main()
{
    Expression<Func<EntityA, EntityB>> selector = c => c.EntityB;
    Expression<Func<EntityB, bool>> predicate = c => c.IsDeleted && c.Name == "AAA";

    Expression<Func<EntityA, bool>> exp = Compose(selector, predicate);
    System.Console.WriteLine(exp.Compile()(new EntityA()));
}
0
1/10/2012 2:00:38 PM

Popular Answer

There is no doubt that you should not be using these lambda expressions. Rewriting the phrases is what you should be doing. Just as if you were invoking the lambda expressions, you'll need a means to tie values to them. To do this, update the expressions' bodies, substituting the parameters with the values you want to bind to. This may be used.SubstitutionVisitor To facilitate that:

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);
    }
}

Using the following examples:

Expression<Func<EntityA, EntityB>> selector =
    entityA => entityA.EntityB;
Expression<Func<EntityB, bool>> predicate =
    entityB => entityB.IsDeleted && entityB.Name == "AAA";

The objective is to successfully rewrite it such that it reads like this:

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);
}

Here is an explanation of what is happening line by line:

  1. For the new lambda we're building, create a new argument.

    entity => ...
    
  2. Replace all occurrences of the original argument with the selection.entityA the use of our new parameterentity obtaining the property from the lambda's body.

    entityA => entityA.EntityB
    // becomes
    entity.EntityB
    
  3. Replace all occurrences of the original argument with the predicate.entityB with property already acquiredentity.EntityB to create the body of our new lambda, we took the lambda's body.

    entityB => entityB.IsDeleted && entityB.Name == "AAA"
    // becomes
    entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA"
    
  4. Combine everything into the new lambda.

    entity => entity.EntityB.IsDeleted && entity.EntityB.Name == "AAA"
    


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow