Building lambda expression dynamically

dynamic expression expression-trees lambda tree

Question

I know how to build a simple lambda like x => x > 5:

int[] nbs = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };            
IEnumerable<int> result1 = nbs.Where(x => x > 5);

ParameterExpression parameter = Expression.Parameter(typeof(int), "x");
ConstantExpression constant = Expression.Constant(5);
BinaryExpression expressionBody = Expression.GreaterThan(parameter, constant);
Expression<Func<int, bool>> expression = Expression.Lambda<Func<int, bool>>(expressionBody, parameter);
IEnumerable<int> result2 = nbs.Where(expression.Compile());

But, how do I create a more complex lambda like this p=>p.FindAttribute("Gender")?.Value == "Female" in the same style as above?

  public class Person
    {
        public bool POI { get; set; } = false;
        public string Name { get; set; }
        public List<Car> Cars { get; set; }
        public List<Attribute> Attributes { get; set; }

        public bool PersonOfInterest()
        {            
            return POI;
        }    

        public Attribute FindAttribute(string name)
        {
            return Attributes.FirstOrDefault(a => a.Name == name);
        }
    }

    public class Attribute
    {
        public string Name { get; set; }
        public string Value { get; set; }

        public Attribute(string name, string value) { Name = name; Value = value; }
    }

    public class Car
    {
        public string Make { get; set; }
        public int Horsepowers { get; set; }
        public string Fuel { get; set; }
    }

    Person p1 = new Person();
    p1.Name = "Thom";
    p1.POI = true;
    p1.Attributes = new List<Attribute>() {new Attribute("Length", "Tall"), new Attribute("Hair", "Long hair")};
    p1.Cars = new List<Car>()
    {
        new Car(){Horsepowers = 100, Make = "Toyota", Fuel = "Diesel"},
        new Car(){Horsepowers = 200, Make = "Fiat", Fuel = "Diesel"},
        new Car(){Horsepowers = 300, Make = "Audi", Fuel = "Diesel"},
        new Car(){Horsepowers = 150, Make = "Ferrari", Fuel = "Petrol"}
    };

    Person p2 = new Person();
    p2.POI = false;
    p2.Attributes = new List<Attribute>() { new Attribute("Nationality", "English"), new Attribute("Gender", "Female") };
    p2.Name = "Sophie";
    p2.Cars = new List<Car>()
    {
        new Car(){Horsepowers = 500, Make = "McLaren", Fuel = "Diesel"},
        new Car(){Horsepowers = 200, Make = "Volvo", Fuel = "Diesel"},
        new Car(){Horsepowers = 300, Make = "Audi", Fuel = "Diesel"},
        new Car(){Horsepowers = 400, Make = "Ferrari", Fuel = "Diesel"}
    };

    IEnumerable<Person> res = persons.Where(p=>p.FindAttribute("Gender")?.Value == "Female");

Of course I could always create a new method on Person like:

public bool HasAttributeWithValue(string name, string value)
{
    return FindAttribute(name)?.Value == value;
}

But it's still of interest to me if it's possible to construct the complex lambda dynamically!

1
3
9/18/2018 8:01:03 AM

Accepted Answer

Null-conditional operator is a language feature. It can be converted to this code:

var attribute = p.FindAttribute("Gender");
return attribute == null ? false : attribute.Value == "Female";

To build an expression tree you can use Expression.Block:

var p = Expression.Parameter(typeof(Person), "p");
var findAttribute = Expression.Call(p,
    typeof(Person).GetMethod(nameof(Person.FindAttribute)),
    Expression.Constant("Gender"));


var attribute = Expression.Parameter(typeof(Attribute), "attribute");
var body = Expression.Block(
    new[] {attribute}, // local variables
    new Expression[]
    {
        Expression.Assign(attribute, findAttribute),
        Expression.Condition(
            Expression.Equal(attribute, Expression.Constant(null)),
            Expression.Constant(false),
            Expression.Equal(
                Expression.PropertyOrField(attribute, "Value"),
                Expression.Constant("Female")))
    }
);

var lambda = Expression.Lambda<Func<Person, bool>>(body, p);
0
9/18/2018 10:25:23 AM

Popular Answer

This piece of code does the job.

    ParameterExpression parameter = Expression.Parameter(typeof(Person), "p");
    ConstantExpression my_null_object = Expression.Constant(null);
    ConstantExpression gender = Expression.Constant("Gender");
    MethodCallExpression methodcall = Expression.Call(parameter, typeof(Person).GetMethod("FindAttribute"), gender);
    BinaryExpression is_null = Expression.Equal(methodcall, my_null_object);
    ConstantExpression constant = Expression.Constant("Female");
    MemberExpression member = Expression.Property(methodcall, typeof(Attribute).GetProperty("Value"));
    BinaryExpression expressionBody = Expression.Equal(member, constant);
    BinaryExpression cond = Expression.AndAlso(Expression.IsFalse(is_null), expressionBody);

Maybe clearer this way

        ParameterExpression parameter = Expression.Parameter(typeof(Person), "p");

        ConstantExpression my_null_object = Expression.Constant(null);
        MethodCallExpression methodcall = Expression.Call(parameter, typeof(Person).GetMethod("FindAttribute"), Expression.Constant("Gender"));

        BinaryExpression cond = Expression.AndAlso(

            Expression.IsFalse(Expression.Equal(methodcall, my_null_object)),

            Expression.Equal(
                Expression.Property(methodcall, typeof(Attribute).GetProperty("Value")), 
                Expression.Constant("Female")
                )
        );


Related Questions





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