How does SQLTransaction.Commit() work? - c#

A few day ago, I have studied SqlTransaction and I know the purpose of SqlTransaction.Commit() - it should "commit the database transaction." - MSDN.
But HOW DOES IT WORK?
For example: I wrote a piece of code like this:
using (SqlTransaction tran = connection.BeginTransaction())
{
try
{
using (SqlCommand cmd = connection.CreateCommand())
{
cmd.CommandText = msg.command;
cmd.Transaction = tran;
cmd.ExecuteNonQuery();
}
}
catch (Exception)
{
// if all of above have any exception, that's mean my transaction is
// failure and my database has no change.
return false;
}
tran.Commit();
// if all of above have no problems, that's mean my transaction is successful
return true;
connection.Dispose();
}
In this case, SQL Server is on another computer.
I guess: commit method has two periods, Period 1: when I implement tran.Commit(), compiler will signal SQL Server and talk to SQL Server that: "I'm ok, please help me commit (change) data", and then SQL Server will implement compiler's request. Period 2: when SQL Server implement compiler's request completely, implement result will be return to our compiler. When our compiler receive implement result, our compiler will continue compile the next command line ("return true").
But if in second period, the connection is broken and implement result isn't transferred back to our compiler. In this case, our transaction is success or not? Is data persisted in SQL Server or not?
Additional question: my prediction about two period of SQLTransaction.Commit() is true or not?
Thanks!

try
{
using (var conn = new SqlConnection(/* connection string or whatever */))
{
conn.Open();
using (var trans = conn.BeginTransaction())
{
try
{
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = trans;
/* setup command type, text */
/* execute command */
}
trans.Commit();
}
catch (Exception ex)
{
trans.Rollback();
/* log exception and the fact that rollback succeeded */
}
}
}
}
catch (Exception ex)
{
/* log or whatever */
}
and read this also
https://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqltransaction.commit(v=vs.110).aspx

Related

SqlCommand timeout in C# MMO application

I have a C# project that is working with TCP socket in an asynchronous way.
Every request comes from client and ask question from SQL Server stored procedure, opens and closes a SQL connection after ending of question.
I've used this code:
using (var con = new SqlConnection(setting.ConnectionString))
{
try
{
//some codes (edited)
SqlCommand command = new SqlCommand(con);
command.CommandText = "procedurename1";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#name", sb.ToString()));
SqlDataAdapter adapter = new SqlDataAdapter(command);
try
{
adapter.Fill(dataSet);
}
catch (Exception ex)
{
con.Close();
con.Dispose();
throw ex;
}
finally {
con.Close();
con.Dispose();
}
}
catch(Exception ex)
{}
finally
{
con.close();
con.dispose();
}
}
I've used
netstat -a -n | find /c "1433"
to count SQL connections open and close.
Problem is SQL connections count increases and it rarely decreases and count down.
Main problem, is when my program works under lots of requests about 30 minutes, I get
SqlCommand timeout error (default 30 seconds passed)
and after restarting my C# program, the SqlCommand timeout will be gone.
Is this a problem of my program or SQL Server side?
Remember it always calls a stored procedure in SQL Server, not executing query
directly.
main method:
public void main()
{
Task.Factory.StartNew(() =>
{
allDone.Reset();
mySocket.AcceptAsync(e);
allDone.WaitOne();
});
}
public void e_Completed(object sender, SocketAsyncEventArgs e)
{
var socket = (Socket)sender;
ThreadPool.QueueUserWorkItem(HandleTcpRequest, e.AcceptSocket);
e.AcceptSocket = null;
socket.AcceptAsync(e);
}
public void HandleTcpRequest(object state)
{
//do some code and connection to SQL server
DLL.Request httprequest = new DLL.Request(dataSet.Tables[0], fileDt);
DLL.IHttpContext _context = new DLL.HttpContext(httprequest);
_context.GetResults();
}
Main problem, is when my program works under lots of requests about 30 minutes,
To isolate the root problem of the time-out, I suggest testing the sql query of the stored procedure independent of TCP socket calls for 30 minutes
and log the time-out exception details for inspection
Run the following query within 30 minutes to simulate your working environment:
public void RunQuery()
{
using (var con = new SqlConnection(setting.ConnectionString))
{
try
{
//some codes
}
catch(SqlException ex)
{
//test for timeout
if (ex.Number == -2) {
Console.WriteLine ("Timeout occurred");
// log ex details for more inspection
}
}
}
}
Read How to handle the CommandTimeout properly?
As you use async calls, I suggest you to try to use Asynchronous Database Calls With Task-based Asynchronous Programming Model (TAP)
I'm going to take a long-shot based on the way the limited Sql-related code we can see is written since we can't see "//some codes".
I'm going to guess that some of the disposable things like SqlCommand, DataReader, SqlDataAdapter, TransactionScope, etc are not in 'using' blocks, so are holding resources open on the database.
It may also be worth raising the possibility that this kind of problem could be in the code shown in the question or any other program accessing that database, including your own applications and SSMS (e.g. if a developer has an uncommitted transaction running in a window).
P.S. I would suggest deleting everything in the using block except the "//some codes" part.
UPDATE after more code was added
Here is your code after correction. this will ensure that the resources are disposed, which will prevent the leaking resources that are probably causing your problem.
using (var con = new SqlConnection(setting.ConnectionString))
{
//some codes (edited)
using (SqlCommand command = new SqlCommand(con))
{
command.CommandText = "procedurename1";
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add(new SqlParameter("#name", sb.ToString()));
using (var adapter = new SqlDataAdapter(command))
{
adapter.Fill(dataSet);
}
}
}
P.S. don't ever write "throw ex;" from inside a catch ever again. It causes the stack trace to be lost - just use "throw;".

