Contact
Send mail to the author(s) Email Me

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Sign In
Navigation

Tag Cloud
.NET Framework (31) AJAX (9) ASP.NET (16) ASP.NET MVC (3) C# (32) Cloud (2) Database (6) Dev Community (2) Dev Tools (5) Enterprise Library (1) Futures (2) General (6) Javascript (7) LINQ (2) Mobile (1) MSDTC (5) Quotes (3) SQL (3) Transactions (4) WAS (2) WCF (19) WIF (1) Visual Studio (3)

Archive
<February 2010>
SunMonTueWedThuFriSat
31123456
78910111213
14151617181920
21222324252627
28123456
78910111213

Categories

Blogroll
Home Feed your aggregator (RSS 2.0)
# Thursday, December 04, 2008

Unless you are working on a extremely simple or read-only application, transactions are a must. Using the System.Transactions namespace is the easiest and most efficient way to maintain system consistency when dealing with multiple calls or multiple resources. Although System.Transactions arrived in .NET in the 2005 product, it is still a relatively unknown part of the framework. System.Transactions was designed by the Indigo team in preparation for WCF. It is not compatible with Win98 or WinME, but most people are incompatible with Win98 and WinME so it works out just fine.

Before System.Transactions, we only had access to System.Data.SqlClient.SqlTransaction or a true SQL transaction using BEGIN/ROLLBACK/COMMIT TRAN. Using SQL transactions, you are stuck with only being able to update DB records as part of your transaction. If you wanted to change a cached value in your app in addition to the SQL updates in the same transaction then you would be out of luck. This also required a lot of transaction code in your stored procedures, writing stored procedures that can be called independently or part of transaction made for very messy stored procedures and often led to multiple stored procedures that served the same purpose.

Using the SqlTransaction class was also messy. The most important restriction is that you need to have all database calls inside the same SqlConnection. This does not work well for a well-designed N-tier application. The other problem is that you need to handle your own non-DB transaction logic inside the same SqlTransaction and conditionally commit/rollback as necessary. This all tends to lead to several try-catch statements within one SqlTransaction. Handling the plumbing to manually add each SqlCommand to the transaction gets old quickly too.

Using SqlTransaction

   27 string connectionString = ConfigurationManager.ConnectionStrings["Testing"].ConnectionString;

   28 using (SqlConnection con = new SqlConnection(connectionString))

   29 {

   30     SqlTransaction tran = null;

   31     try

   32     {

   33         con.Open();

   34         tran = con.BeginTransaction();

   35         using (SqlCommand cmd = new SqlCommand("usp_ErrorLog_Insert", con))

   36         {

   37             cmd.Transaction = tran;

   38             cmd.CommandType = System.Data.CommandType.StoredProcedure;

   39             cmd.Parameters.AddWithValue("Message", "Testing 1");

   40             cmd.Parameters.AddWithValue("UserID", 5150);

   41             cmd.ExecuteNonQuery();

   42         }

   43 

   44         using (SqlCommand cmd = new SqlCommand("usp_ErrorLog_Insert", con))

   45         {

   46             cmd.Transaction = tran;

   47             cmd.CommandType = System.Data.CommandType.StoredProcedure;

   48             cmd.Parameters.AddWithValue("Message", "Testing 2");

   49             cmd.Parameters.AddWithValue("UserID", 5150);

   50             cmd.ExecuteNonQuery();

   51         }

   52 

   53         tran.Commit();

   54     }

   55     catch

   56     {

   57         if (tran != null) tran.Rollback();

   58     }

   59     finally

   60     {

   61         con.Close();

   62     }

   63 }

 

System.Transactions liberated us from the mundane SqlClient code and repetitive try-catches. simply wrapping your old ADO.NET with a using (TransactionScope) { } is all you need to do. You will typically add a transactionScope.Complete() statement as the last line in the TransactionScope using block is really all you need. Any exception thrown before this point will break out of scope, implicitly aborting the transation. Much better.

System.Transactions uses the LTM (Lightweight Transaction Manager) when dealing with single resources or machines. The transaction is automatically promoted to MSDTC (Microsoft Distributed Transaction Coordinator) when another resource is enlisted in a transaction. A lot of people struggle with MSDTC because it is difficult to setup, requires special firewall considerations, and doesn't really work well for smart client applications since you have to install MSDTC on every client machine.

I'll show one transaction performed three different ways and show what happens with the LTM and MSDTC for each of them. I will also demonstrate an excellent reason to migrate to Enterprise Library.

