不能得到异常&Dispose()在这个方法中工作

本文关键字:方法 工作 异常 Dispose 不能 | 更新日期: 2023-09-27 18:11:07

我有以下从火鸟数据库读取的函数。该函数工作,但不处理异常(必需)。

public IEnumerable<DbDataRecord> ExecuteQuery(string Query)
{
    var FBC = new FbCommand(Query, DBConnection);
    using (FbDataReader DBReader = FBC.ExecuteReader())
    {
        foreach (DbDataRecord record in DBReader)
            yield return record;
    }
}

将try/catch添加到该函数中会产生有关yield的错误。我理解为什么我得到错误,但我尝试过的任何解决方案都导致DBReader通过使用()过早地间接处置或Dispose()未被调用all。我如何让这段代码使用异常&清理而不必包装方法或复制DBReader可能包含数千条记录?

更新:

下面是一个尝试修复的示例。在这种情况下,DBReader被过早地处理了。

    public IEnumerable<DbDataRecord> ExecuteQuery(string Query)
    {   
        var FBC = new FbCommand(Query, DBConnection);
        FbDataReader DBReader = null;
        try
        {
            using (DBReader = FBC.ExecuteReader());
        }
        catch (Exception e)
        {
            Log.ErrorException("Database Execute Reader Exception", e);
            throw;
        }
        foreach (DbDataRecord record in DBReader) <<- DBReader is closed at this stage
            yield return record;
    }

不能得到异常&Dispose()在这个方法中工作

对我来说,你得到的代码看起来很好(除了我将在yield return周围使用大括号,并更改变量名以适应。net命名约定:)

Dispose方法只有在以下情况下才会在reader上被调用:

  • 读取MoveNext()Current时抛出异常
  • 使用迭代器的代码对其调用dispose

注意,foreach语句会自动调用迭代器上的Dispose,所以如果您写:

foreach (DbDataRecord record in ExecuteQuery())
{
    if (someCondition)
    {
        break;
    }
}

则会在块末尾的迭代器上调用Dispose,然后在FbDataReader上调用Dispose。换句话说,它应该都按预期工作。

如果您需要在方法中添加异常处理,则需要执行如下操作:

using (FbDataReader DBReader = FBC.ExecuteReader())
{
    using (var iterator = DBReader.GetEnumerator())
    {
        while (true)
        {
            DbDataRecord record = null;
            try
            {
                if (!iterator.MoveNext())
                {
                    break;
                }
                record = iterator.Current;
            }
            catch (FbException e)
            {
                // Handle however you want to handle it
            }
            yield return record;
        }
    }
}

就我个人而言,我会在更高的级别处理异常…

这行不行,注意最后的;,它是using()

的整个范围
    try
    {
        using (DBReader = FBC.ExecuteReader())
          ;  // this empty statement is the scope of using()
     }

除了不能从try/catch中产生以外,下面的语法是正确的:

    // not working
    try
    {
        using (DBReader = FBC.ExecuteReader())
        {
          foreach (DbDataRecord record in DBReader) 
             yield return record;
        }
    }
    catch (Exception e)
    {
        Log.ErrorException("Database Execute Reader Exception", e);
        throw;
    }

但是你可以更接近你的原始代码:

    // untested, ought to work
    FbDataReader DBReader = null;
    try
    {
        DBReader = FBC.ExecuteReader();
    }
    catch (Exception e)
    {
        Log.ErrorException("Database Execute Reader Exception", e);
        throw;
    }
    using (DBReader)
    {
      foreach (DbDataRecord record in DBReader) // errors here won't be logged
        yield return record;  
    }

也可以从读循环中捕获错误,参见Jon Skeet的回答。