Create Expression for List.Any clause

2019-09-07 20:19发布

问题:

Consider this hierarchy of classes.

class Event
{
    public Attendees[] AttendeesList;
}

class Attendees
{
    public ComplexProperty Property;
    public object Value;
}

class ComplexProperty
{

}

class Program
{
    static void Main(string[] args)
    {
        // There are constants.
        ComplexProperty constproperty = new ComplexProperty();
        object constValue = 5;

        // consider this linq query:
        Event evnt = new Event();

        var result = evnt.AttendeesList.Any((attnds) => attnds.Property == constproperty && attnds.Value == constValue);


        // I want to create an Expression tree for above linq query. I need something like this:
        ParameterExpression parameter = Expression.Parameter(typeof(Attendees));
        Expression left = Expression.Property(parameter, typeof(ComplexProperty).GetProperty("Property"));
        // complete this piece.......

    }
}

I want to create a Linq.Expressions.Expression for evnt.AttendeesList.Any((attnds) => attnds.Property == constproperty && attnds.Value == constValue); this linq query. How to query collection with Linq.Expressions looks similar, but I have Any in my linq expression.

Please help.

回答1:

This will give you a start:

ParameterExpression parameter = Expression.Parameter(typeof(Attendees));
Expression left = Expression.Equal(Expression.Property(parameter, "Property"), Expression.Constant(constproperty));

var objType = constValue == null ? typeof(object) : constValue.GetType();
var convertLeft = Expression.Convert(Expression.Property(parameter, "Value"), objType);
var convertRight = Expression.Convert(Expression.Constant(constValue), objType);
Expression right = Expression.Equal(convertLeft, convertRight);
Expression joined = Expression.And(left, right);

var anyMethod = typeof(Queryable).GetMethods().Where(m => m.Name=="Any" && m.GetParameters().Count() == 2).First().MakeGenericMethod(typeof(Attendees));
var call = Expression.Call(anyMethod, Expression.Constant(evnt.AttendeesList, typeof(IQueryable<Attendees>)), Expression.Lambda(joined, parameter));

I changed Value to type int for ease of use. If you want to keep it as an object, you will need to add a Expression.Convert() call in the right Expression.