Setting CommandTimeout to solve lock wait timeout exceeded try restarting transaction

I have an error like this
lock wait timeout exceeded try restarting transaction
Maybe I didn't understand it. But I've a solution if I set CommandTimeout=1000 or something higher. I didn't try it in production yet. But I'd like to hear any opinion on this.
// 40 lines of command.Parameters here
command.Parameters.AddWithValue("sample1", sam1);
command.Parameters.AddWithValue("sample2", sam2);
command.Parameters.AddWithValue("sample3", sam2);
try
{
command.ExecuteNonQuery();
}
catch (MySqlException mex)
{
I was receiving "lock wait timeout exceeded try restarting transaction" intermittently. Then I started wrapping everything in transactions and I stopped receiving those errors. This should prevent table locks from remaining after the query is executed.
(Assuming "conn" is a MySqlConnection, "iLevel" is the isolation level you want to use, and "query" contains your query as a string)
int rowCount = 0; // In case you want the number of rows affected
try
{
if (conn.State != ConnectionState.Open)
conn.Open();
MySqlCommand command = new MySqlCommand(query, conn);
using(var transaction = conn.BeginTransaction(iLevel))
{
command.Transaction = transaction;
command.CommandTimeout = int.MaxValue;
// Set parameters etc...
try
{
rowCount = command.ExecuteNonQuery();
transaction.Commit();
}
catch(Exception ex)
{
transaction.Rollback();
// Handle query exception...
}
}
}
catch(Exception ex)
{
// Handle general exception...
}
You could try (just for testing purposes) set transaction isolation level =
"READ COMMITTED" and if this fails try set to "READ UNCOMMITTED"
MySql reference
check for dead lock:
"SHOW ENGINE INNODB STATUS" from the MySQL Command line client (not a
query browser) will give you info on deadlocks.
Deadlocks can also be caused by uncommitted transactions (usually
program bugs) and the person who is running the uncommitted
transaction will not see the problem as they will be working fine
(through their data will not be committed). Quote from here

Save data in multiple databases using a single transaction (multiple connection strings)

I have a server with multiple databases, each database will be connected using a different user ID pswd.
I need to update/insert/delete the records in tables in any of the DBs. If any error occurs - rollback all changes to all databases in the current transaction.
My code looks as below:
string connStrTest1 = "connectionstring to connect to DB1";
string connStrTest2 = "connectionstring to connect to DB2";
string connStrTest3 = "connectionstring to connect to DB3";
//For an example I have created 3 DBs which have the same tables and columns.
string InsertPerson = "insert into Person (Id, Name, City) VALUES (123, 'Jon' , 'England' )";
string InsertPhones = "insert into Phones (Id, Number, SrvcPrvdr) VALUES (123, '+442345678' , 'Some')";
string InsertWork = "INSERT INTO WorkPlace (Id, Office, Address) VALUES (123, 'Soem', 'England' )";
string FailInsertWork = "INSERT INTO WorkPlace (Id, Office, Address) VALUES (999, 'some', 'Australia' )";
static void Main()
{
using (var connTest1 = new SqlConnection(connStrTest1))
{
connTest1.Open();
var transaction = connTest1.BeginTransaction();
try
{
//Update 1st DB here.....
var command = new SqlCommand(InsertPerson, connTest1, transaction);
command.CommandType = System.Data.CommandType.Text;
command.CommandText = InsertPerson;
command.ExecuteNonQuery();
command.CommandText = InsertPhones;
command.ExecuteNonQuery();
command.CommandText = InsertWork;
command.ExecuteNonQuery();
//updating DBs 2 & 3 here
updateRecords();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
throw ex;
}
}
}
private static void updateRecords()
{
//Updating tables in 2nd Test DB
using (var conn = new SqlConnection(connStrTest2))
{
conn.Open();
try
{
var command = new SqlCommand(InsertPerson, conn);
command.CommandType = System.Data.CommandType.Text;
command.ExecuteNonQuery();
command.CommandText = InsertPhones;
command.ExecuteNonQuery();
command.CommandText = InsertWork;
command.ExecuteNonQuery();
}
catch (Exception ex)
{
throw ex;
}
}
//Updating tables in 3rd Test DB
using (var conn = new SqlConnection(connStrTest3))
{
conn.Open();
try
{
var command = new SqlCommand(InsertPerson, conn);
command.CommandType = System.Data.CommandType.Text;
command.ExecuteNonQuery();
command.CommandText = InsertPhones;
command.ExecuteNonQuery();
if (fail)
{
command.CommandText = FailInsertWork;
command.ExecuteNonQuery();
}
else
{
command.CommandText = InsertWork;
command.ExecuteNonQuery();
}
}
catch (Exception ex)
{
throw ex;
}
}
}
In the query FailInsertWork, I have written the query so that it will raise an exception as the Foreign key violation will occur.
Now, I want my program to function as when the update to 3rd DB fails, all the inserts happened previously for the 1st and 2nd DB also should be rolled back.
You can pass the connectionString or an instance of the connection or an instance of the transaction to the updateRecords method.
FYI - I DO NOT WANT TO USE TransactionScope/DTC/System.Transactions.Transaction.
Any other solutions apart from this are highly appreciated.
You must use from TransactionScope.
TransactionScope is a very special and important class in the .NET Framework. Supporting transactions from a code block is the main responsibility of this class.it's easy to use like :
// This function takes arguments for 2 connection strings and commands to create a transaction
// involving two SQL Servers. It returns a value > 0 if the transaction is committed, 0 if the
// transaction is rolled back. To test this code, you can connect to two different databases
// on the same server by altering the connection string, or to another 3rd party RDBMS by
// altering the code in the connection2 code block.
static public int CreateTransactionScope(
string connectString1, string connectString2,
string commandText1, string commandText2)
{
// Initialize the return value to zero and create a StringWriter to display results.
int returnValue = 0;
System.IO.StringWriter writer = new System.IO.StringWriter();
try
{
// Create the TransactionScope to execute the commands, guaranteeing
// that both commands can commit or roll back as a single unit of work.
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString1))
{
// Opening the connection automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();
// Create the SqlCommand object and execute the first command.
SqlCommand command1 = new SqlCommand(commandText1, connection1);
returnValue = command1.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command1: {0}", returnValue);
// If you get here, this means that command1 succeeded. By nesting
// the using block for connection2 inside that of connection1, you
// conserve server and network resources as connection2 is opened
// only when there is a chance that the transaction can commit.
using (SqlConnection connection2 = new SqlConnection(connectString2))
{
// The transaction is escalated to a full distributed
// transaction when connection2 is opened.
connection2.Open();
// Execute the second command in the second database.
returnValue = 0;
SqlCommand command2 = new SqlCommand(commandText2, connection2);
returnValue = command2.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
}
}
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
writer.WriteLine("TransactionAbortedException Message: {0}", ex.Message);
}
catch (ApplicationException ex)
{
writer.WriteLine("ApplicationException Message: {0}", ex.Message);
}
// Display messages.
Console.WriteLine(writer.ToString());
return returnValue;
}
Check this on MSDN
If I understand correctly you're trying to make your .net code manage multiple recipient databases.
One approach would be to switch to .Net generated Ids. I suggest guid.
Then create a managing database, so that you may fix/correct the recipient databases in the event of an application interruption.
Transaction information should be inserted into the managing database before it is executed against recipient databases and should be deleted once all databases successfully executed the transaction. You could even place a bit column for each database.
In the event of interruption you need only check the started transactions in the managing database and decide how to correct the incomplete transactions.
Depending on how robust the managing database is, you could even create a windows service that catches up a recipient database that was offline once it comes back online.

