tags:

views:

278

answers:

2

I have this code working:

public IEnumerable<string> GetEmpNames()
{
    var cmd = SqlCommand("select [EmpName] from [dbo].[Emp]");
    using (var rdr = cmd.ExecuteReader())
        while (rdr.Read())
            yield return (string) rdr["EmpName"];
}

However, I'm wondering if there's a better (LINQish) way, not having to resort to yield return. (And LINQ to SQL is not an option :) )

+3  A: 
IEnumerable<string> result = DataContext.ExecuteQuery<string>(sqlstring)

http://msdn.microsoft.com/en-us/library/bb361109.aspx

David B
I suspect that this will be lazy in the not-executed-until-enumeration sense, but not in the one-result-at-a-time sense of @Joel's code. I couldn't find any documentation about that, though.
Gabe Moothart
The database execution is Now (not lazy). I believe the translation of rows is also Now (not lazy) via the DataContext.Translate method.
David B
+1  A: 

You should not do that! Your reader needs to be closed as soon as possible. you do not want to keep it open for the duration of the enumeration. It is better to just create an explicit list, and return that.

var cmd = SqlCommand("select [EmpName] from [dbo].[Emp]");
List<string> results = new List<string>();
using (var rdr = cmd.ExecuteReader()) {
    while (rdr.Read())
        results.Add((string) rdr["EmpName"]);
}
return results;

You can use Linq expressions on a DataReader by casting it:

using (var rdr = cmd.ExecuteReader()) {
    results = (from row in rdr.Cast<DbDataRecord>()
               select (string)row["EmpName"]).ToList();
}

But notice that you need to call ToList(), or you will get an error when you try to enumerate because the reader has already been closed.

Edit

There seems to be some confusion in the comments about what a DataReader actually does when it's open. From MSDN:

While the SqlDataReader is being used, the associated SqlConnection is busy serving the SqlDataReader, and no other operations can be performed on the SqlConnection other than closing it. This is the case until the Close method of the SqlDataReader is called. For example, you cannot retrieve output parameters until after you call Close.

Therefore you should close it as soon as possible to free up the connection.

Gabe Moothart
I wouldn't do that. If you want to put it in a list, make OP's function `private` and have your `public` method return `GetEmpNames().ToList()`.
Mehrdad Afshari
I'd say it's the caller's choice. With the original code, they can call `ToList()` themselves if they have a lot of work to do otherwise. With your code, all the data is going to be buffered even if the calling code is actually just going to do something like an aggregation. You're *reducing* flexibility.
Jon Skeet
@Jon You think it's ok to just leave the connection open indefinitely like that?
Gabe Moothart
I think Skeet is on the right track. Yes, calling ToList() requires more knowledge, but if you are doing any LINQ coding the lazy nature of LINQ has probably already bit you a few times and you know to add the ToList() prior to disposing of the connection (and it is true you want to get rid of that as soon as possible, so you normally will call ToList() as soon as you are done with the LINQ operations. Our coding standards require all data access to be in a USING for the connection and they learn pretty quickly when to make the list.
Godeke