Чтобы ответить на ваши вопросы:
- Транзакции должны быть совершены явно, как сказал Даниэль. В случае непредвиденной ошибки я предпочел бы, чтобы мои данные были оставлены как есть, а не в полусоставленном состоянии, что является точкой транзакции. В этом случае блок перехвата может быть использован для повтора операции с другими параметрами и тому подобным. Во многих случаях с моей работой, если транзакция достигнет конца оператора using без коммита, она откатит его назад без моего кодирования явного try / catch. Помните, что почти во всех случаях исключения объекты в блоке using все равно будут расположены, даже если вы не поймаете исключение. (Мне нравится этот метод, потому что код чище без повтора / ловит везде - я использую try / catch только тогда, когда могу реагировать соответственно)
- Оператор использования в порядке. Если транзакция была зафиксирована, ничего не будет отменено. Если транзакция не была зафиксирована, транзакция будет откатана. Имейте в виду, однако, что удаление объекта транзакции не приведет к явному закрытию основного соединения с базой данных.
Однако я заметил, что созданные вами объекты команд не связаны с транзакцией. Если бы этот код выполнялся на сервере SQL или Oracle, было бы сгенерировано исключение о том, что всем командам должна быть назначена активная транзакция (если она есть).
Чтобы связать команду с транзакцией, вам потребуется следующий фрагмент кода после каждого нового объекта команды:
cmd.Transaction = trans;
Обычно код моей базы данных имеет формат:
using (SqlConnection connection = new SqlConnection("...")) {
connection.Open();
using (SqlTransaction transaction = connection.BeginTransaction())
using (SqlCommand command = connection.CreateCommand()) {
command.Transaction = transaction;
command.CommandText = "INSERT INTO ...";
// add parameters...
command.ExecuteNonQuery();
transaction.Commit();
}
// Reference to question 1: At this point in the code, assuming NO unhandled
// exceptions occurred, the connection object is still open and can be used.
// for example:
using (SqlCommand command = connection.CreateCommand()) {
command.CommandText = "SELECT ...";
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
// do awesome processing here.
}
}
}
}
Этот поток описанных выше соединений обеспечит очистку всех связанных ресурсов с соединением, транзакцией и объектом команды в случае исключения. Если выдается исключение, ошибка находится в строке, которая его вызвала, а не в блоке перехвата, который перехватил и выбросил его снова. Кроме того, транзакция будет откатываться, и базовое соединение с базой данных будет закрыто (или возвращено в пул, если таковое существует).
Помните, что если что-то имеет метод Dispose()
и реализует интерфейс IDisposable
, лучше обернуть это в оператор using, потому что даже если вызов Dispose()
сейчас ничего не делает, нет гарантии, что он будет таким образом в будущем.