I\'ve noticed something curious about reading from an IDataReader
within a using statement that I can\'t comprehend. Though I\'m sure the answer is simple.
Summary: Both versions of the method defer, but because
ReadSomeProcExt
doesn't defer execution, the reader is disposed before execution is passed back to the caller (i.e. beforeEnumerate
can run).ReadSomeProc
, on the other hand, doesn't create the reader until it's been passed back to the caller, so it doesn't dispose the container until all its values have been read.
When your method uses yield return
, the compiler actually changes the compiled code to return an IEnumerable<>
, and the code in your method will not run until other code starts iterating over the returned IEnumerable<>
.
That means that the code below doesn't even run the first line of your Enumerate
method before it disposes the reader and returns a value. By the time someone else starts iterating over your returned IEnumerable<>
, the reader has already been disposed.
using(SqlDataReader rd = cmd.ExecuteReader()){
return rd.Enumerate();
}
But this code would execute the entire Enumerate()
method in order to produce a List<>
of results prior to returning:
using(SqlDataReader rd = cmd.ExecuteReader()){
return rd.Enumerate().ToList();
}
On the other hand, whoever's calling the method with this code doesn't actually execute the method until the result is evaluated:
using(SqlDataReader rd = cmd.ExecuteReader()){
while(rd.Read())
yield return rd.ConvertTo(); //extension method wrapping FastMember
}
But the moment they execute the returned IEnumerable<>
, the using
block opens up, and it doesn't Dispose()
until the IEnumerable<>
finishes its iterations, at which point you will have already read everything you need from the data reader.