使用yield 迭代datareader 可能不会关闭连接?

发布于 2024-07-05 00:34:35 字数 838 浏览 12 评论 0原文

这是一个示例代码,用于使用我在谷歌搜索时在一些地方找到的yield关键字从数据库检索数据:

public IEnumerable<object> ExecuteSelect(string commandText)
{
    using (IDbConnection connection = CreateConnection())
    {
        using (IDbCommand cmd = CreateCommand(commandText, connection))
        {
             connection.Open();
             using (IDbDataReader reader = cmd.ExecuteReader())
             {
                while(reader.Read())
                {
                    yield return reader["SomeField"];
                }
             }
             connection.Close();
        }
    }
}

我是否正确地认为,在这个示例代码中,如果我们不迭代,连接就不会关闭整个数据读取器?

如果我正确理解了收益,这里是一个不会关闭连接的示例。

foreach(object obj in ExecuteSelect(commandText))
{
  break;
}

对于可能不是灾难性的数据库连接,我想 GC 最终会清理它,但是如果它不是一个连接,而是一个更关键的连接呢?资源?

Here is a sample code to retrieve data from a database using the yield keyword that I found in a few place while googling around :

public IEnumerable<object> ExecuteSelect(string commandText)
{
    using (IDbConnection connection = CreateConnection())
    {
        using (IDbCommand cmd = CreateCommand(commandText, connection))
        {
             connection.Open();
             using (IDbDataReader reader = cmd.ExecuteReader())
             {
                while(reader.Read())
                {
                    yield return reader["SomeField"];
                }
             }
             connection.Close();
        }
    }
}

Am I correct in thinking that in this sample code, the connection would not be closed if we do not iterate over the whole datareader ?

Here is an example that would not close the connection, if I understand yield correctly..

foreach(object obj in ExecuteSelect(commandText))
{
  break;
}

For a db connection that might not be catastrophic, I suppose the GC would clean it up eventually, but what if instead of a connection it was a more critical resource?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

策马西风 2024-07-12 00:34:35

编译器综合的 Iterator 实现了 IDisposable,当退出 foreach 循环时 foreach 会调用它。

迭代器的 Dispose() 方法将在提前退出时清理 using 语句。

只要您在 foreach 循环、using() 块中使用迭代器,或以其他方式调用 Dispose() 方法,将会发生迭代器的清理。

The Iterator that the compiler synthesises implements IDisposable, which foreach calls when the foreach loop is exited.

The Iterator's Dispose() method will clean up the using statements on early exit.

As long as you use the iterator in a foreach loop, using() block, or call the Dispose() method in some other way, the cleanup of the Iterator will happen.

我纯我任性 2024-07-12 00:34:35

连接将自动关闭,因为您在“using”块内使用它。

Connection will be closed automatically since you're using it inside "using" block.

眸中客 2024-07-12 00:34:35

从我尝试过的简单测试来看,aku 是对的,一旦 foreach 块退出,就会调用 dispose。

@David:但是调用堆栈在调用之间保留,因此连接不会关闭,因为在下一次调用时,我们将返回到yield之后的下一条指令,即while块。

我的理解是,当迭代器被释放时,连接也会随之被释放。 我还认为不需要 Connection.Close,因为当由于 using 子句而处置对象时,它将被处理。

这是我尝试测试行为的一个简单程序......

class Program
{
    static void Main(string[] args)
    {
        foreach (int v in getValues())
        {
            Console.WriteLine(v);
        }
        Console.ReadKey();

        foreach (int v in getValues())
        {
            Console.WriteLine(v);
            break;
        }
        Console.ReadKey();
    }

    public static IEnumerable<int> getValues()
    {
        using (TestDisposable t = new TestDisposable())
        {
            for(int i = 0; i<10; i++)
                yield return t.GetValue();
        }
    }
}

public class TestDisposable : IDisposable
{
    private int value;

    public void Dispose()
    {
        Console.WriteLine("Disposed");
    }

    public int GetValue()
    {
        value += 1;
        return value;
    }
}

From the simple test I have tried, aku is right, dispose is called as soon as the foreach block exit.

@David : However call stack is kept between call, so the connection would not be closed because on the next call we would return to the next instruction after the yield, which is the while block.

My understanding is that when the iterator is disposed, the connection would also be disposed with it. I also think that the Connection.Close would not be needed because it would be taken care of when the object is disposed because of the using clause.

Here is a simple program I tried to test the behavior...

class Program
{
    static void Main(string[] args)
    {
        foreach (int v in getValues())
        {
            Console.WriteLine(v);
        }
        Console.ReadKey();

        foreach (int v in getValues())
        {
            Console.WriteLine(v);
            break;
        }
        Console.ReadKey();
    }

    public static IEnumerable<int> getValues()
    {
        using (TestDisposable t = new TestDisposable())
        {
            for(int i = 0; i<10; i++)
                yield return t.GetValue();
        }
    }
}

public class TestDisposable : IDisposable
{
    private int value;

    public void Dispose()
    {
        Console.WriteLine("Disposed");
    }

    public int GetValue()
    {
        value += 1;
        return value;
    }
}
但可醉心 2024-07-12 00:34:35

此技术说明来看,您的代码将不会按预期工作,但中止第二项,因为返回第一项时连接已关闭。

@Joel Gauvreau:是的,我应该继续读下去。 本系列的第 3 部分解释了编译器添加对finally 块的特殊处理仅在真实结束时触发。

Judging from this technical explanation, your code will not work as expected, but abort on the second item, because the connection was already closed when returning the first item.

@Joel Gauvreau : Yes, I should have read on. Part 3 of this series explains that the compiler adds special handling for finally blocks to trigger only at the real end.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文