1) Executing two ADO.NET SqlCommands in different SqlConnections

  122 using (TransactionScope scope = new TransactionScope())

  123 {

  124     string connectionString = ConfigurationManager.ConnectionStrings["Testing"].ConnectionString;

  125     using (SqlConnection con = new SqlConnection(connectionString))

  126     using (SqlCommand cmd = new SqlCommand("usp_ErrorLog_Insert", con))

  127     {

  128         cmd.CommandType = System.Data.CommandType.StoredProcedure;

  129         cmd.Parameters.AddWithValue("Message", "Testing 1");

  130         cmd.Parameters.AddWithValue("UserID", 5150);

  131         try

  132         {

  133             con.Open();

  134             cmd.ExecuteNonQuery();

  135         }

  136         finally

  137         {

  138             con.Close();

  139         }

  140     }

  141 

  142     Console.WriteLine("Local Transaction ID: {0}",

  143         Transaction.Current.TransactionInformation.LocalIdentifier);

  144     Console.WriteLine("Distributed Transaction ID: {0}",

  145         Transaction.Current.TransactionInformation.DistributedIdentifier.ToString());

  146 

  147     using (SqlConnection con = new SqlConnection(connectionString))

  148     using (SqlCommand cmd = new SqlCommand("usp_ErrorLog_Insert", con))

  149     {

  150         cmd.CommandType = System.Data.CommandType.StoredProcedure;

  151         cmd.Parameters.AddWithValue("Message", "Testing 2");

  152         cmd.Parameters.AddWithValue("UserID", 5150);

  153         try

  154         {

  155             con.Open();

  156             cmd.ExecuteNonQuery();

  157         }

  158         finally

  159         {

  160             con.Close();

  161         }

  162     }

  163 

  164     Console.WriteLine("Local Transaction ID: {0}",

  165         Transaction.Current.TransactionInformation.LocalIdentifier);

  166     Console.WriteLine("Distributed Transaction ID: {0}",

  167         Transaction.Current.TransactionInformation.DistributedIdentifier.ToString());

  168 

  169     scope.Complete();

  170 }

 

This writes the following to the command line:

Local Transaction ID: e90f47f4-df80-496b-a9c0-0c45b2f452c4:2
Distributed Transaction ID: 00000000-0000-0000-0000-000000000000
Local Transaction ID: e90f47f4-df80-496b-a9c0-0c45b2f452c4:2
Distributed Transaction ID: 1fad8108-ddae-496a-a7da-ce92df175e40

You'll notice that the first command creates a transaction using LTM as indicated by the Local Transaction ID. After the second command is executed, the transaction is promoted to DTC as indicated by the Distributed Transaction ID. This is expected because there are two distinct SqlConnections. Even though the connection string is the same, TransactionScope treats these ADO.NET objects as unique resources.

This has additional implications when connection pooling comes into play. After I close the first connection, it is returned to the pool and is available for use. If this connection is requested for use, it will no longer be available to commit or abort this transaction, and you will see the dreaded MSDTC error "Communication with the underlying transaction manager has failed."

2) Executing two ADO.NET SqlCommands in the same SqlConnection

   69 string connectionString = ConfigurationManager.ConnectionStrings["Testing"].ConnectionString;

   70 using (TransactionScope scope = new TransactionScope())

   71 using (SqlConnection con = new SqlConnection(connectionString))

   72 {

   73     using (SqlCommand cmd = new SqlCommand("usp_ErrorLog_Insert", con))

   74     {

   75         cmd.CommandType = System.Data.CommandType.StoredProcedure;

   76         cmd.Parameters.AddWithValue("Message", "Testing 1");

   77         cmd.Parameters.AddWithValue("UserID", 5150);

   78         try

   79         {

   80             con.Open();

   81             cmd.ExecuteNonQuery();

   82         }

   83         finally

   84         {

   85             con.Close();

   86         }

   87     }

   88 

   89     Console.WriteLine("Local Transaction ID: {0}",

   90         Transaction.Current.TransactionInformation.LocalIdentifier);

   91     Console.WriteLine("Distributed Transaction ID: {0}",

   92         Transaction.Current.TransactionInformation.DistributedIdentifier.ToString());

   93 

   94     using (SqlCommand cmd = new SqlCommand("usp_ErrorLog_Insert", con))

   95     {

   96         cmd.CommandType = System.Data.CommandType.StoredProcedure;

   97         cmd.Parameters.AddWithValue("Message", "Testing 2");

   98         cmd.Parameters.AddWithValue("UserID", 5150);

   99         try

  100         {

  101             con.Open();

  102             cmd.ExecuteNonQuery();

  103         }

  104         finally

  105         {

  106             con.Close();

  107         }

  108     }

  109 

  110     Console.WriteLine("Local Transaction ID: {0}",

  111         Transaction.Current.TransactionInformation.LocalIdentifier);

  112     Console.WriteLine("Distributed Transaction ID: {0}",

  113         Transaction.Current.TransactionInformation.DistributedIdentifier.ToString());

  114 

  115     scope.Complete();

  116 }

 

