Failed TransactionScope still completing - c#

For a long time I've been using the following structure when using TransactionScope.
using(var con = new SqlConnection(CONNECTIONSTRING))
{
con.Open();
foreach(var file in files)
{
try
{
using(var tran = new TransactionScope())
{
using(var cmd = new SqlCommand(CMDTEXT1, con)
{
//add parameters
//ExecuteScalar or ExecuteNonQuery
}
//...repeat above as needed for other inserts
//...run other C# methods like FTP upload
tran.Complete();
}
}
catch(Exception ex)
{
//log exception
}
}
}
Today, my FTP server was down but my SQL server was up. The error was properly caught and tran.Complete never ran. I would expect tran to rollback the changes but instead it had inserted all my commands.
Is this because the SqlConnection is not within the TransactionScope? Do I have to start a new SqlConnection for each transaction? I want to retain this structure so I can reuse the SqlConnection so I considered replacing new TransactionScope with con.BeginTransaction but I read that it doesn't allow the mixing of C# (I need to run C# methods like FTPUpload). Did I misintrepret this?
Answered by comments
Is this because the SqlConnection is not within the TransactionScope? Yes
Do I have to start a new SqlConnection for each transaction? Yes
Did I misintrepret this? I confused EntityFramework.BeginTransaction with SqlConnection.BeginTransaction.

Related

How to check if a SqlCommand is within a transaction or not

I have a standard routine for executing SqlCommand with an exception handler. But if an exception is thrown within this routine then I can't make an rollback if this standard routine is called within a transaction. So how can I check that this standard routine is placed inside a transaction or not? What is the proper way? I have googled a lot so far...
Do I have to thrown a new exception in my exception handler for the standard routine for forcing the overall transaction to rollback?
My standard routine looks like:
try
using (SqlConnection con = new SqlConnection(dbCon))
{
using (SqlCommand cmd = new SqlCommand(SQL, con))
{
con.Open();
cmd.CommandTimeout = 600;
cmd.ExecuteScalar();
}
}
}
catch (Exception ex)
{
// do some stuff here
maybe check for existence in a transaction here
}
My overall transaction look like this:
using (SqlConnection con = new SqlConnection(dbCon))
{
con.Open();
string TransactionName = "TransactionName";
using (SqlTransaction sqlTransaction = con.BeginTransaction(TransactionName))
{
try
{
// do some stuff here and call the standard routine here several times...
}
catch (Exception vDBException)
{
DB.getInstance().RollbackTransaction(sqlTransaction, TransactionName);
}
}
}
I have tried to make use of select ##trancount with no success.
I have also tried to check sys.sysprocesses from SQL Server with no success.
I really hope that someone can show me the right direction.

How to roll back if all files have not been uploaded

Ok so i have a webform and 5 FileUpload control..a user can upload any number of files from 1 to 5 but if anyone of these files does not get uploaded then I want to rollback everything...
For ex:
if user has selected 4 files and if something unexpected occurs at 4th then I want to remove or rollback all the previous 3 file uploads..
I tried this..
try
{
using (TransactionScope scope = new TransactionScope())
{
dboperation dbinsert=new dboperation();
if (file1.ContentLength > 0)
{
.......
.......
dbo.insert(bytes, lastid, file2.FileName);
}
if (file2.ContentLength > 0)
{
.......
.......
dbo.insert(bytes, lastid, file2.FileName);
}
if (file3.ContentLength > 0)
{
.......
.......
dbo.insert(bytes, lastid, file2.FileName);
}//till ...file5
scope.Complete();
}//end of transactionscope
}
catch { }
'dboperation' is a class in c# file and 'dbinsert' is a method which is executing an insert stored procedure. My guess is that I need to use Transaction Scope but I am not sure if I am correct and even if I am how am I supposed to achieve this?
You need to implement transaction. You should start the transaction before inserting first one and catch any errors that occur. in case of error you have to rollback the transaction. And if all goes well you can commit your transaction.
You should also move you connection outside the dboperation or make a method in dboperation that takes connection from outside and uses that
for this you need to use Transaction something like this. I give you example.
class WithTransaction
{
public WithTransaction()
{
string FirstQuery = "INSERT INTO Table1 VALUES('Vineeth',24)";
string SecondQuery = "INSERT INTO Table2 VALUES('HisAddress')";
int ErrorVar = 0;
using (SqlConnection con = new SqlConnection("your connection string"))
{
try
{
SqlCommand ObjCommand = new SqlCommand(FirstQuery, con);
SqlTransaction trans;
con.Open();
trans = con.BeginTransaction();
ObjCommand.Transaction = trans;
//Executing first query
//What ever operation on your database do here
ObjCommand.ExecuteNonQuery(); //Exected first query
ObjCommand.CommandText = SecondQuery;
ObjCommand.ExecuteNonQuery(); //Exected first query
//Everything gone fine. So commiting
ObjCommand.Transaction.Commit();
}
catch (Exception ex)
{
Console.WriteLine("Error but we are rollbacking");
ObjCommand.Transaction.Rollback();
}
con.Close();
}
}
}
Or you can use TransactionScope
check this Link
TransactionScope
I hope this will help you.

