将列值作为IEnumerable返回

3

我已经成功运行了这段代码:

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"];
}

然而,我在想是否有更好的(LINQ风格的)方法,而不必使用yield return。(并且LINQ to SQL不是一个选择 :))


2
清晰简洁。要么完全切换到LINQ to SQL,要么保持原样。yield return没有任何问题。 - Mehrdad Afshari
@JoelFan,yield return 有什么问题吗?这也是我会这样做的方式。 - Stan R.
@Stan,问题在于你保持连接的时间比应该的时间长。 - Gabe Moothart
2个回答

5

我怀疑这个代码在未执行枚举时会变得懒惰,但不会像@Joel的代码一样每次只返回一个结果。虽然我找不到任何相关的文档说明。 - Gabe Moothart
数据库执行现在是立即的(不是惰性的)。我相信通过DataContext.Translate方法翻译行也是立即的(不是惰性的)。 - Amy B

3

你不应该这样做!你的读者需要尽快关闭。你不想在枚举期间保持它处于打开状态。最好只创建一个明确的列表,并返回它。

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;

您可以通过将DataReader进行强制类型转换来在Linq表达式中使用它:

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

但请注意,你需要调用ToList()方法,否则当你尝试枚举时会出现错误,因为读取器已经关闭。

编辑

评论中似乎存在一些关于DataReader打开时实际执行的操作的混淆。根据MSDN

当SqlDataReader正在使用时,相关的SqlConnection正忙于服务SqlDataReader,除了关闭连接之外,不能在SqlConnection上执行任何其他操作。这种情况持续到SqlDataReader的Close方法被调用。例如,在调用Close之前,您无法检索输出参数。

因此,您应该尽快关闭它以释放连接。


我不会这样做。如果你想把它放在列表中,就将 OP 的函数设为 private,然后让你的 public 方法返回 GetEmpNames().ToList() - Mehrdad Afshari
1
我认为这是调用者的选择。使用原始代码,如果他们有很多工作要做,他们可以自己调用 ToList()。而使用你的代码,即使调用代码实际上只需要执行类似聚合的操作,所有数据都将被缓冲。你正在减少灵活性。 - Jon Skeet
@Jon 你认为像那样无限期地保持连接是可以的吗? - Gabe Moothart
1
我认为Skeet是正确的。 是的,调用ToList()需要更多的知识,但如果你正在进行任何LINQ编码,那么LINQ的惰性特性可能已经咬了你几次,并且你知道在处理连接之前添加ToList()(而且确实希望尽快摆脱它,因此通常会在完成LINQ操作后立即调用ToList())。我们的编码标准要求所有数据访问都在连接的USING中进行,并且他们很快就学会了何时制作列表。 - Godeke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接