4

I want to build LINQ which will be later translated to the WHERE IN in sql. In regular use case I would do:

(querable is IQuerable<T> and arr is IEnumerable<int>)

querable = querable.Where(e => arr.Contains(e.Id));

BUT

my issue is that the value I want to filter (e.Id) is dynamic, and I get it as a string. How can I do that?

Some more background: I'm doing REST API endpoint in which user can send by which column he wants to filter values, so examples would be:

  1. filter: {"id": [1,2]}

Which I want to translate into something like queryable.Where(e => new [] {1,2}.Contains(e.Id))

  1. filter: {"customerId": [4,5]}

Which I want to translate into something like queryable.Where(e => new [] {4,5}.Contains(e.CustomerId))

So basically my input is a column name which is a string and list of ids which is IEnumerable<int>

I feel it can be achieved by using Expression (similar to this answer) but I don't know how to do it.

Trevor
  • 7,777
  • 6
  • 31
  • 50
Tomasz Madeyski
  • 10,742
  • 3
  • 50
  • 62
  • I guess [this](https://stackoverflow.com/a/30173701/861716) is what you're looking for, but then using a parametrized property name. – Gert Arnold Dec 13 '17 at 15:30

1 Answers1

2

You can do it like this:

public static class QueryableExtensions {
    public static IQueryable<TEntity> WhereIn<TEntity, TValue>(this IQueryable<TEntity> query, string property, IEnumerable<TValue> values) {
        // x
        var arg = Expression.Parameter(typeof(TEntity), "x");
        // x.Property            
        var prop = Expression.Property(arg, property);
        // values.Contains(x.Property)
        var contains = Expression.Call(
            typeof(Enumerable),
            "Contains",
            new[] { typeof(TValue) },
            Expression.Constant(values),
            prop
        );
        // x => values.Contains(x.Property)
        var lambda = Expression.Lambda<Func<TEntity, bool>>(contains, arg);
        return query.Where(lambda);
    }
}

Note that type of values in enumerable you pass, and property type should match for this to work (so if property is of type int - pass array of ints).

Evk
  • 98,527
  • 8
  • 141
  • 191