Commit multiple SqlCommands with SqlTransaction - c#

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

Related

How to maintain transaction handling executing two stored procedure from C# code

I one method in my class Which executing two stored procedure and insert data into three tables. How to handle transaction in that case?
For Example:
public class InsertClass
{
public void AddData()
{
try
{
Execute Sp1;
Execute Sp2;
}
catch (Exception ex)
{
throw ex;
}
}
}
Both SP are for insert.
Try this:
SqlConnection con = new SqlConnection(conString);
SqlCommand cmd1,cmd2;
con.Open();
cmd1 = new SqlCommand("sp1",con);
cmd2 = new SqlCommand("sp2",con);
SqlTransaction trans = con.BeginTransaction();
cmd1.Transaction = trans;
cmd2.Transaction = trans;
try
{
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
trans.Commit();
}
catch (Exception)
{
trans.Rollback();
}
Try this code:
connection.Open();
SqlTransaction transaction = connection.BeginTransaction();
try
{
Execute Sp1;
Execute Sp2;
}
catch (Exception)
{
transaction.Rollback();
connection.Close();
}
transaction.Commit();

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

BeginTransaction() on sqltransaction freezes, how to use try catch to solve it?

I'm using SqlTransaction transaction = connection.BeginTransaction() in my C# application to get data from SQLServer.
The problem I have when there is no connection to server (no internet connection), then my application just freezes and stops working, no exception, no error, just freezes.
I tried use try catch to catch error, but even then application just freezes, and the only option is to kill application. Can anyone help me to catch this error and istead of freezeing give me error - like Connection to server failed. Please check internet connection.
Here is my class code for connection:
public static class RequestID
{
// Methods
public static int GetID(string server, string database, string user, string pass)
{
int num = 0;
using (SqlConnection connection = new SqlConnection(string.Format("server={0};database={1};uid={2};pwd={3};Connect Timeout=900", new object[] { server, database, user, pass })))
{
SqlCommand command = new SqlCommand("SELECT Value_Int FROM Param WHERE code= 'counter'");
SqlCommand command2 = new SqlCommand("UPDATE Param SET Value_Int = Value_Int + 1 WHERE code= 'counter'");
if (connection.State != ConnectionState.Open)
{
connection.Open();
}
try
{
using (SqlTransaction transaction = connection.BeginTransaction())
{
try
{
command.Connection = connection;
command.Transaction = transaction;
command2.Connection = connection;
command2.Transaction = transaction;
num = (int)command.ExecuteScalar();
command2.ExecuteNonQuery();
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
finally
{
if (connection.State != ConnectionState.Closed)
{
connection.Close();
}
}
}
}
catch (Exception ex)
{
throw;
}
return num;
}
}
}
Problem is in line:
using (SqlTransaction transaction = connection.BeginTransaction())
Thanks in advance.
P.S. Application works just fine, the only problam so far then connection to internet lost. I wasn't able to find solution...
Your problem is because
connection.Open();
is outside of your try block so you will never hit the catch.
Move that inside your try block.

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.

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