I developed some C#.NET 2.0 code that performed perfectly with SQL Server 2000 but is now bombing out with SQL Server 2005. I've been able to duplicate this behavior in every single construct that looks like this:
public void SqlMethod1()
{
using (TransactionScope ts = new TransactionScope())
{
{
con.Open();
using (SqlCommand cmd = new SqlCommand())
{
cmd.Connection = con;
cmd.CommandText = <any SQL string>;
using (SqlDataReader rdr = cmd.ExecuteReader
(CommandBehavior.CloseConnection))
{
while (rdr.Read())
{
SqlMethod2();
}
rdr.Close();
}
}
}
ts.Complete();
}
}
private void SqlMethod2()
{
using (SqlConnection con = new SqlConnection(DataManager.DSN))
{
con.Open()
...
This always ran with SQL Server 2000 as the database engine. With Sql Server 2005, if I comment out the transaction, the code runs perfectly. With the transaction in place, the code runs as far as con.Open(), and then I get the following exception:
[SqlException (0x80131904): There is already an open DataReader associated with this Command which must be closed first.]
System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, String transactionName, IsolationLevel iso, SqlInternalTransaction internalTransaction) +406
System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String name, IsolationLevel iso, SqlInternalTransaction internalTransaction) +163
System.Data.SqlClient.SqlDelegatedTransaction.Promote() +92
[TransactionPromotionException: Failure while attempting to promote transaction.]
System.Data.SqlClient.SqlDelegatedTransaction.Promote() +127
System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx) +55
System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx) +201
[TransactionAbortedException: The transaction has aborted.]
System.Transactions.TransactionStateAborted.CheckForFinishedTransaction(InternalTransaction tx) +11
System.Transactions.EnlistableStates.Promote(InternalTransaction tx) +22
System.Transactions.Transaction.Promote() +53
System.Transactions.TransactionInterop.ConvertToOletxTransaction(Transaction transaction) +28
System.Transactions.TransactionInterop.GetExportCookie(Transaction transaction, Byte[] whereabouts) +202
System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx) +414
System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx) +735868
System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction) +730199
System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction) +30
System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject) +1209
System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) +82
System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) +105
System.Data.SqlClient.SqlConnection.Open() +111
Truston.Managers.QuestionManager.LoadQuestionSetPoolItem(String controlID, String caseID, Int32 setID) in C:\Documents and Settings\Eric.MEWTWO\My Documents\Visual Studio 2005\Projects\Truston2005\Managers\QuestionManager.cs:159
Truston.Managers.QuestionManager.ProcessLogic(String controlID, String caseID, Int32 questionID, String response, Boolean reverse) in C:\Documents and Settings\Eric.MEWTWO\My Documents\Visual Studio 2005\Projects\Truston2005\Managers\QuestionManager.cs:609
Truston.Managers.QuestionManager.ProcessLogic(String controlID, String caseID, Int32 questionID, String response) in C:\Documents and Settings\Eric.MEWTWO\My Documents\Visual Studio 2005\Projects\Truston2005\Managers\QuestionManager.cs:636
Truston.Managers.QuestionManager.ProcessResponse(String controlID, String caseID, Int32 questionID, String response) in C:\Documents and Settings\Eric.MEWTWO\My Documents\Visual Studio 2005\Projects\Truston2005\Managers\QuestionManager.cs:534
test.Page_Load(Object sender, EventArgs e) in c:\Inetpub\wwwroot\TrustonWeb\test.aspx.cs:160
System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp, Object o, Object t, EventArgs e) +15
System.Web.Util.CalliEventHandlerDelegateProxy.Callback(Object sender, EventArgs e) +34
System.Web.UI.Control.OnLoad(EventArgs e) +99
System.Web.UI.Control.LoadRecursive() +47
System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1061
Does anyone have information on this
Thanks.
Eric Valinsky, Principal Consultant
Plainwrap Solutions

Problem with TransactionScope and SqlDataReader with SQL Server 2005?
SlinkingFerret
"Tricky issues" may have been bit strong of wording on my part :-). There were certainly plenty of tricky issues on our side of things for development of this feature, but MOST cases for you should be pretty straight-forward. Here's an article that discusses the feature in depth (including issues and behaviors that may surprise you):
http://msdn.microsoft.com/library/default.asp url=/library/en-us/dnsql90/html/MARSinSQL05.asp
Note that there is a slight error in the article -- it says that MARS is turned on by default, but this was changed late in the development cycle to be OFF by default.
SHASHIVRAT
Yoav Sion
Thanks for the detailed reply. After I posted my message, I found some other discussion on MARS and putting it back in everything works. I am just a little unclear on whether I am masking some transient potential for a problem or whether it is generally okay to use MARS
Thank you,
Kevin
TomSt
Thank you,
Kevin
sanjeewa
Do you have any articles/pointers on "tricky issues with it when it comes to the transaction semantics between batches in some scenarios" so that I make sure I don't fall into that category
Thanks!
Kevin
cmb082000
This is due to some default optimizations for System.Transactions against a SQL Server 2005 instance. When the connection enlists during the Open call, since there are no other DB transactions associated with the System transaction, the connection becomes the root and tries to handle the work as a local transaction (in System.Transactions parlance, this is a Lightweight Transaction). When the second connection enlists, the first transaction must be promoted to DTC, but the connection is busy with the reader, so fails.
Going against the SQL2000 instance Lightweight Transactions aren't supported, so the enlist causes the first connection to immediately start a new DTC transaction. There's no additional work to be done on that connection when the second connection enlists, so everything works fine.
To work around the issue, either ensure the initial transaction is promoted to a DTC transaction up front, or make sure the initial connection can talk to the server when the transaction does get promoted. Probably the simplest way to accomplish this is to turn on MARS for the initial connection ("Multiple Active Results = true" in the connection string). That will allow the promote to flow back to the server while the reader is open.
We are taking a look at this behavior to see if we can fix the backward compat issue.
vo_yage
I tried and researched enough but no luck.
If there is really no support for transaction by these objects then they are really useless. And not only that we lost out best friend dataset/sqldatadapter/component designer for such stupid objects....
I wish them to back in VS2005 upgrade....
thanks