1

I am creating a query to return some data from a database. I am passing in a list of ids that I want to filter on. If the filter list is null or empty I want to return all the things.

I have an extension method that lets me do this query is an IQueryable, Ids is a list of nullable ints (don't ask!) ListHasElements is a method that returns true if the list is non-null and has something in it.

 var filteredList = query.WhereIf(ListHasElements(Ids), s => Ids.Contains(s.Id.Value));

However when I build query I use my prefered query syntax

var query = from a in dbContext.as
            join b in dbContext.bs on a.Id.ToString() equals b.Id
            join cin dbContext.cs on b.Id equals c.Id into bcJoin
            from items in bcJoin.DefaultIfEmpty()
            where b.Sent >= fromDate
            where b.Sent <= toDate
            select new{a=a.thing, b=b.thingy, q=items.q,Id=a.Id}

Then I have to insert the initial line to do my magic WhereIf filter. and finally a further select to group by and create my output object (code not shown!)

The extension method looks like this.

public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition,
    Expression<Func<TSource, bool>> predicate)
{
    return condition ? source.Where(predicate) : source;
}

Can I use this method directly in my query syntax query?

Loofer
  • 6,841
  • 9
  • 61
  • 102

3 Answers3

1

Quote from MSDN:

Some queries must be expressed as method calls

This is one of those cases. Even some of their own extension methods must be called via method syntax. For example, Count.

test
  • 2,589
  • 2
  • 24
  • 52
0

You can't. The compiler takes well-known query syntax keywords and converts them to method syntax behind-the-scenes. It knows nothing of your custom extension and thus has no equivalent in query syntax. Note that other "built-in" linq functions have no query syntax equivalent:

  • Skip
  • Count
  • Take
  • Single
  • FirstOrDefault
  • Max
  • etc.

Although I would note that your extension is basically equivalent to

where !condition || predicate(b)
D Stanley
  • 149,601
  • 11
  • 178
  • 240
-1

YES!

var query = from a in dbContext.as.WhereIf(ListHasElements(Ids), s => Ids.Contains(s.Id.Value))
        join b in dbContext.bs on a.Id.ToString() equals b.Id
        join cin dbContext.cs on b.Id equals c.Id into bcJoin
        from items in bcJoin.DefaultIfEmpty()
        where b.Sent >= fromDate
        where b.Sent <= toDate
        select new{a=a.thing, b=b.thingy, q=items.q,Id=a.Id}

My test code (Listings is a table with an Id field):

void Main()
{
   var list=new int []{1,2,3,4};
   var query=from l in Listings.WhereIf(ListHasElements(list),s=>list.Contains(s.Id))
   select l;
   query.Dump();
}
public bool ListHasElements<T>(IEnumerable<T> it)
{
    return it.Any();
}

public static class Ext {
    public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition,
        Expression<Func<TSource, bool>> predicate)
    {
        return condition ? source.Where(predicate) : source;
    }
}
Robert McKee
  • 21,305
  • 1
  • 43
  • 57
  • 1
    The filtering happens at the end, not the beginning. You're also not doing that in query syntax, you're using method syntax. (You're just avoiding creation of a second variable.) – Servy Nov 03 '15 at 18:53
  • I used the method in his query syntax. Apparently he has no issues doing so since is using `.ToString` and `.DefaultIfEmtpy` which are both methods in his otherwise query syntax. As to doing the filtering at the end or beginning, it makes no difference in the results of his query. I used it in his query, and it gives the results you would expect. – Robert McKee Nov 03 '15 at 19:31