Problem with TransactionScope and SqlDataReader with SQL Server 2005?

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())
    {
        using (SqlConnection con = new SqlConnection(DataManager.DSN))
        {
           
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())
                    {
                        Sql
Method2();
                    }
                    
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

 



Answer this question

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

    It's generally okay to use MARS, but there can be some tricky issues with it when it comes to the transaction semantics between batches in some scenarios. Turning MARS on for a scenario like this one (the MARS connection is purely reading) is pretty safe.

  • Yoav Sion

    Hi alazela,

    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

    I am having a similar problem except it is even weirder because I was running fine on SQL Server 2005 CTP; however, when I upgraded to the retail version of SQL Server 2005, the same exact code is now causing these "Failure while attempting to promote transaction." exceptions. Eric, if you have made any headway on this, can you please post your resolution

    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

    Is there anybody who can talk about how to achieve the db transaction using sqldatasource and object datasource
    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


  • Problem with TransactionScope and SqlDataReader with SQL Server 2005?