SQLTransaction has completed error

I got following error once in my application.
This SQLTransaction has completed; it is no longer usable
Stack Trace is attached below – It says about Zombie Check and Rollback.
What is the mistake in the code?
Note: This error came only once.
UPDATE
From MSDN - SqlTransaction.Rollback Method
A Rollback generates an InvalidOperationException if the connection is terminated or if the transaction has already been rolled back on the server.
From Zombie check on Transaction - Error
One of the most frequent reasons I have seen this error showing up in various applications is, sharing SqlConnection across our application.
CODE
public int SaveUserLogOnInfo(int empID)
{
int? sessionID = null;
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlTransaction transaction = null;
try
{
transaction = connection.BeginTransaction();
sessionID = GetSessionIDForAssociate(connection, empID, transaction);
//Other Code
//Commit
transaction.Commit();
}
catch
{
//Rollback
if (transaction != null)
{
transaction.Rollback();
transaction.Dispose();
transaction = null;
}
//Throw exception
throw;
}
finally
{
if (transaction != null)
{
transaction.Dispose();
}
}
}
return Convert.ToInt32(sessionID,CultureInfo.InvariantCulture);
}
Stack Trace
REFERENCE:
What is zombie transaction?
Zombie check on Transaction - Error
SqlTransaction has completed
http://forums.asp.net/t/1579684.aspx/1
"This SqlTransaction has completed; it is no longer usable."... configuration error?
dotnet.sys-con.com - SqlClient Connection Pooling Exposed
Thread abort leaves zombie transactions and broken SqlConnection
You should leave some of the work to compiler, to wrap that in a try/catch/finally for you.
Also, you should expect that Rollback can occasionally throw an exception, if a problem occurs in Commit stage, or if a connection to server breaks. For that reason you should wrap it in a try/catch.
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);
}
This is copied exactly from MSDN documentation page for Rollback method.
I see that you're worried that you have a zombie transaction. In case you pasted, it doesn't sound like you have a problem. You're transaction has been completed, and you should no longer have anything to do with it. Remove references to it if you hold them, and forget about it.
From MSDN - SqlTransaction.Rollback Method
A Rollback generates an InvalidOperationException if the connection is terminated or if the transaction has already been rolled back on the server.
Rethrow a new exception to tell user that data may not have been saved, and ask her to refresh and review
Note: This error came only once.
then it is very hard to say much; it could be simply that the // Other Code etc simply took to long, and the entire thing got killed. Maybe your connection died, or an admin deliberately killed it because you were blocking.
What is the mistake in the code?
over-complicating it; it can be much simpler:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using(var transaction = connection.BeginTransaction())
{
try
{
sessionID = GetSessionIDForAssociate(connection, empID, transaction);
//Other Code
transaction.Commit();
}
catch
{
transaction.Rollback();
throw;
}
}
}
much less code to get wrong.
I use code below can reproduce this error, I use 1000 tasks to execute Sql, after about 300 tasks Successfully Completed, lots of exception about timeout error start to occur on ExecuteNonQuery(),
then next error This SqlTransaction has completed will occur on transaction.RollBack(); and its call stack also contains ZombieCheck().
(If single program with 1000 tasks pressure not enough, you can execute multiple compiled exe file at the same time, or even use multi computers execute to one DataBase.)
So I guess one of the reason cause this error can be something wrong in Connection, then cause the transaction error happens as well.
Task[] tasks = new Task[1000];
for (int i = 0; i < 1000; i++)
{
int j = i;
tasks[i] = new Task(() =>
ExecuteSqlTransaction("YourConnectionString", j)
);
}
foreach (Task task in tasks)
{
task.Start();
}
/////////////
public void ExecuteSqlTransaction(string connectionString, int exeSqlCou)
{
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
SqlTransaction transaction;
// Start a local transaction.
transaction = connection.BeginTransaction();
// Must assign both transaction object and connection
// to Command object for a pending local transaction
command.Connection = connection;
command.Transaction = transaction;
try
{
command.CommandText =
"select * from Employee";
command.ExecuteNonQuery();
// Attempt to commit the transaction.
transaction.Commit();
Console.WriteLine("Execute Sql to database."
+ exeSqlCou);
}
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);
}
}
}
}
Besides I find if I commit twice sequentailly will invoke this exception as well.
transaction.Commit();
transaction.Commit();
Or if Connection Closed before commit also invoke this error.
connection.Close();
transaction.Commit();
Update:
I find it strange that I create another new table and insert 500 thousand data to it,
then use 100000 tasks with select * from newtable sql, running 5 programs at the same time, this time the Timeout Error occur, but when transaction.Rollback() it didn't invoke the SQLTransaction has completed error.
but if the Timeout Error occur, jump into the catch block, and in the catch block do transaction.Commit() again, the SQLTransaction has completed error will happen.
I have experienced this error once and i was stuck and unable to know what is going wrong. Actually i was deleting a record and in the Stored procedure i was not deleting its child and specially the delete statement in Stored Procedure was inside the Transaction boundary. I removed that transaction code from stored procedure and got rid of getting this Error of “This SqlTransaction has completed; it is no longer usable.”
This message is simply because that you wrote code that throws an exception after the transaction has been already committed successfully.Please try to check the code you wrote after the Commit method or you can handle it by using Try..Catch and finally Blocks :) .

