ExecuteNonQuery requires the command to have a transaction - c#

I am receiving this error message when i try to execute the following code.
ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction
Can anyone advice where the problem is? I guess the root of the problem is the part where i try to execute a stored procedure.
The stored procedure is creates its own transaction when execute
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
SqlCommand command = conn.CreateCommand();
SqlTransaction transaction;
// Start a local transaction.
transaction = conn.BeginTransaction("createOrder");
// Must assign both transaction object and connection
// to Command object for a pending local transaction
command.Connection = conn;
command.Transaction = transaction;
try
{
command.CommandText = "INSERT INTO rand_resupply_order (study_id, centre_id, date_created, created_by) " +
"VALUES (#study_id, #centre_id, #date_created, #created_by) SET #order_id = SCOPE_IDENTITY()";
command.Parameters.Add("#study_id", SqlDbType.Int).Value = study_id;
command.Parameters.Add("#centre_id", SqlDbType.Int).Value = centre_id;
command.Parameters.Add("#date_created", SqlDbType.DateTime).Value = DateTime.Now;
command.Parameters.Add("#created_by", SqlDbType.VarChar).Value = username;
SqlParameter order_id = new SqlParameter("#order_id", SqlDbType.Int);
//study_name.Value =
order_id.Direction = ParameterDirection.Output;
command.Parameters.Add(order_id);
command.ExecuteNonQuery();
command.Parameters.Clear();
//loop resupply list
for (int i = 0; i < resupplyList.Count(); i++)
{
try
{
SqlCommand cmd = new SqlCommand("CreateOrder", conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#study_id", SqlDbType.Int).Value = study_id;
cmd.Parameters.Add("#centre_id", SqlDbType.Int).Value = centre_id;
cmd.Parameters.Add("#created_by", SqlDbType.VarChar).Value = username;
cmd.Parameters.Add("#quantity", SqlDbType.VarChar).Value = resupplyList[i].Quantity;
cmd.Parameters.Add("#centre_id", SqlDbType.Int).Value = centre_id;
cmd.Parameters.Add("#depot_id", SqlDbType.VarChar).Value = depot_id;
cmd.Parameters.Add("#treatment_code", SqlDbType.Int).Value = centre_id;
cmd.Parameters.Add("#order_id", SqlDbType.Int).Value = (int)order_id.Value;
cmd.ExecuteNonQuery();
}
catch (SqlException ex)
{
transaction.Rollback();
ExceptionUtility.LogException(ex, "error");
throw ex;
}
catch (Exception ex)
{
transaction.Rollback();
ExceptionUtility.LogException(ex, "error");
throw ex;
}
finally
{
conn.Close();
conn.Dispose();
}
}
return (int)order_id.Value;
}
catch (Exception ex)
{
transaction.Rollback();
ExceptionUtility.LogException(ex, "error");
throw ex;
}
finally
{
// Attempt to commit the transaction.
transaction.Commit();
conn.Close();
conn.Dispose();
command.Dispose();
}

when using transaction, you should use it everywhere.
cmd.Transaction = transaction;

using Connection String transaction not popular so far.you can delete every things that related to SqlTransaction and then wrap your code with TransactionScope

Related

Can I execute a stored procedure many time in a for loop?

I have a web service that executes a stored procedure. My web service function returns string[].
Sometimes I need to call the Web service many times.
For optimization reasons, I thought about adding a function to my web service which executes the stored procedure many times, in a for loop. That way the web service is called only once instead of several times.
1-Is my thinking correct ?
2-Below is my code, only the part specific to the problem described above.
3-Is it just not possible to do this using a for loop ?
My Problem: If I only use this code to call the stored procedure once, it works, but as soon as it is more (for loop iterates second time), the catch block is accessed.
If you can explain to me why is this happening and/or suggest a solution/workaround I would really appreciate.
try
{
for (int i = 0; i < number; i++)
{
connection.Open();
cmd = new SqlCommand();
//SqlTransaction transaction;
transaction = connection.BeginTransaction();
cmd.Transaction = transaction;
cmd.Connection = connection;
cmd.Parameters.Clear();
cmd.CommandText = "InsertMsg";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", SqlDbType.VarChar).Value = IDs[i];
cmd.Parameters.Add("#name", SqlDbType.VarChar).Value = names[i];
cmd.Parameters.Add("#age", SqlDbType.DateTime).Value = age;
cmd.ExecuteNonQuery();
data[i] = IDs[i];
transaction.Commit();
}
connection.Close();
return data;
}
catch (SqlException ex)
{
transaction.Rollback();
data[0] = "Error";
return data;
}
}
The issues appears to be with the open and close statements. Close is outside the for loop, change it like
try
{
connection.Open();
transaction = connection.BeginTransaction();
for (int i = 0; i < number; i++)
{
cmd = new SqlCommand();
//SqlTransaction transaction;
cmd.Transaction = transaction;
cmd.Connection = connection;
cmd.Parameters.Clear();
cmd.CommandText = "InsertMsg";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#ID", SqlDbType.VarChar).Value = IDs[i];
cmd.Parameters.Add("#name", SqlDbType.VarChar).Value = names[i];
cmd.Parameters.Add("#age", SqlDbType.DateTime).Value = age;
cmd.ExecuteNonQuery();
data[i] = IDs[i];
transaction.Commit();
}
connection.Close();
return data;
}
catch (SqlException ex)
{
transaction.Rollback();
data[0]="Error";
return data;
}
Closing the connection should be inside a finally block, better use a using statement instead. Also if possible do the looping and transaction inside the stored procedure which will be faster.
Put your connection.Open() outside the loop
I wanted to chime in and mention there's probably an advantage to using a DataAdapter in this circumstance:
https://msdn.microsoft.com/en-us/library/aadf8fk2(v=vs.110).aspx
FTA - "Batch support in ADO.NET allows a DataAdapter to group INSERT, UPDATE, and DELETE operations from a DataSet or DataTable to the server, instead of sending one operation at a time. The reduction in the number of round trips to the server typically results in significant performance gains. "
I just winged this off real quick so ignore any syntax errors. Basically you want to make sure you are taking advantage of "using" statements. When you use a "using" it automatically calls Dispose() after the scope of the code has been reached, that way you dont have to worry about opening or closing a connection that is in use.
for (int i = 0; i < number; i++)
{
//Initialize this however you need to
using (SqlConnection connection = new SqlConnection())
{
connection.Open();
using (SqlCommand command =
new SqlCommand("InsertMsg", connection, connection.BeginTransaction())
{
CommandType = CommandType.StoredProcedure
})
{
try
{
command.Parameters.Clear();
command.Parameters.Add("#ID", SqlDbType.VarChar).Value = IDs[i];
command.Parameters.Add("#name", SqlDbType.VarChar).Value = names[i];
command.Parameters.Add("#age", SqlDbType.DateTime).Value = age;
command.ExecuteNonQuery();
data[i] = IDs[i];
command.Transaction.Commit();
}
catch (SqlException ex)
{
command.Transaction.Rollback();
data[0] = "Error";
}
}
}
}
return data;
try
{
connection.Open();
cmd = new SqlCommand();
transaction = connection.BeginTransaction();
cmd.Transaction = transaction;
cmd.Connection = connection;
cmd.CommandText = "InsertMsg";
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter ID = cmd.Parameters.Add("#ID", SqlDbType.VarChar);
SqlParameter name = cmd.Parameters.Add("#name", SqlDbType.VarChar);
SqlParameter age = cmd.Parameters.Add("#age", SqlDbType.DateTime);
for (int i = 0; i < number; i++)
{
ID.Value = IDs[i];
name.Value = names[i];
age.Value = age;
cmd.ExecuteNonQuery();
data[i] = IDs[i];
}
transaction.Commit();
}
catch (SqlException ex)
{
transaction.Rollback();
data[0] = "Error";
}
finally
{
connection.Close();
}
return data;