How to handle multiple SQL transactions through different .net(c#) threads

I have the following method for bulk insert of the data in tables.
First my code populates the data in data tables and inserts this data in corresponding tables using the SqlBulkCopy claas of the .net .
I have requirement that data should get inserted in all tables or neither of them.
For this I have used SqlTransaction class of the .net.
Scenario is, multiple threads execute the following code block at the same time.
public void Import()
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
SqlTransaction sqlTrans =null;
try
{
sqlConnection.Open();
sqlTrans = sqlConnection.BeginTransaction(IsolationLevel.Serializable)
SqlCommand cmd = sqlConnection.CreateCommand();
cmd.CommandText = "select top 1 null from lockTable with(xlock)";
cmd.CommandTimeout = 3600*3;
cmd.Transaction = sqlTrans;
SqlDataReader reader = cmd.ExecuteReader();
foreach (DataTable dt in DataTables)
{
ImportIntoDatabase(sqlConnection, dt, sqlTrans);
}
reader.Close();
sqlTrans.Commit();
}
catch (Exception ex)
{
sqlTrans.Rollback();
throw ex;
}
}
}
private void ImportIntoDatabase(SqlConnection sqlConn, DataTable dt, SqlTransaction sqlTrans)
{
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConn, SqlBulkCopyOptions.Default, sqlTrans))
{
bulkCopy.BulkCopyTimeout = dt.Rows.Count * 10;
try
{
bulkCopy.DestinationTableName = dt.TableName;
bulkCopy.WriteToServer(dt);
}
catch (Exception ex)
{
throw ex;
}
}
}
To handle this concurrency, I have created one dummy table(table named 'lockTable'), in the database where the other table resides(the bulk insert tables). I am getting exclusive lock on this dummy table in the SqlTransaction having command time out as high as 3 hours.
Problem:
I am getting following exception
: Cannot access destination table 'Tbl1' (tbl1 is the table for bulk inserting)
followed by another exception, while rolling back the transaction in catch block
: Error While executing activity The server failed to resume the transaction. Desc:3a00000001.
The transaction active in this session has been committed or aborted by another session.
Can any one help me for this weird behavior of the code. I have already searched a lot on this issue on the internet, but I have not found anything helpful for me.
In Import (DataTable dt in DataTables) is not going to be thread safe.
sqlConnection already has an active reader from Import so that connection cannot be used in ImportIntoDatabase.
Echo smp - if you are locking a table then why multi threads?
If you want to build up the input while the SQL inserts are taking place then use
Asynch method such as SqlCommand.BeginExecuteReader. You get asynch without the overhead of a thread. And DataTables are relatively slow. I insert using TVP and light weight objects. A huge factor in insert performance is index fragmentation. If at all possible insert order by the order of the clustered index. The loop is simple build input, wait for asynch, run asych. Or build input may be read input from a queue. SQL insert to the same table(s) are typically not going to go faster in parallel. My experience is ordered serial inserts with no gap in time between inserts.
I got my problem solved out.
Following are the changes which I have made to my Import method
public void Import()
{
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
using (SqlTransaction sqlTrans = sqlConnection.BeginTransaction())
{
try
{
SqlCommand cmd = sqlConnection.CreateCommand();
cmd.CommandText = "select top 1 null from lockTable with(xlock)";
cmd.CommandTimeout = LOCK_TIME_OUT;
cmd.Transaction = sqlTrans;
SqlDataReader reader = cmd.ExecuteReader();
foreach (DataTable dt in DataTables)
{
ImportIntoDatabase(sqlConnection, dt, sqlTrans);
}
reader.Close();
sqlTrans.Commit();
}
catch (Exception ex)
{
sqlTrans.Rollback();
throw ex;
}
}
sqlConnection.Close();
}
}
If multiple threads have access to the "Import" Method, then shouldn't you be locking the content of this method?
I don't think you need a dummy table, you just need to lock the two methods above.
I would also mention that you should join all threads, so that you can tell when they have finished.