Entity Framework: How to put multiple stored procedures in a transaction?

I did a lot search already but couldn't find a straight anwser.
I have two stored procedures and they both were function imported to the DBContext object
InsertA()
InsertB()
I want to put them in a transaction. (i.e. if InsertB() failed, rolled back InsertA())
How do I do that? Can I just declare a TransactionScope object and wrap around the two stored procedures?
Thanks
You need to enlist your operations in a transaction scope, as follows:
using(TransactionScope tranScope = new TransactionScope())
{
InsertA();
InsertB();
tranScope.Complete();
}
On error, the transaction scope will automatically be rolled back. Of course, you still need to handle exceptions and do whatever your exception handling design dictates (log, etc). But unless you manually call Complete(), the transaction is rolled back when the using scope ends.
The transaction scope will not be promoted to a distributed transaction unless you open other database connections in the same transaction scope (see here).
This is important to know because otherwise you would need to configure MSDTC on all your servers involved in this operation (web, middle tier eventually, sql server). So, as long as the transaction isn't promoted to a distributed one, you'll be fine.
Note:
In order to fine-tune your transaction options, such as timeouts and isolation levels, have a look at this TransactionScope constructor. Default isolation level is serializable.
Additional sample: here.
You can use the TransactionScope object or you can use the SqlConnection.BeginTransaction Method. Be careful using TransactionScope, transactions can be esculated to distributed transactions when calling stored procedures in a different database. Distributed
transactions can be resource intensive.
How to use sqlConnection.BeginTransaction...(http://msdn.microsoft.com/en-us/library/86773566.aspx)
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);
}
}
}
}
How to use TransactionScope...(http://msdn.microsoft.com/en-us/library/system.transactions.transactionscope.aspx)
// This function takes arguments for 2 connection strings and commands to create a transaction
// involving two SQL Servers. It returns a value > 0 if the transaction is committed, 0 if the
// transaction is rolled back. To test this code, you can connect to two different databases
// on the same server by altering the connection string, or to another 3rd party RDBMS by
// altering the code in the connection2 code block.
static public int CreateTransactionScope(
string connectString1, string connectString2,
string commandText1, string commandText2)
{
// Initialize the return value to zero and create a StringWriter to display results.
int returnValue = 0;
System.IO.StringWriter writer = new System.IO.StringWriter();
try
{
// Create the TransactionScope to execute the commands, guaranteeing
// that both commands can commit or roll back as a single unit of work.
using (TransactionScope scope = new TransactionScope())
{
using (SqlConnection connection1 = new SqlConnection(connectString1))
{
// Opening the connection automatically enlists it in the
// TransactionScope as a lightweight transaction.
connection1.Open();
// Create the SqlCommand object and execute the first command.
SqlCommand command1 = new SqlCommand(commandText1, connection1);
returnValue = command1.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command1: {0}", returnValue);
// If you get here, this means that command1 succeeded. By nesting
// the using block for connection2 inside that of connection1, you
// conserve server and network resources as connection2 is opened
// only when there is a chance that the transaction can commit.
using (SqlConnection connection2 = new SqlConnection(connectString2))
{
// The transaction is escalated to a full distributed
// transaction when connection2 is opened.
connection2.Open();
// Execute the second command in the second database.
returnValue = 0;
SqlCommand command2 = new SqlCommand(commandText2, connection2);
returnValue = command2.ExecuteNonQuery();
writer.WriteLine("Rows to be affected by command2: {0}", returnValue);
}
}
// The Complete method commits the transaction. If an exception has been thrown,
// Complete is not called and the transaction is rolled back.
scope.Complete();
}
}
catch (TransactionAbortedException ex)
{
writer.WriteLine("TransactionAbortedException Message: {0}", ex.Message);
}
catch (ApplicationException ex)
{
writer.WriteLine("ApplicationException Message: {0}", ex.Message);
}
// Display messages.
Console.WriteLine(writer.ToString());
return returnValue;
}

Categories