Транзакция SQLite не выполнена, хотя я не выполняю откат моей транзакции? - PullRequest
4 голосов
/ 07 сентября 2011

Создание соединения + транзакции:

public SQLiteTransaction BeginTransaction()
{
            var con = new SQLiteConnection(@"Data Source=A:\TransactionScopeTest\TransactionTest.db;Foreign Keys=ON");
            con.Open();
            var trans = con.BeginTransaction();
            return trans;
}

Выполните 2 вставки sqlite с одинаковым значением первичного ключа, чтобы вызвать исключение

[TestMethod]
public void TestMethod1()
{
    using (var trans = BeginTransaction())
    {
        try
        {
            SQLiteConnection con = trans.Connection;

            SQLiteCommand cmd1 = con.CreateCommand();
            cmd1.CommandText = "INSERT INTO TEST(Name) VALUES('John')";
            cmd1.ExecuteNonQuery();

            SQLiteCommand cmd2 = con.CreateCommand();
            cmd2.CommandText = "INSERT INTO TEST(Name) VALUES('John')";
            cmd2.ExecuteNonQuery();                   

            trans.Commit();
        }
        catch (Exception)
        {
            trans.Rollback();
            throw;
        }
    }
}

Поскольку я использую SQLite, рекомендуется использоватьКласс SQLiteTransaction для каждой выполняемой команды sql.Соединение из транзакции должно быть общим для методов поставщика данных.

Теперь я задаю ВАМ несколько вопросов:

1.) Когда возникает исключение SQLiteExcex из-за вставкиВ те же первичные ключи «Джон» там не вставлено ни одно из значений «Джон».Это нормально, потому что я использовал транзакцию, и .Commit () должен быть выполнен.Меня беспокоит то, ПОЧЕМУ это не имеет никакого значения, ИЛИ НЕ Я использую trans.Rollback () в блоке catch.

2.) Я использую оператор "using (resource)" - так чтопроизойдет, если транзакция завершится успешно / подтвердит состояние соединения?Это будет закрыто?Просто учтите, что я не использую `using (var trans = new SQLiteTransaction ()) {...}

Ответы [ 2 ]

3 голосов
/ 07 сентября 2011

Чтобы ответить на ваши вопросы:

  1. Транзакции должны быть совершены явно, как сказал Даниэль. В случае непредвиденной ошибки я предпочел бы, чтобы мои данные были оставлены как есть, а не в полусоставленном состоянии, что является точкой транзакции. В этом случае блок перехвата может быть использован для повтора операции с другими параметрами и тому подобным. Во многих случаях с моей работой, если транзакция достигнет конца оператора using без коммита, она откатит его назад без моего кодирования явного try / catch. Помните, что почти во всех случаях исключения объекты в блоке using все равно будут расположены, даже если вы не поймаете исключение. (Мне нравится этот метод, потому что код чище без повтора / ловит везде - я использую try / catch только тогда, когда могу реагировать соответственно)
  2. Оператор использования в порядке. Если транзакция была зафиксирована, ничего не будет отменено. Если транзакция не была зафиксирована, транзакция будет откатана. Имейте в виду, однако, что удаление объекта транзакции не приведет к явному закрытию основного соединения с базой данных.

Однако я заметил, что созданные вами объекты команд не связаны с транзакцией. Если бы этот код выполнялся на сервере 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() сейчас ничего не делает, нет гарантии, что он будет таким образом в будущем.

1 голос
/ 07 сентября 2011

Потому что существует неявный откат с транзакциями. Коммиты должны быть явными.

Соединение будет в конечном итоге закрыто во время выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...