0

I have this function in c#. When the FOR is call, a error appears in ExecuteNonQuery. The error is "ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized."

SqlConnection cnn = new SqlConnection(WebConfigurationManager.ConnectionStrings["strCnn"].ToString());
                cnn.Open();
                SqlTransaction trx = cnn.BeginTransaction();

                try
                {

                    SqlCommand cmd= new SqlCommand();

                    for (int j = 0; j < arr.Length; j++) {
                        cmd.CommandText = "UPDATE rc SET nc= " +  arr[j].Col3 + " WHERE cr = " + arr[j].Col1;
                        cmd.Connection = cnn;
                        cmd.ExecuteNonQuery();
                    }

                    trx.Commit();
                    return 1;
                }
                catch (SqlException ex)
                {
                    try
                    {
                        trx.Rollback();
                        return 0;
                    }
                    catch (Exception exRollback)
                    {
                        return 0;
                    }  
                }
3
  • it's a good habit to parameterize all your SQL queries, for reasons of security and readability Commented Oct 31, 2013 at 11:01
  • There are plenty of answers to your question, but it should be pointed out that it's always good practice to use parameters in your queries, instead of just merging strings. Commented Oct 31, 2013 at 11:01
  • While not part of the question, you may want to consider using parameterized query rather than stitching a sql hunk of sql text together. Google Sql Injection Commented Oct 31, 2013 at 11:01

7 Answers 7

1

This error message shows that you have opened a transaction and it is still open at the point of executing ExecuteNonQuery,

You are executing ExecuteNonQuery before commitment of transaction.

Define

comando.Transaction = trx;

So that ExecuteNonQuery will be executed on the same transaction.

Sign up to request clarification or add additional context in comments.

Comments

0

Use

// Create command on transaction and automatically assign open transaction
var comando = conexao.CreateCommand()

or assign the transaction to the command.

// Create command
var comando = new SqlCommand();
// and assign transaction manually
comando.Transaction = trx;

Comments

0

you forgot to set transaction

 comando.Transaction = trx;

Comments

0

You need to assign the transaction to the command, like so:

SqlCommand comando = new SqlCommand();
comando.Transaction = trx;

I would also recommend setting the Connection property outside of the for loop too, so you code would look like this:

SqlCommand comando = new SqlCommand();
comando.Transaction = trx;
comando.Connection = conexao;

for (int j = 0; j < arr.Length; j++) {
    comando.CommandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = " +  arr[j].Col3 + " WHERE CD_RECURSO = " + arr[j].Col1;
    comando.ExecuteNonQuery();
}

trx.Commit();

Comments

0

You need to set SqlCommand's transaction property.

 SqlCommand comando = new SqlCommand();
 comando.Transaction = trx;

Comments

0

Your sqlCommand doesn't know about your transaction.

Here's a copy-paste fix:

SqlConnection conexao = new SqlConnection(WebConfigurationManager.ConnectionStrings["strConexao"].ToString());
conexao.Open();
SqlTransaction trx = conexao.BeginTransaction();

try
{
    for (int j = 0; j < arr.Length; j++) {
        var commandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = " +  arr[j].Col3 + " WHERE CD_RECURSO = " + arr[j].Col1;
        SqlCommand comando = new SqlCommand(commandText, conexao, trx);
        comando.ExecuteNonQuery();
    }

    trx.Commit();
    return 1;
}
catch (SqlException ex)
{
    try
    {
        trx.Rollback();
        return 0;
    }
    catch (Exception exRollback)
    {
        return 0;
    }  
}

Comments

0

As has been pointed out, you never assign your transaction to the command. However there are a few other points I have picked up on.

First and foremost USE PARAMETRISED QUERIES, they will improve performace, type safety and most importantly save you from SQL Injection Attacks.

So instead of:

comando.CommandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = " +  arr[j].Col3 + " WHERE CD_RECURSO = " + arr[j].Col1;

You would use:

comando.CommandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = @Col3 WHERE CD_RECURSO = @Col1";
comando.Parameters.AddWithValue("@Col3", arr[j].Col3);
comando.Parameters.AddWithValue("@Col1", arr[j].Col1);

Secondly, wrap your sql command object with a using wrapper to ensure it is properly dispose of, there is no benefit from reusing the same object over and over again (and this can cause problems):

for (int j = 0; j < arr.Length; j++) 
{
    using (var comando = new SqlCommand("UPDATE RECURSO_CLIENTE SET NM_CLIENTE = @Col3 WHERE CD_RECURSO = @Col1", conexao))
    {
        comando.Transaction = trx;
        comando.Parameters.AddWithValue("@Col3", arr[j].Col3);
        comando.Parameters.AddWithValue("@Col1", arr[j].Col1);
        comando.ExecuteNonQuery();
    }
}

Finally, if you are using SQL-Server 2008+ you can use Table valued Parameters to do this update in a single query:

You first need a type

CREATE TABLE YourTypeName AS TABLE (Col1 INT, Col3 INT);

Then your update statement would be something like:

DECLARE @UpdateValues AS YourTypeName;

MERGE RECURSO_CLIENTE rc
USING @UpdateValues u
    ON rc.CD_RECURSO = u.Col1
WHEN MATCHED UPDATE
    SET NM_CLIENTE = u.Col3;

This means a single statement and you don't need to use explicit transactions. (You might wonder why I have used merge instead of UPDATE, here is why). So putting it all together you would get:

var dataTable = new DataTable();
dataTable.Columns.Add("Col1", typeof(int));
dataTable.Columns.Add("Col3", typeof(int));

for (int j = 0; j < arr.Length; j++) 
{
    var newRow = dataTable.NewRow();
    newRow[0] = arr[j].Col1;
    newRow[1] = arr[j].Col3;
    dataTable.Rows.Add(newRow);
}

string sql = @" MERGE RECURSO_CLIENTE rc
                USING @UpdateValues u
                    ON rc.CD_RECURSO = u.Col1
                WHEN MATCHED UPDATE
                    SET NM_CLIENTE = u.Col3;";

using (var conexao = new SqlConnection(WebConfigurationManager.ConnectionStrings["strConexao"].ToString()))
using (var comando = new SqlCommand(sql, conexao))
{
    conexao.Open(); 
    var tableParam = new SqlParameter("@UpdateValues", SqlDbType.Structured);
    tableParam.TypeName = "@YourTypeName";
    tableParam.Value = dataTable;
    comando.Parameters.Add(tableParam);
    comando.ExecuteNonQuery();
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.