carewithl
carewithl

Reputation: 1153

How can the result of this LINQ query be of type IQueryable<> and not of type IEnumerable<>?

I understand that next query throws an exception since IQueryable<> provider doesn't support Enumerable.TakeWhile extension method.

        IQueryable<Contact> search = context.Contacts.TakeWhile(q => q.ContactID > 10);
        foreach(var q in search); // exception

But it gets confusing if I try to understand the preceding query from C#'s point of view.

Since last Linq method to be called in above chain is Linq-to-Objects TakeWhile, I'd expect search variable to be of type IEnumerable<T> , but instead it is of type IQueryable (I know that IQueryable<T> queries are converted into expression trees, while Linq-to-Objects queries are essentially converted into deferred method calls, so trying to convert an expression tree (created by context.Contacts ) into method calls wouldn't work. As said, this is confusing to me from C#'s point of view ).

Also, the way Queryable operators build up their queries is by calling on IQueryProvider<T>.CreateQuery, which given an expression tree build up new IQueryable<T> objects. Doesn't this mean that somehow IEnumerable<T>.TakeWhile operator must know to either build an expression tree by itself (which it doesn't) or must be able to call IQueryProvider<T>.CreateQuery?

I'm pretty sure my assumptions are wrong, so can someone explain how is the preceding query able to return a result of type IQueryable<T> instead of IEnumerable<T>?

thank you in advance

Upvotes: 1

Views: 1169

Answers (2)

Johan Larsson
Johan Larsson

Reputation: 17600

IQueryable<T>.TakeWhile() returns IQueryable<T>

IEnumerable<T>.TakeWhile() returns IEnumerable<T>

[Test]
public void TakeWhileTest()
{
    List<int> ints = new List<int> {1, 2, 3, 4};
    var enumerable = ints.AsEnumerable().TakeWhile(x => x < 3);
    Assert.IsNotInstanceOf<IQueryable<int>>(enumerable);
    foreach (var i in enumerable)
    {

    }
    IQueryable<int> queryable = ints.AsQueryable().TakeWhile(x => x < 3);
    Assert.IsInstanceOf<IQueryable<int>>(queryable);
    foreach (var i in queryable)
    {

    }
}

The above works, my guess is that you try to get q.ContactId from a q that is null. You can try this (and skip the TakeWhile):

foreach (var contact in context.Contacts)
{
    if (contact.ContactID > 10)
        break;
    //Do your stuff here
}

Upvotes: 1

Aducci
Aducci

Reputation: 26694

There is a Queryable.TakeWhile as well as an Enumerable.TakeWhile

Upvotes: 1

Related Questions