Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Select entities where ID in int array - WCF Data Services, LINQ

I would like to return a set of entities who has and ID that is contained in a list or array of IDs using LINQ and Data Services. I know how to this using LinqToEF but I am at a loss how to this with Data Services or using OData query conventions for that matter.

My thought is that I would do something like:

int[] intArray = {321456, 321355, 218994, 189232};
var query = (from data in context.Entity
             where intArray.contains(data.ID)
             select data);

Is there any way to accomplish using Data Services / OData? I know I could probably hack it with a Service Operation but I would prefer not to do that.

Cheers.

like image 576
gombala Avatar asked Aug 19 '10 11:08

gombala


Video Answer


2 Answers

Currently OData (the underlying protocol) doesn't support the Contains operation. So that's why the client library does not translate the above query. People are basically using two ways to overcome this limitation: 1) Use service operations as you noted. 2) Construct a where clause dynamically which uses simple comparisons to compare the value to each item from the array. So if the array contains 1, 2, 3, the where would be data.ID == 1 || data.ID == 2 || data.ID == 3 The #2 solution is nice because it's a client side only change. The downside is, that it only works for small arrays. If the array contains too many items the expression gets too long and that leads to all kinds of troubles. The #1 solution doesn't have the size problem, but you need to provide the operation on the server.

like image 72
Vitek Karas MSFT Avatar answered Nov 14 '22 23:11

Vitek Karas MSFT


Here is my realization of WhereIn() Method, to filter IQueryable collection by a set of selected entities:

 public static IQueryable<T> WhereIn<T,TProp>(this IQueryable<T> source, Expression<Func<T,TProp>> memberExpr, IEnumerable<TProp> values) where T : class
    {
        Expression predicate = null;
        ParameterExpression param = Expression.Parameter(typeof(T), "t");

        bool IsFirst = true;

        // Create a comparison for each value eg:                 
        // IN:  t => t.Id == 1 | t.Id == 2                

        MemberExpression me = (MemberExpression) memberExpr.Body;
        foreach (TProp val in values)
        {
            ConstantExpression ce = Expression.Constant(val);


            Expression comparison = Expression.Equal(me, ce);

            if (IsFirst)
            {
                predicate = comparison;
                IsFirst = false;
            }
            else
            {
                predicate = Expression.Or(predicate, comparison);
            }
        }

        return predicate != null
            ? source.Where(Expression.Lambda<Func<T, bool>>(predicate, param)).AsQueryable<T>()
            : source;
    }

And calling of this method looks like:

IQueryable<Product> q = context.Products.ToList();

var SelectedProducts = new List<Product>
{
  new Product{Id=23},
  new Product{Id=56}
};
...
// Collecting set of product id's    
var selectedProductsIds = SelectedProducts.Select(p => p.Id).ToList();

// Filtering products
q = q.WhereIn(c => c.Product.Id, selectedProductsIds);
like image 38
Fragment Avatar answered Nov 14 '22 21:11

Fragment