Npgsql не фиксирует транзакцию после неудачной команды - PullRequest
3 голосов
/ 23 сентября 2011

Я использую Npgsql 2.0.11 в .NET 4.0 для изменения базы данных PostgreSQL 9.0.Программа вносит множество изменений в базу данных, все в рамках одной транзакции.

Непосредственно перед фиксацией я запускаю инструкцию SELECT, которая иногда не срабатывает (например, с таймаутом).Я проглатываю исключение и продолжаю в любом случае совершать транзакцию.Нет ошибки, поэтому кажется, что все работает, но на самом деле база данных вообще не была изменена!

Я предполагаю, что неудачный SELECT откатил всю транзакцию.Могу ли я предотвратить это (т. Е. Сделать транзакцию все еще зафиксированной) или, по крайней мере, обнаружить эту ситуацию и выдать исключение, чтобы пользователь знал, что фиксация не удалась?

Я знаю, что в этом конкретном случае я мог бы просто переместитьSELECT вне транзакции, но меня больше волнует решение этого для общего случая.Наличие коммита, а не коммита - довольно серьезная проблема, и я хочу убедиться, что он не останется незамеченным.

Ответы [ 3 ]

7 голосов
/ 23 сентября 2011

Я ничего не знаю о Npgsql, но могу говорить о поведении PostgreSQL. При возникновении любой ошибки в транзакции PostgreSQL транзакция помечается недействительным до тех пор, пока она не будет закрыта. (Их термин «прерван», что, я думаю, вводит в заблуждение.) Кроме того, и это ИМХО безумно, если вы COMMIT неверная транзакция, она «успешно», но имеет тот же эффект, что и ROLLBACK. Вы можете наблюдать это в psql REPL; он напечатает ROLLBACK в ответ на вашу команду COMMIT, но не сообщит об ошибке.

Вы можете создать SAVEPOINT прямо перед вашим окончательным SELECT. Если это не удается, то ROLLBACK к имени точки сохранения; это выведет вас из недопустимого состояния и позволит совершить предыдущую часть транзакции.

0 голосов
/ 24 сентября 2011

В итоге я написал небольшой метод-обертку, который пытается выполнить тривиальный оператор как часть транзакции непосредственно перед фиксацией, что эффективно при обнаружении проблемы.

    public static void CommitTransaction(NpgsqlConnection conn, NpgsqlTransaction tran)
    {
        using (var command = new NpgsqlCommand("SELECT 1", conn, tran))
        {
            try
            {
                command.ExecuteScalar();
            }
            catch (NpgsqlException ex)
            {
                if (ex.Code == "25P02")
                    throw new Exception("The transaction is invalid...");
                throw;
            }
        }

        tran.Commit();
    }

Исправление является либо Морг. или Райан Калпеппер : либо запустите инструкцию вне транзакции, либо заранее создайте SAVEPOINT и ROLLBACK верните ее в случае ошибки.

0 голосов
/ 23 сентября 2011

Если в транзакции что-то не получилось, и, тем не менее, транзакция завершена, это было бы не очень транзакционно, верно?

Так что, в принципе, если это может произойти сбой, и вас это не волнует, не помещайте это втранзакция с тем, что не должно завершиться неудачей.

Используйте транзакции так, как они предназначены, и у вас не возникнет никаких проблем;)

...