Home > Back-end >  How do I use reflection to get a property and use it in a query?
How do I use reflection to get a property and use it in a query?

Time:11-26

I have a generic method and I want to add a search capability to my method. as parameter I get the name of property(string) and the value(string) it should search for in the list. how can I achieve this?

**This code is not the exact code I have so it may seem that I can use other options like Expression functions which is not possible in my case cause it should be consumed in an Api Controller **I use unit of work with repository pattern in real project and for sake of simplicity I have tryed to add it up in one simple function

public async Task<ActionResult<List<T>>> GetAll(string? filterProperty = null, string? filterValue = null)
{
    IQueryable<T> query = dbSet;
    if (filterProperty != null)
    {
        PropertyInfo property = typeof(T).GetProperty(filterProperty);
        query = query. Where(u=> u.property.Contains(filterValue));
    }
    return await query.ToListAsync();
}

CodePudding user response:

For IQueryable you'll want to create a LambdaExpression for the filter predicate. (For IEnumerable you can compile that expression into an appropriate Func<>.)

This all works by building an expression tree that represents the action you want to perform. In this case you're calling Contains on the result of getting the property value, passing a constant for the filter value.

Let's start with the Contains method, which you can reuse. Rather than basic reflection, here's how you can get it using an expression:

static readonly MethodInfo _contains =
  (((Expression<Func<string, bool>>)(s => s.Contains("a"))).Body as MethodCallExpression)
  .Method;

While that might look a little confusing, it's leveraging the compiler to do the reflection work for us. Sometimes it's easier than searching for the right version of a method with multiple overloads, or when it's not obvious which extension method is involved. The result here is that _contains gets initialized with the method info we need.

You've already got the property info for the target property, so let's put them together:

// The parameter for the predicate
var row = Expression.Parameter(typeof(T), "row");

// Constant for the filter value
var filter = Expression.Constant(filterValue);

// Get the value of the property
var prop = Expression.Property(property);

// Call 'Contains' on the property value
var body = Expression.Call(prop, _contains, filter);

// Finally, generate the lambda
var predicate = Expression.Lambda<Func<T, bool>(body, row);

// Apply to the query
query = query.Where(predicate);

Or in slightly more compact form:

var row = Expression.Parameter(typeof(T), "row");
var predicate = 
    Expression.Lambda<Func<T, bool>
    (
        Expression.Call
        (
            Expression.Property(row, property),
            _compare,
            Expression.Constant(filterValue)
        ),
        row
    );

When you're working on data via IEnumerable<T> instead, predicate.Compile() will produce a working Func<T, bool> to pass to IEnumerable.Where():

private static readonly _tostring = typeof(Object).GetMethod("ToString");

public static IEnumerable<T> Search<T>(this IEnumerable<T> items, string properyyName, string filterValue)
{
    var property = typeof(T).GetProperty(propertyName);
    var row = Expression.Parameter(typeof(T), "row");
    
    // Let's make sure we're actually dealing with a string here
    var prop = Expression.Property(row, property);
    if (property.PropertyType != typeof(string))
        prop = Expression.Call(prop, _tostring);
    
    var func = 
        Expression.Lambda<Func<T, bool>>
        (
            Expression.Call
            (
                prop,
                _compare,
                Expression.Constant(filterValue)
            ),
            row
        ).Compile();

    return items.Where(func);
}

Expressions are pretty versatile, and there are a lot of places where they come in handy. It can be more efficient to compose a function and call it multiple times than to go through reflection all the time, and you can do interesting things with merging expressions and so on.

  • Related