SET TRANSACTION error , C# , Oracle - c#

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

Related

Oracle Update Command Freezing when running it in C#

I have an update statement that works fine when I run it from SQL Developer. However, when I try to run it in C#, the program will freeze on the line that executes the command and not output anything. I have it defined as follows below:
private static OracleCommand cmd = new OracleCommand();
private static OracleConnection conn = new OracleConnection();
conn.ConnectionString = Properties.Settings.Default.myconstring;
cmd.Connection = conn;
cmd.CommandText = "UPDATE mytable SET PARAM1 = :param1 WHERE PARAM2 = :param2";
cmd.CommandType = CommandType.Text;
cmd.BindByName = true;
cmd.Parameters.Clear();
cmd.Parameters.Add(new OracleParameter(":param1", OracleDbType.Single)).Value = param1Val;
cmd.Parameters.Add(new OracleParameter(":param2", OracleDbType.Int32)).Value = param2Val;
conn.Open();
try {
cmd.ExecuteNonQuery(); //Freezes here
} catch(Exception e) {
MessageBox.Show(e.ToString());
}
conn.Close();
I have verified my values being put in are correct.

C# Oracle - Execute Stored Procedures in sequence

Hi guys I'm having troubles trying execute Stored procedures on cascade, I need some help. Let's see the scenario:
I have a father table, let's call it "REQUEST" and a child table, "REQUEST_DETAILS"
Now from C# I know how to execute an Oracle SP, but I donĀ“t know how to execute two in a chain, without commit until the end of all.
I need to insert the father table data and after get the generated REQUEST.ID to start to insert the children data with the REQUEST.ID
So the first stored procedure will insert the REQUEST data and the second one will insertrt the REQUEST_DETAIL data but if something goes wrong I want to rollback all the transactions.
There is a way to do this of a simple way ?
Here is my code any help will be usefull.
public Bool SaveRequest(Request newRequestData)
{
var connection = new connection();
bool isSuccess = true;
OracleConnection Conn = connection._GetInstance();
OracleCommand Cmd = new OracleCommand();
Conn.Open();
Cmd.Connection = Conn;
Cmd.CommandType = CommandType.StoredProcedure;
Cmd.CommandText = "PackageRequests.InsertNewRequest";
Cmd.BindByName = true;
//IN PARAM
Cmd.Parameters.Add(new OracleParameter("P_LOCATION", OracleDbType.Varchar2, newRequestData.location, ParameterDirection.Input));
Cmd.Parameters.Add(new OracleParameter("P_PCSTOTAL", OracleDbType.Int32, newRequestData.pcsTotal, ParameterDirection.Input));
Cmd.Parameters.Add(new OracleParameter("P_STATUS", OracleDbType.Int32, newRequestData.status, ParameterDirection.Input));
//Out Param
Cmd.Parameters.Add(new OracleParameter("P_NEW_ID", OracleDbType.Int32)).Direction = ParameterDirection.Output;
OracleTransaction transaction = Conn.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
Cmd.ExecuteNonQuery();
//New request_id
string newId = Convert.ToString(Cmd.Parameters["P_NEW_ID"].Value);
//Here I think goes the logic for execute the another procedure that will insert the data into REQUEST_DETAIL
/***
foreach(var item in newRequestData.List)
{
//Insert request_detail_Data()
}
***/
//after all -- transaction.Commit();
}
catch (OracleException ex)
{
//If something goes wrong rollback.
transaction.Rollback();
isSuccess = false;
}
finally
{
Conn.Close();
}
return isSuccess;
}
I found the solution to do this the trick is on pass the Oracle Connection like parameter to the another functions and when all is done commit and if something fails rollback, from the initial function. I leave an example I hope will be helpful for someone.
public Bool SaveRequest(Request newRequestData)
{
var connection = new connection();
bool isSuccess = true;
OracleConnection Conn = connection._GetInstance();
OracleCommand Cmd = new OracleCommand();
Conn.Open();
Cmd.Connection = Conn;
Cmd.CommandType = CommandType.StoredProcedure;
Cmd.CommandText = "PackageRequests.InsertNewRequest";
Cmd.BindByName = true;
// IN PARAMETERS...
Cmd.Parameters.Add(new OracleParameter("P_LOCATION", OracleDbType.Varchar2, newRequestData.location, ParameterDirection.Input));
// OUT PARAMETER (Here I recover the master table ID)
Cmd.Parameters.Add(new OracleParameter("P_NEW_ID", OracleDbType.Int32)).Direction = ParameterDirection.Output;
// Initialize the Transaction
OracleTransaction transaction = Conn.BeginTransaction(IsolationLevel.ReadCommitted);
try
{
//Execute the first SP
Cmd.ExecuteNonQuery();
string newId = Convert.ToString(Cmd.Parameters["P_NEW_ID"].Value);
// Calls another function and pass Oracle Connection, and master table ID like parameters
InsertRequestDetail(Conn, newId);
transaction.Commit();
}
catch (OracleException ex)
{
//If something goes wrong rollback.
transaction.Rollback();
isSuccess = false;
}
finally
{
Conn.Close();
}
return isSuccess;
}
private void InsertRequestDetail(OracleConnection Conn, string newId)
{
OracleCommand Cmd = new OracleCommand();
Cmd.Connection = Conn;
Cmd.CommandType = CommandType.StoredProcedure;
Cmd.CommandText = "MY_PACKAGE.AnotherSPName";
Cmd.BindByName = true;
//IN - OUT PARAMS
Cmd.Parameters.Add(new OracleParameter("...
Cmd.ExecuteNonQuery();
}

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;

InvalidOperationException The connection was not closed. The connection's current state is open

Why does this code throw an Invalid Operation Exception?
private SqlCommand cmd; // initialized in the class constructor
public void End(string spSendEventNotificationEmail) {
try {
cmd.CommandText = spSendEventNotificationEmail;
cmd.Parameters.Clear();
cmd.Parameters.Add("#packetID", SqlDbType.Int).Value = _packetID;
cmd.Parameters.Add("#statusID", SqlDbType.Int).Value = _statusID;
cmd.Parameters.Add("#website", SqlDbType.NVarChar, 100).Value = Tools.NextStep;
cmd.Connection.Open();
cmd.ExecuteNonQuery();
} finally {
cmd.Connection.Close();
cmd.Parameters.Clear();
cmd.Dispose();
}
endCall = true;
}
You're trying to open a connection which is already open, this results in exception.
Solution 1 (recommended):
Inspect your code, check all the parts where cmd.Connection connection is opened and ensure that it's always closed properly.
Solution 2 (quick'n'dirty fix):
before line
cmd.Connection.Open();
add the following check/cleanup code:
if (cmd.Connection.State == ConnectionState.Open)
{
cmd.Connection.Close();
}
There's very little need for keeping the Sql* objects at the class level, especially based on what you're showing. You'll also lose the benefits of connection pooling by attempting to do it yourself.
With this method, you remove the possibility of your error because you're not sharing any objects
private readonly _connectionString = "...";
public void End(string spSendEventNotificationEmail) {
using(var conn = new SqlConnection(_connectionString))
using(var cmd = conn.CreateCommand())
{
cmd.CommandText = spSendEventNotificationEmail;
cmd.Parameters.Add("#packetID", SqlDbType.Int).Value = _packetID;
cmd.Parameters.Add("#statusID", SqlDbType.Int).Value = _statusID;
cmd.Parameters.Add("#website", SqlDbType.NVarChar, 100).Value = Tools.NextStep;
conn.Open();
cmd.ExecuteNonQuery();
}
endCall = true;
}

ExecuteNonQuery requires the command to have a transaction

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

Categories