Best practices while update/insert to a database

I'm using the following class to handle the database transactions, but the problem is that when my client come up with a faulty database update i cannot roll back the DB into the old state where everything was fine. So is there any best practice to achieve this.
public class DBConnection
{
static SqlConnection connection;
public void getConnection() {
try {
connection = new SqlConnection("Data Source=PC-DILUKSHAN\\SQLEXPRESS;Initial Catalog=EMS;User ID=user;Password=1234");
connection.Open();
}catch(Exception e){
throw e;
}
}
public DataTable executeSelectQuery(String sql) {
try
{
SqlCommand cmd = new SqlCommand(sql, connection);
cmd.CommandType = CommandType.Text;
// Create a DataAdapter to run the command and fill the DataTable
SqlDataAdapter da = new SqlDataAdapter();
da.SelectCommand = cmd;
DataTable dt = new DataTable();
da.Fill(dt);
return dt;
}
catch (Exception e)
{
throw e;
}
finally {
connection.Close();
}
}
public void executeUpdateQuery(String sql) {
try {
SqlCommand cmd = new SqlCommand(sql, connection);
cmd.ExecuteNonQuery();
}catch(Exception e){
throw e;
}
finally
{
connection.Close();
}
}
}
Use the SqlTransaction object. Here is an example on how to rollback a transaction:
private static void ExecuteSqlTransaction(string connectionString)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
SqlTransaction transaction;
// Start a local transaction.
transaction = connection.BeginTransaction("SampleTransaction");
// Must assign both transaction object and connection
// to Command object for a pending local transaction
command.Connection = connection;
command.Transaction = transaction;
try
{
command.CommandText =
"Insert into Region (RegionID, RegionDescription) VALUES (100, 'Description')";
command.ExecuteNonQuery();
command.CommandText =
"Insert into Region (RegionID, RegionDescription) VALUES (101, 'Description')";
command.ExecuteNonQuery();
// Attempt to commit the transaction.
transaction.Commit();
Console.WriteLine("Both records are written to database.");
}
catch (Exception ex)
{
Console.WriteLine("Commit Exception Type: {0}", ex.GetType());
Console.WriteLine(" Message: {0}", ex.Message);
// Attempt to roll back the transaction.
try
{
transaction.Rollback();
}
catch (Exception ex2)
{
// This catch block will handle any errors that may have occurred
// on the server that would cause the rollback to fail, such as
// a closed connection.
Console.WriteLine("Rollback Exception Type: {0}", ex2.GetType());
Console.WriteLine(" Message: {0}", ex2.Message);
}
}
}
}