SqlCommand.Dispose() before SqlTransaction.Commit()?

would it work to dispose a command assigned to a transaction before the transaction is committed? I tested the following code myself, and it seems to have worked fine, but this is a rather small example, so I'm looking for confirmation if someone positively knows.
internal static void TestTransaction()
{
try
{
Program.dbConnection.Open();
using (SqlTransaction transaction = Program.dbConnection.BeginTransaction())
{
Boolean doRollback = false;
for (int i = 0; i < 10; i++)
{
using (SqlCommand cmd = new SqlCommand("INSERT INTO [testdb].[dbo].[transactiontest] (testvalcol) VALUES (#index)", Program.dbConnection, transaction))
{
cmd.Parameters.AddWithValue("#index", i);
try
{
cmd.ExecuteNonQuery();
}
catch (SqlException)
{
doRollback = true;
break;
}
}
}
if (doRollback)
transaction.Rollback();
else
transaction.Commit();
}
}
finally
{
Program.dbConnection.Close();
}
}
The connection, transaction and command objects are just vehicles to send commands to a database. Once a command is executed the database has received it. Whatever you do with the command object afterwards, dispose it, burn it, or shoot it to the moon, this fact does not change. (It can only be rolled back).
You can create and dispose as many commands as you like within the scope of one SqlConnection (with or without SqlTransaction). And you can start and dispose as many transactions as you like within one SqlConnection. To demonstrate this, see:
using (var conn = new SqlConnection(#"server=(local)\sql2008;database=Junk;Integrated Security=SSPI"))
{
conn.Open();
// Repeat this block as often as you like...
using (var tran = conn.BeginTransaction())
{
using (var cmd = new SqlCommand("INSERT INTO Mess VALUES ('mess1')", conn, tran))
{
cmd.ExecuteNonQuery(); // Shows in Sql profiler
}
tran.Commit(); // Commits
}
using (var cmd = new SqlCommand("INSERT INTO Mess VALUES ('mess2')", conn))
{
cmd.ExecuteNonQuery(); // Executes and commits (implicit transaction).
}
}
Of course, for healthy code you need to dispose of all objects in the correct order. Disposing a command after disposing a SqlConnection may cause the connection object to stay in memory.
Yes, it's probably safe. The using() is closing the Command, not the Connection.
But you should put that Connection in another using() block or in a try/finally construct.
Confirmed, this works very well and (at least here at our company) is even considered the correct approach.
create connection
create transaction
create command(s), use the transaction, execute
dispose command(s)
commit transaction
dispose connection

When does a db transaction written in ADO.NET actually begin?

One of the key things in database-intensive applications is to keep the transactions as short as possible.
Today I was wondering when this transaction would actually begin:
using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
sqlConnection.Open();
/*(1)*/ SqlTransaction sqlTransaction = sqlConnection.BeginTransaction(IsolationLevel.ReadUncommitted);
//Perform some stuff
//...
/*(2)*/ using (SqlCommand command = new SqlCommand(sqlQuery, sqlConnection, sqlTransaction))
{
//Some other stuff
//...
try
{
/*(3)*/sqlCommand.ExecuteNonQuery();
//More irrelevant code
//...
sqlCommand.CommandText = otherQuery;
sqlCommand.ExecuteNonQuery();
sqlTransaction.Commit();
}
catch(Exception)
{
sqlTransaction.Rollback();
throw;
}
}
}
In step (1), (2) or (3)? Ideally it should be in step 3.
The transaction starts at point 3, the first time you exectue a command on the connection.
You can verify this using SQL Server Profiler.

Categories