Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filter column against list of values #576

Open
phatcher opened this issue Jan 22, 2019 · 5 comments
Open

Filter column against list of values #576

phatcher opened this issue Jan 22, 2019 · 5 comments

Comments

@phatcher
Copy link
Collaborator

Say you are trying to restrict a query against a list of values that is supplied at runtime e.g.

var publishers = new List<int> { 1, 3, 5 };

var books = client.For<Book>
                  .Filter(x => publishers.Contains(x.PublisherId))
                  .FindEntriesAsync()

You will get a syntax error saying that it can't turn System.Collection into a URI

It is possible to put this together like this..

var filterParams = publishers.Select(id => $"PublisherId eq {id}");
var filter = string.Join(" or ", filterParams);
var pubFilter = Uri.EscapeDataString($"({filter})");

var books = client.For<Book>
                  .Filter(pubFilter)
                  .FindEntriesAsync()

But you have to take care with the type that's being encoded and its fairly hacky overall.

Suggest we look at a way of providing a helper that would do this sort of encoding, may be an overload of filter that takes a delegate and a collection?

@sixlettervariables
Copy link
Collaborator

I use the following extension method:

public static IBoundClient<TOwner> WhereInFilter<TOwner, TProperty>(
    this IBoundClient<TOwner> client,
    Expression<Func<TOwner, TProperty>> getter,
    params TProperty[] propertyValues)
    where TOwner : class
{
    return client.Filter(CreateWhereInFilter(getter, propertyValues));
}

public static IBoundClient<TOwner> WhereInFilter<TOwner, TProperty>(
    this IBoundClient<TOwner> client,
    Expression<Func<TOwner, TProperty>> getter,
    IEnumerable<TProperty> propertyValues)
    where TOwner : class
{
    return client.Filter(CreateWhereInFilter(getter, propertyValues));
}

private static Expression<Func<TOwner, bool>> CreateWhereInFilter<TOwner, TProperty>(Expression<Func<TOwner, TProperty>> getter, IEnumerable<TProperty> propertyValues)
{
    // 1. Find the property accessor
    var arg = getter.Parameters.First();
    var property = (MemberExpression)getter.Body;

    // 2. Create comparison chain
    Expression comparisons = null;
    foreach (var value in propertyValues)
    {
        var comparison = Expression.Equal(property, Expression.Constant(value));
        if (comparisons == null)
        {
            comparisons = comparison;
        }
        else
        {
            comparisons = Expression.Or(comparisons, comparison);
        }
    }

    // 3. Return the chained comparisons as a new predicate
    return Expression.Lambda<Func<TOwner, bool>>(comparisons, arg);
}

Used like:

var foos = await this.ODataApi.For<Foo>()
                              .WhereInFilter(x => x.FooID, fooIds)
                              .FindEntriesAsync()
                              .ConfigureAwait(false);

@phatcher
Copy link
Collaborator Author

@sixlettervariables Thanks, that's the sort of thing I was thinking of.

Think this should be in the library as it's a common problem and under the class of problem that it was intended to help with - mind adding it as a pull request once @object expresses an opinion?

@sixlettervariables
Copy link
Collaborator

Certainly, I'd enjoy some feedback on the naming of the API (and its shape) and where in the project it would best fit (i.e. new API on IBoundClient? Extension method?).

@scsloan
Copy link

scsloan commented Dec 6, 2023

Any update here?

@sixlettervariables did you have a shared library for this?

@sixlettervariables
Copy link
Collaborator

Apologies @scsloan but in my current role I no longer actively use OData.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants