#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;
}
Чтобы также перехватить ошибки из цикла чтения, см. Ответ Джона Скита.