Возвращаемое значение ExecuteNonQuery после отката

Предполагая, что у нас есть хранимая процедура, которая делает что-то вроде этого:

BEGIN TRANSACTION
    UPDATE sometable SET aField = 0 WHERE anotherField = 1;       
    UPDATE sometable SET aField = 1 WHERE anotherField = 2;
ROLLBACK TRANSACTION;

И из C # у нас есть что-то вроде этого:

using (var connection = new SqlConnection("connection string")) 
{
    connection.Open();
    var cmd = connection.CreateCommand();
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandText = "my_procedure";
    var res = cmd.ExecuteNonQuery();                
}

Почему я не получаю разрешение == -1? Я все еще получаю количество затронутых строк. Когда в документации указано«Если происходит откат, возвращаемое значение также равно -1»

Что мне здесь не хватает?

 JoshBerke25 февр. 2013 г., 18:18
Похоже, функция суммирует сообщения количества строк. Вы можете попробовать отключить их, добавив set nocount в вашу процедуру, но это также может помешать возвращению количества строк ... Похоже, что либо документация неправильная, либо ошибка.
 Rawling25 февр. 2013 г., 17:27
Интересно, что если вы просто запустите этот код в SSMS, вы получите «1 строку (и)» или что-то подобное. (Или 2, или что угодно - но не 0, даже если это откат.)
 Peter25 февр. 2013 г., 16:42
Какова стоимость Res?
 coffeeyesplease25 февр. 2013 г., 19:46
Боюсь, я больше не на работе. Но мы создали тестовую среду с вышеупомянутым запросом (добавляя CREATE PROCEDURE ..., очевидно), где нет коммита, просто ROLLBACK, так как это то, что нас беспокоит. База данных ведет себя правильно (откат), мы просто не получаем ожидаемое значение на стороне кода. И это не из-за SET NOCOUNT. Мы это тоже проверили.
 coffeeyesplease25 февр. 2013 г., 16:45
@peer в этом случае будет 2. Количество затронутых строк

Ответы на вопрос(1)

ExecuteNonQuery откат не затрагивается, хотя в документации четко указано, что это так. Вот некоторые возможные обходные пути.

1) Используйте ExecuteScalar

SP:

DECLARE @RowCount INT
DECLARE @Error INT

BEGIN TRAN

UPDATE Table1 SET Value1 = NULL

SELECT @RowCount = @@ROWCOUNT, @Error = @@ERROR

IF @Error <> 0 BEGIN
    ROLLBACK TRAN
    SELECT -1
END ELSE BEGIN
    COMMIT TRAN
    SELECT @RowCount
END

C #

using (SqlConnection dbConnection = new SqlConnection("Data Source=.;Initial Catalog=Database1;Integrated Security=True;MultipleActiveResultSets=True"))
{
    dbConnection.Open();

    using (SqlCommand command = dbConnection.CreateCommand())
    {
        command.CommandText = "QuickTest";
        command.CommandType = CommandType.StoredProcedure;

        rowsAffected = command.ExecuteScalar();
    }
}

2) Используйте параметр возврата / вывода

SP: ОБЪЯВИТЬ @RowCount INT ЗАЯВИТЬ @Error INT

BEGIN TRAN

UPDATE Table1 SET Value1 = NULL

SELECT @RowCount = @@ROWCOUNT, @Error = @@ERROR

IF @Error <> 0 BEGIN
    ROLLBACK TRAN
    RETURN -1
END ELSE BEGIN
    COMMIT TRAN
    RETURN @RowCount
END

C #

using (SqlConnection dbConnection = new SqlConnection("Data Source=.;Initial Catalog=Database1;Integrated Security=True;MultipleActiveResultSets=True"))
{
    dbConnection.Open();

    using (SqlCommand command = dbConnection.CreateCommand())
    {
        command.Parameters.Add(new SqlParameter() {Direction = ParameterDirection.ReturnValue });
        command.CommandText = "QuickTest";
        command.CommandType = CommandType.StoredProcedure;

        command.ExecuteNonQuery();
        rowsAffected = command.Parameters[0].Value;
    }
}

3) Переместите логику отката / фиксации в код

Это даст вам возможность определить, произошел ли откат, и при необходимости вывести значение -1. Оператор транзакции должен быть удален из sproc.

SP:

UPDATE Table1 SET Value1 = NULL

C #:

using (SqlConnection dbConnection = new SqlConnection("Data Source=.;Initial Catalog=Database1;Integrated Security=True;MultipleActiveResultSets=True"))
{
    dbConnection.Open();

    usin,g (SqlTransaction tran = dbConnection.BeginTransaction())
    {
        using (SqlCommand command = dbConnection.CreateCommand())
        {
            command.Transaction = tran;

            try
            {
                command.Parameters.Add(new SqlParameter() {Direction = ParameterDirection.ReturnValue });
                command.CommandText = "QuickTest";
                command.CommandType = CommandType.StoredProcedure;

                rowsAffected = command.ExecuteNonQuery();
            }

            catch (Exception)
            {
                rowsAffected = -1;
                throw;
            }

            tran.Commit();
        }
    }
}

Как отмечалось ранее, на значение @@ ROWCOUNT и результат ExecuteNonQuery влияют триггеры.

 coffeeyesplease26 февр. 2013 г., 16:21
Я знаю, что могу ВЫВОДИТЬ значение, чтобы продолжить свою обработку, но я просто не могу поверить, что в такой фундаментальной функции / методе есть ошибка или кто-то испортил документацию. И я был бы более чем рад, если бы кто-нибудь (пожалуйста) доказал, что я неправ.
 Sacrilege26 февр. 2013 г., 16:35
Хорошо, эта проблема отличается от того, что вы выразили. Ваш вопрос был "почему я не получаю -1 здесь".
 Mike Perrenoud26 февр. 2013 г., 15:58
Стоит отметить, что если есть триггер - эти строки будут добавлены - поэтому OP должен следить за ними тоже.
 coffeeyesplease26 февр. 2013 г., 16:14
Здравствуй. Спасибо, но это не то, что я ищу. Я хочу, чтобы количество затронутых строк, когда запрос идет хорошо. Как я хочу -1, если нет. Установив NO COUNT ON, я всегда получу -1 как результат ExecuteNonQuery
 coffeeyesplease26 февр. 2013 г., 16:17
И это не то, что говорится в документации. Он говорит: «Для операторов UPDATE, INSERT и DELETE возвращаемое значение - это количество строк, затронутых командой. Если в таблице, которая вставляется или обновляется, существует триггер, возвращаемое значение включает в себя количество строк, затронутых обеими операциями вставки. или операция обновления и количество строк, затронутых триггером или триггерами. Для всех других типов операторов возвращаемое значение равно -1. Если происходит откат, возвращаемое значение также равно -1. "

Ваш ответ на вопрос