0

I have a extension method which creates a lambda predicate expression, which I use to filter data when the value is not null.

It works fine for cases like

query.FilterBy(obj => obj.MyProp, value);

but it fails in cases like

query.FilterBy(obj => obj.MyObject.MyProp, value);

In this case the expression for MyProp property is accessed on obj, which of course doesn't have the property.

I'm not familiar with the expression tree and could not figure out how to modify the extension method to access properties of sub objects. Does anyone have an idea how I could do it?

The extension method:

public static IQueryable<TSource> FilterBy<TSource, TProp>(this IQueryable<TSource> source,
    Expression<Func<TSource, TProp>> property,
    TProp value)
{
    if (value != null)
    {
        var parameter = Expression.Parameter(typeof(TSource));

        var memberExpression = property.Body as MemberExpression ??
                               ((UnaryExpression)property.Body).Operand as MemberExpression;

        if (memberExpression is null)
        {
            throw new InvalidOperationException("Please provide a valid property expression.");
        }

        var propertyName = memberExpression.Member.Name;

        var body = Expression.Equal(
            Expression.Property(parameter, propertyName),
            Expression.Constant(value));

        var predicate = Expression.Lambda<Func<TSource, bool>>(body, parameter);

        source = source.Where(predicate);
    }

    return source;
}
arkord
  • 201
  • 1
  • 12
  • What is the benefit in your case of using this instead of .Where(item => item.Prop.SubProp == value)? I understand what you'd want the method to do, but I don't think I have a ready answer, it does not seem easy. I'll give it a shot later or tomorrow – Alexandru Clonțea Jul 13 '21 at 12:10
  • Also, you may find this thread useful: https://stackoverflow.com/questions/29084894/how-to-use-an-expressionfunc-to-set-a-nested-property – Alexandru Clonțea Jul 13 '21 at 12:13
  • The benefit over `.Where()` is that I only want the filter being executed if the value is not `null`. I use the method to filter over several properties and in each case I had to do `if (value != null) { query = query.Where(o => o.MyProp == value); }`. It is just simpler to have a single reusable method. – arkord Jul 13 '21 at 12:59
  • I'm still confused. Wouldn't "== value" also mean "!= null" when value is not null? (don't want to discuss NaN here :) ). Unless you mean value types, but... Yep, I'm still confuesd. – Alexandru Clonțea Jul 13 '21 at 13:14
  • 1
    I use the method for a EF Core query, which at the end generates a sql script. The record set should only be filtered if the `value != null`. – arkord Jul 13 '21 at 13:19
  • I figured some SQL was involved since I saw the IQueryable definition. Oh, I see now, If it's null, you return everything. Ok, that actually makes sense. – Alexandru Clonțea Jul 13 '21 at 13:22
  • Correct! And instead of having a bunch of `if` statements I rather wanted to have single reusable method ;) – arkord Jul 13 '21 at 13:38

1 Answers1

1

You could simply use epxression passed like parameter property of FilterBy() function for creating new filtering expression instead of trying to create expression from scratch. It will works with "sub-properties".

public static IQueryable<TSource> FilterBy<TSource, TProp>(this IQueryable<TSource> source,
        Expression<Func<TSource, TProp>> property,
        TProp value)
{
    if (value != null)
    {
        var expression = Expression.Equal(property.Body, Expression.Constant(value));
        var predicate = Expression.Lambda<Func<TSource, bool>>(expression, property.Parameters[0]);

        source = source.Where(predicate);
        return source;
    }

    return source;
}
  • It dont work quite yet (because of type matching) but it is the right direction. Thank you, I will look deeper into it. – arkord Jul 13 '21 at 13:14