Reputation: 1153
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
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