Не удается получить исключения и Dispose() для работы в этом методе

#c# #exception #firebird

#c# #исключение #firebird

Вопрос:

У меня есть следующая функция, которая считывает данные из базы данных firebird. Функция работает, но не обрабатывает исключения (обязательно).

 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 к этой функции выдает ошибку относительно выхода. Я понимаю, почему я получаю ошибку, но любой обходной путь, который я пробовал, приводил к тому, что DBReader удалялся косвенно с помощью using() слишком рано или 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;

    }
  

Комментарии:

1. Пожалуйста, покажите код, в котором есть ошибка, и какова точная ошибка.

2. Что вы подразумеваете под «обработкой исключений»? Я не думаю, что этот метод может или должен. Поместите свой catch в вызывающий код.

3. @Henk Этот метод находится в классе, единственная цель которого — общение с базой данных. Все остальные методы в классе уже обрабатывают исключения, поэтому просто пытаются поддерживать стандарт.

Ответ №1:

Полученный вами код выглядит для меня нормально (за исключением того, что я бы также использовал фигурные скобки вокруг возвращаемого значения yield и изменил имена переменных, чтобы они соответствовали .Соглашения об именовании СЕТЕЙ 🙂

Dispose Метод будет вызываться только для чтения, если:

  • Доступ MoveNext() к или Current в программе чтения вызывает исключение
  • Код, использующий итератор, вызывает dispose для него

Обратите внимание, что foreach оператор автоматически вызывает Dispose итератор, поэтому, если вы написали:

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

тогда это вызовет Dispose итератор в конце блока, который затем вызовет Dispose FbDataReader . Другими словами, все должно работать так, как задумано.

Если вам нужно добавить обработку исключений в метод, вам нужно будет сделать что-то вроде:

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

Лично я бы обработал исключение на более высоком уровне…

Комментарии:

1. Спасибо, попробовал это, но не скомпилировал 2 ошибки «Система. Коллекции. IEnumerator: тип, используемый в инструкции using, должен быть неявно преобразован в ‘System . IDisposable'» amp; «Не может неявно преобразовать тип ‘object’ в ‘System.Data. Обычный. DbDataRecord’. Существует явное преобразование (вам не хватает приведения?) »

2. @Canacourse: я предполагал, что это общий IEnumerable . Похоже, что это не так — если вы можете использовать LINQ, вы можете использовать DBReader.Cast<DbDataRecord>().GetEnumerator()

Ответ №2:

Эта строка не будет работать, обратите внимание ; на конец, это вся область действия 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;  
    }
  

Чтобы также перехватить ошибки из цикла чтения, см. Ответ Джона Скита.