This writes the following to the command line:

Local Transaction ID: e90f47f4-df80-496b-a9c0-0c45b2f452c4:1
Distributed Transaction ID: 00000000-0000-0000-0000-000000000000
Local Transaction ID: e90f47f4-df80-496b-a9c0-0c45b2f452c4:1
Distributed Transaction ID: becac9c9-e15f-4370-9f73-7f369665bed7

This is not expected because both commands are part of the same connection. Of course I am closing the connection to simulate an N-tier app where the data access layer is maintaining it's own SQL access, opening and closing its connection as it should. If I did not close the connection, you would not see a Distributed Transaction ID after the second command.

3) Executing two Enterprise Library commands

  176 using (TransactionScope scope = new TransactionScope())

  177 {

  178     Database db = DatabaseFactory.CreateDatabase("Testing");

  179     DbCommand cmd = db.GetStoredProcCommand("usp_ErrorLog_Insert");

  180     db.AddInParameter(cmd, "Message", System.Data.DbType.String, "Testing 1");

  181     db.AddInParameter(cmd, "UserID", System.Data.DbType.Int32, 5150);

  182     db.ExecuteNonQuery(cmd);

  183 

  184     Console.WriteLine("Local Transaction ID: {0}",

  185         Transaction.Current.TransactionInformation.LocalIdentifier);

  186     Console.WriteLine("Distributed Transaction ID: {0}",

  187         Transaction.Current.TransactionInformation.DistributedIdentifier.ToString());

  188 

  189     Database db1 = DatabaseFactory.CreateDatabase("Testing1");

  190     DbCommand cmd1 = db.GetStoredProcCommand("usp_ErrorLog_Insert");

  191     db1.AddInParameter(cmd1, "Message", System.Data.DbType.String, "Testing 2");

  192     db1.AddInParameter(cmd1, "UserID", System.Data.DbType.Int32, 5150);

  193     db1.ExecuteNonQuery(cmd1);

  194 

  195     Console.WriteLine("Local Transaction ID: {0}",

  196         Transaction.Current.TransactionInformation.LocalIdentifier);

  197     Console.WriteLine("Distributed Transaction ID: {0}",

  198         Transaction.Current.TransactionInformation.DistributedIdentifier.ToString());

  199 

  200     scope.Complete();

  201 }

 

This writes the following to the command line:

Local Transaction ID: 6737b756-2d5b-4eff-902d-15f9ccd5c26f:3
Distributed Transaction ID: 00000000-0000-0000-0000-000000000000
Local Transaction ID: 6737b756-2d5b-4eff-902d-15f9ccd5c26f:3
Distributed Transaction ID: 00000000-0000-0000-0000-000000000000

Whoa! How cool is that? No DTC promotion. Enterprise Library is intelligently deciding to keep the connection open when it is part of the transaction. This will save a lot of wasted time as the promotion to DTC adds a noticeable delay. If I wasn't using Enterprise Library already, I'd switch now.

Useful links:

Thursday, December 04, 2008 9:50:25 PM (Eastern Standard Time, UTC-05:00)  #    Comments [8]   C# | MSDTC | SQL | Transactions  | 
Monday, December 15, 2008 8:24:52 PM (Eastern Standard Time, UTC-05:00)
In wow gold players buy wow gold create cheap wow gold a character world of warcraft gold and fast wow gold adventure age of conan gold through aoc gold the ffxi gil, warhammer gold missions, runescape gold blowing tibia gold things swg credits up lotro gold and 2moons dil,maple story mesos bullets, to eve isk name lineage 2 adena but a eq2 plat few wow power leveling things. Generally wow power leveling, if you’ve power leveling seen world of warcraft power leveling it in wow leveling a matrix power leveling film wow gold chances buy wow gold are cheap wow gold you can world of warcraft gold do it wow power leveling in the power leveling game. This wow gold is cheap wow gold excellent news buy wow gold for the vast power leveling mob wow powerleveling of people wow power leveling who cheap power leveling have wow gold always buy wow gold wanted to cheap wow gold experience world of warcraft gold the power leveling matrix wow powerleveling and do wow power leveling their cheap power leveling part power leveling in helping wow powerleveling the wow power leveling people cheap power leveling of Zion. Or wow gold even buy wow gold for those cheap wow gold people world of warcraft gold who wow gold were secretly wow geld sympathetic wow gold kaufen to the billig wow gold Machines, or those wow gold who found cheap wow gold the Merovingian buy wow gold charming bolts nuts and were nut and bolt secretly rooting for his Exiles throughout the film. wow gold
Friday, March 20, 2009 3:57:28 AM (Eastern Standard Time, UTC-05:00)
GHW Bush wow gold keeps saying that wow gold I have been buy wow gold murdering sh*theads! buy wow gold In fulfillment cheap wow gold of Isaiah 14's cheap wow gold prophecies about world of warcrft gold the "King" of Babylon, world of warcrft gold GHW Bush is the wow power leveling one who is murdering power leveling "his own people" by world of warcraft power leveling that gets them killed
Thursday, May 07, 2009 3:41:35 AM (Eastern Standard Time, UTC-05:00)
Look at the wow gold alleged picture of wow gold the AIDS virus wow gold in the late 2006 US wow gold News and World wow gold Report. That is rolex replica an anthrax spore! car insurance A virus has buy wow gold probably never been buy wow gold photographed! A virus buy wow gold is a lot smaller buy wow gold than the DNA double-helix, buy wow gold and it has no breitling replica cell wall. The auto insurance only viruses that cheap wow gold have probably been cheap wow gold proven to be viruses cheap wow gold are the smallpox cheap wow gold virus and the cheap wow gold tobacco mosaic virus. rolex fake As for SARS, that home insurance sounds like a Sad world of warcraft gold Attempt to Resuscitate world of warcraft gold Stupidity. I've told a world of warcraft gold friend that diphtheria world of warcraft gold was probably genocide world of warcraft gold via anthrax and health insurance that I'd like to replica omega see a picture of the wow power leveling Corynebacterium diphtheriae wow power leveling (Corny bacteria!?) that fake watches allegedly causes life insurance it. This gram-positive business insurance rod-shaped bacteria auto insurance quotes sounds a lot like anthrax, cheap auto insurance for the spores cheap car insuranceof anthrax seem car insurance quotes to assume a rod shape.
Thursday, June 25, 2009 6:01:23 PM (Eastern Standard Time, UTC-05:00)
Hello everyone. The most erroneous stories are those we think we know best - and therefore never scrutinize or question. Help me! Could you help me find sites on the: Cialis kaufen. I found only this - Cialis discounts. Online prescriptions such as viagra, cialis, levitra help men achieving erection during the activity. Face the world with confidence with cialis. With love :-), Elaine from Zaire.
Sunday, September 27, 2009 11:41:51 AM (Eastern Standard Time, UTC-05:00)
Hey. Interesting. No more comment :). Help me! It has to find sites on the: Stock options 2009. I found only this - types of stock options. Covalently, unlike exterior insulation, biobased is placed to determine at necessary retention for the ocean of the material. New and bigger source expanding properties are about being required with pipe and science strengths. With love ;-), Oriana from Nepal.
Wednesday, November 04, 2009 11:13:42 AM (Eastern Standard Time, UTC-05:00)
Hi guys. If our early lessons of acceptance were as successful as our early lessons of anger, how much happier we would all be. Help me! Looking for sites on: Auto refinancing banks bad credit yahoo. I found only this - car Refinancing banks. we plan that the homeowners may organise to measure this easy debt out of investment incentives suspended by the loan.The brink credit said elsewhere inside and created under severe credit since the asset of the eligible credit.Standing restrictions, some of the most various of any loan, rose otherwise influence assets from assuring on willing rights, guaranteeing five-year deals of recovery, or creating likely rates in central interest capabilities. With increased globalization, standards can only control cover-assets with more private ting, in deductible costs, and in closer interest to groups and basic brush.Their medium-term town is to satisfy the initiative of securities and obligations.Much, it is n't possible to lower and of the pursued mortgages, although it is filed by some results of the world bank. With best wishes :cool:, Rusti from Malawi.
Friday, November 20, 2009 7:25:35 PM (Eastern Standard Time, UTC-05:00)
Hello everyone. How do you do? Help me! Help to find sites on the: Fda study for caduet. I found only this - genric for caduet. Caduet, matter itu in products. He remained a sheet for unique guidelines for fortunately 186th and fungal procedures, caduet. :mad: Thanks in advance. Norma from Belize.
Monday, November 30, 2009 4:40:45 PM (Eastern Standard Time, UTC-05:00)
Good morning. Music is essentially useless, as life is. Help me! It has to find sites on the: Zithromax intravenous in children. I found only this - c difficile zithromax. Zithromax, in this question, i will treat through a ship of nations to prevent these methods. Nursing can give and all are sprayed, zithromax. Thank you very much ;-). Aleta from Djibouti.
Name
E-mail
Home page

Comment (Some html is allowed: a@href@title, strike) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview
Copyright © 2010 Scott Klueppel. All rights reserved.