Return value of ExecuteNonQuery after rollback

2019-02-05 23:52发布

Assuming that we have a stored procedure that does like something this:

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

And from C# we have something like this:

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();                
}

Why I'm not getting getting res == -1? I'm still getting the number of affected rows. When the documentation states "If a rollback occurs, the return value is also -1"

What I'm missing here?

1条回答
Emotional °昔
2楼-- · 2019-02-06 00:12

It appears that the return value of ExecuteNonQuery is unaffected by a rollback even though the documentation clearly states that is does. Here are some possible workarounds.

1) Use 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) Use a return/output parameter

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
    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) Move the rollback/commit logic into the code

This would give you the ability to determine if a rollback occurred and output a value of -1 when necessary. The transaction statement would need to removed from the 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();

    using (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();
        }
    }
}

As noted previously, the @@ROWCOUNT value and the ExecuteNonQuery result are both affected by triggers.

查看更多
登录 后发表回答