I’m trying to understand what the affect of AsEnumerable() has over my data when iterating over it. I have a mock in-memory list. If I foreach over it with first calling ToList(), this forces evaluation and my printout looks like this (see code at the bottom of this post to explain output):
entering da
yield return
yield return
yield return
exiting da
doing something to aaron
doing something to jeremy
doing something to brendan
All makes sense. The ToList() forces the yields in the repository to execute first into a list, then we get our foreach iteration. All good so far.
When I do the same except use AsEnumerable(), based on what I’ve read regarding IQueryable (I understand this isn’t IQueryable), I would have thought this also forces evaluation, but it does not. It looks like this:
entering da
yield return
doing something to aaron
yield return
doing something to jeremy
yield return
doing something to brendan
exiting da
As it would if I never even called AsEnumerable(), so my question is:
-
Why does
AsEnumerablebehave differently for an in memory collection vs linq to sql and itsIQueryablereturn type? -
How would all this change when my repository is changed to using a
SqlDataReaderand doing ayield returninside of the reader (whilst callingRead()method). Would the rows coming from SqlServer that are buffered in the clients network buffer be fully evaluated before executing theforeach(normally ayieldhere will cause a “pause” in the repo while each row is processed by theforeachblock. I know if I callToList()first in this case, I can force evaluation of theSqlDataReader, so doesAsEnumerabledo the same here?
Note: I am not interested in whether putting yield into a SqlDataReader is a good idea, given it might hold the connection open, I’ve beaten this topic to death already 🙂
Here is my test code:
public class TestClient
{
public void Execute()
{
var data = MockRepo.GetData();
foreach (var p in data.AsEnumerable()) //or .ToList()
{
Console.WriteLine("doing something to {0}", p.Name);
}
Console.ReadKey();
}
}
public class Person
{
public Person(string name)
{
Name = name;
}
public string Name { get; set; }
}
public class MockRepo
{
private static readonly List<Person> items = new List<Person>(3)
{
new Person("aaron"),
new Person("jeremy"),
new Person("brendan")
};
public static IEnumerable<Person> GetData()
{
Console.WriteLine("entering da");
var enumerator = items.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine("yield return");
yield return enumerator.Current;
}
Console.WriteLine("exiting da");
}
}
AsEnumerabledoes nothing except change the expression type toIEnumerable<T>. When it’s used in a query like this:… that just means you’ll use
Queryable.Wherefor the first predicate (so that’s converted to SQL), andEnumerable.Wherefor the second predicate (so that’s executed in your .NET code).It doesn’t force evaluation. It doesn’t do anything. It doesn’t even check whether it’s called on
null.See my Edulinq blog post on
AsEnumerablefor more information.