SET TRANSACTION error , C# , Oracle

I have procedure in my C# device application app. This is how it looks:
private void stk_crane_start_movement()
{
conn.Open();
OracleCommand cmd = new OracleCommand();
OracleTransaction trans;
trans = conn.BeginTransaction();
cmd.Transaction = trans;
cmd.Connection = conn;
conn.AutoCommit = false;
cmd.CommandTimeout = 0;
cmd.CommandText = "dc.stk_crane_start_movement";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("pn_crane_opr_id_no", OracleDbType.Number).Value = empid.ToString();
cmd.Parameters.Add("pn_crane_movement_id_no", OracleDbType.Number).Value = pn_crane_movement_id_no.ToString();
cmd.Parameters.Add(new OracleParameter("pv_error", OracleDbType.VarChar));
cmd.Parameters["pv_error"].Direction = ParameterDirection.Output;
string pv_error;
cmd.ExecuteNonQuery();
pv_error = cmd.Parameters["pv_error"].Value.ToString();
if (pv_error.ToString() == "")
{
trans.Commit();
trans.Dispose();
conn.Close();
cmd.Dispose();
}
else
{
trans.Rollback();
MessageBox.Show("" + pv_error, "Error");
}
}
I'm getting ORA-01453: SET TRANSACTION must be first statement of transaction at trans = conn.BeginTransaction();
Can someone please explain to me what exactly i'm doing wrong?
I have also tried it like this:
private void stk_crane_start_movement()
{
conn.Open();
OracleCommand cmd = conn.CreateCommand();
OracleTransaction trans;
trans = conn.BeginTransaction(IsolationLevel.ReadCommitted);
cmd.Transaction = trans;
try
{
cmd.CommandText = "dc.stk_crane_start_movement";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("pn_crane_opr_id_no", OracleDbType.Number).Value = empid.ToString();
cmd.Parameters.Add("pn_crane_movement_id_no", OracleDbType.Number).Value = pn_crane_movement_id_no.ToString();
cmd.Parameters.Add(new OracleParameter("pv_error", OracleDbType.VarChar));
cmd.Parameters["pv_error"].Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
trans.Commit();
}
catch
{
pv_error = cmd.Parameters["pv_error"].Value.ToString();
MessageBox.Show("" + pv_error, "Error");
try
{
trans.Rollback();
}
catch (OracleException ex)
{
MessageBox.Show("Rollback failed" + ex, "Exception Error");
}
}
}
But because its not an exception error that I'm expecting it doesn't go through the try catch statement.
I want it to rollback when my pv_error variable is populated. That is why I included a if statement in the first example.
Also I don't have any other transaction before this one..
There is a suggestion here that you may see this error if you already have an open transaction. They advise committing or rolling back anything that's open. Unlike in SQL Server, I have been able to issue prophylactic "COMMIT" statements in Oracle without even knowing whether I have anything open.
The only difference I see between your code and the sample code here is that they tie the command object to the connection before assigning the transaction to it:
OracleCommand command = connection.CreateCommand();
command.Transaction = transaction;
You might want to try that variation. You're not setting an isolation level, so the issue with oracle oci.dll below version 10.2 presumably doesn't apply here.
According to the MSDN docs, your second version correctly uses the transaction. So if you combine that with your if statement, you should be fine:
private void stk_crane_start_movement()
{
conn.Open();
OracleCommand cmd = conn.CreateCommand();
OracleTransaction trans;
trans = conn.BeginTransaction(IsolationLevel.ReadCommitted);
cmd.Transaction = trans;
try
{
cmd.CommandText = "dc.stk_crane_start_movement";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("pn_crane_opr_id_no", OracleDbType.Number).Value = empid.ToString();
cmd.Parameters.Add("pn_crane_movement_id_no", OracleDbType.Number).Value = pn_crane_movement_id_no.ToString();
cmd.Parameters.Add(new OracleParameter("pv_error", OracleDbType.VarChar));
cmd.Parameters["pv_error"].Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
pv_error = cmd.Parameters["pv_error"].Value.ToString();
if (pv_error.ToString() == "")
{
trans.Commit();
trans.Dispose();
conn.Close();
cmd.Dispose();
}
else
{
trans.Rollback();
MessageBox.Show("" + pv_error, "Error");
}

system.invalidoperationexception

I am trying to run this code
public Exception SetData(string Data , long NoOfColumnsAllowed)
{
try
{
con = new SqlCeConnection(conectionstring);
con.Open();
transaction = con.BeginTransaction();
com = new SqlCeCommand();
com.Transaction = transaction;
com.CommandText = "Select count(*) from [Copy]";
com.Connection = con;
sdr = com.ExecuteReader();
while (sdr.Read())
{
noOfColumns = sdr.GetInt32(0);
}
if (noOfColumns > NoOfColumnsAllowed)
{
long NoOfColumsToBeDeleted = noOfColumns - NoOfColumnsAllowed;
com.CommandText = "delete from [Copy] where Sno<=#sno";
com.Parameters.AddWithValue("#sno", NoOfColumsToBeDeleted);
com.ExecuteNonQuery();
}
com.CommandText = "Insert into [Copy] (Data) values (#data)";
com.Parameters.AddWithValue("#data", Data);
com.ExecuteNonQuery();
transaction.Commit();
con.Close();
return null;
}
catch (Exception ex)
{
try
{
transaction.Rollback();
}
catch (Exception)
{
}
con.Close();
return ex;
}
}
Exception Occur -
system.invalidoperationexception : The transaction can not be
committed if there is any opened cursor in the scope of this
transaction . Make sure all the data readers/ result sets are
explicitly closed before committing the change .
I am new with transaction and not able to find any valuable solution about opened cursor. Is there something wrong with code or i have to explicitly close the data reader if yes please tell me how ?
Just call sdr.Close(); right after the while loop since that's what error is complaining about.

Commit multiple SqlCommands with SqlTransaction

I am trying to pass a list of SqlCommand into a member function that holds the connection to the database.
public void CommitAsTransaction(List<SqlCommand> commands) {
SqlTransaction transaction = null;
SqlConnection connection = null;
try {
connection = this.CreateSqlConnection();
connection.Open();
transaction = connection.BeginTransaction("TransactionID");
foreach (SqlCommand cmd in commands) {
cmd.Transaction = transaction;
cmd.Connection = connection;
cmd.ExecuteNonQuery();
}
transaction.Commit();
}
catch (Exception ex) {
transaction.Rollback();
}
connection.Close();
}
This is what I currently have. The error occurs because the command seems to be being executed as in place and the transaction.Commit(); is never reached. I have seen many people doing it like this and am not sure what I am doing wrong.
PS: The issue is that the stored procedures that will be getting executed MUST all be run within a single transaction, I do not control these and they're encrypted, the reason they must be run in a transaction is because they create temp records in a table that has a PK requirement.
Can you use a transaction scope instead ?
Something like:
// place this code inside CommitAsTransaction
using (TransactionScope scope = new TransactionScope())
{
Boolean AllOK = true;
SqlConnection connection = this.CreateSQLConnection();
try
{
connection.Open()
}
catch (Exception e)
{
// deal with it how you need to
AllOK = false;
}
if (AllOK)
{
foreach(SQlCommand cmd in Commands)
{
try
{
cmd.Connection = connection;
cmd.ExecuteNonQuery();
}
catch (Exception e)
{
// Deal with it..
AllOK = false;
break;
}
}
if (AllOK)
{
scope.Complete();
try
{
connection.Close();
}
catch (Exception e)
{
// deal with it
}
}
}
}
Thanks so much. I ended up figuring it out on my own based on other peoples combined answers, as a thank you here is the code I used:
public List<Models.eConnectModels.eConnStatus> CommitAsTransaction(List<SqlCommand> commands)
{
SqlTransaction transaction = null;
SqlConnection connection = null;
List<eConnStatus> ErrorList = new List<eConnStatus>();
try
{
connection = this.CreateSqlConnection();
connection.Open();
transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted, "TransactionID");
foreach (SqlCommand cmd in commands)
{
eConnStatus curErr = new eConnStatus();
cmd.Transaction = transaction;
cmd.Connection = connection;
SqlParameter errorString = cmd.Parameters.Add("#oErrString", SqlDbType.VarChar);
errorString.Direction = ParameterDirection.Output;
errorString.Size = 8000;
SqlParameter errorStatus = cmd.Parameters.Add("#O_iErrorState", SqlDbType.Int);
errorStatus.Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
curErr.ErrorState = (int)cmd.Parameters["#O_iErrorState"].Value;
curErr.ErrorMessage = (string)cmd.Parameters["#oErrString"].Value;
ErrorList.Add(curErr);
}
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
connection.Close();
throw ex;
}
connection.Close();
return ErrorList;
}

Categories