Search code examples
c#linqexpression-trees

Linq Expression tree Any() issue


Hello I'm having trouble with an expression tree using the .Any() extension method.

Here's my code:

IQueryable<Book> querableBooks = Books.Values.AsQueryable<Book>();

ParameterExpression pe = Expression.Parameter(typeof(Book), "book");

MemberExpression props = Expression.Property(pe, "properties");

ParameterExpression propList = Expression.Parameter(typeof(List<BookProperty>), "properties");

var _test = Expression.Lambda<Func<List<BookProperty>, bool>>(operation, new ParameterExpression[] { propList });

var _Any = Expression.Call(typeof(Enumerable), "any", new Type[] { typeof(BookProperty) }, new Expression[] { propList });

Expression Lamba = Expression.Lambda(_Any, _props);

_test returns {properties => ((bookProperty.type.key == "lingerie") And (bookProperty.value == "1"))}

_Any returns {properties.Any()}

Lambda returns {book.properties => properties.Any()}

The Book class is like this:

 public class Book : IBook
    {

        public int id { get; set; }
//Removed for clarity
        public List<BookProperty> properties { get; set; }

    }

An the BookProperty class:

public class BookProperty
    {
        public BookProperty()
        {
            value = "0";
        }
        public int id { get; set; }
        public int bookId { get; set; }
        public Book book { get; set; }
        public int typeId { get; set; }
        public BookPropertyType type { get; set; }
        public string value { get; set; }
    }

And BookPropertyType class:

 public class BookPropertyType
    {
        public int id { get; set; }
        public string groupe { get; set; }
        public string key { get; set; }
        public string label { get; set; }
        public int order { get; set; }
        public string editorType { get; set; }
        public string unite { get; set; }
    }

So I'm close to it, but I don't how to merge all this correctly to have a query like this

{book.propertie.Any(bookProperty => bookProperty.type.key == "lingerie") And (bookProperty.value == "1")}

Thanks for your help.


Solution

  • If I understand correctly, you are looking for expression like this:

    Expression<Func<Book, bool>> bookPredicate = book => book.properties.Any(
        bookProperty => bookProperty.type.key == "lingerie" && bookProperty.value == "1");
    

    You can build it dynamically like this:

    var book = Expression.Parameter(typeof(Book), "book");
    var properties = Expression.PropertyOrField(book, "properties");
    var bookProperty = Expression.Parameter(typeof(BookProperty), "bookProperty");
    // bookProperty.type.key == "lingerie"
    var conditionA = Expression.Equal(
        Expression.PropertyOrField(Expression.PropertyOrField(bookProperty, "type"), "key"),
        Expression.Constant("lingerie")
    );
    // bookProperty.value == "1"
    var conditionB = Expression.Equal(
        Expression.PropertyOrField(bookProperty, "value"),
        Expression.Constant("1")
    );
    // bookProperty.type.key == "lingerie" && bookProperty.value == "1"
    var condition = Expression.AndAlso(conditionA, conditionB);
    // bookProperty => bookProperty.type.key == "lingerie" && bookProperty.value == "1"
    var predicate = Expression.Lambda<Func<BookProperty, bool>>(condition, bookProperty);
    // book.properties.Any(bookProperty => bookProperty.type.key == "lingerie" && bookProperty.value == "1")
    var anyCall = Expression.Call(
        typeof(Enumerable), "Any", new[] { typeof(BookProperty) },
        properties, predicate
    );
    // book => book.properties.Any(...)
    var bookPredicate = Expression.Lambda<Func<Book, bool>>(anyCall, book);