Skip to content

TransactionScope in 2.1 throwing exception only during debugging in VS2017 15.7.0 Preview 6.0 #21

Description

@BrotherOdin

While testing the .net core 2.1 implementation of TransactionScope I ran into the following issue.

We have a library that configures settings for DB Connections and Transaction scope (Data.Core). Other libraries call this library to execute queries using dapper.

When I execute the code without the debugger, everything runs fine. When I set a breakpoint in the unit test AND also in the method where the DB connection is opened. I then get the exception below.

Any ideas?

System.PlatformNotSupportedException
  HResult=0x80131539
  Message=This platform does not support distributed transactions.
  Source=System.Transactions.Local
  StackTrace:
   at System.Transactions.Distributed.DistributedTransactionManager.GetDistributedTransactionFromTransmitterPropagationToken(Byte[] propagationToken)
   at System.Transactions.TransactionInterop.GetDistributedTransactionFromTransmitterPropagationToken(Byte[] propagationToken)
   at System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx)
   at System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx)
   at System.Transactions.EnlistableStates.Promote(InternalTransaction tx)
   at System.Transactions.Transaction.Promote()
   at System.Transactions.TransactionInterop.ConvertToDistributedTransaction(Transaction transaction)
   at System.Transactions.TransactionInterop.GetExportCookie(Transaction transaction, Byte[] whereabouts)
   at System.Data.SqlClient.SqlInternalConnection.GetTransactionCookie(Transaction transaction, Byte[] whereAbouts)
   at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
   at System.Data.ProviderBase.DbConnectionPool.PrepareConnection(DbConnection owningObject, DbConnectionInternal obj, Transaction transaction)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   at System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at Data.Core.DataStore.get_Connection() in D:\TestApps\Repro\Debugger.Issue\Data.Core\DataStore.cs:line 37
   at Data.Core.Tests.ReproTest1.TestMethod1() in D:\TestApps\Repro\Debugger.Issue\Data.Core.Tests\ReproTest1.cs:line 14

This is the test method in Data.Core.Tests:

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Data.Core.Tests
{
    [TestClass]
    public class ReproTest1
    {
        [TestMethod]
        public void TestMethod1()
        {
            DataStore ds = new DataStore();
            using (var scope = ds.BeginTransactionScope())
            {
                // Set breakpoint on the following line. 1 of 2 breakpoints.
                var conn = ds.Connection;

                // The following will never be executed in debug

                // Run raw ADO.NET command in the transaction
                var command = conn.CreateCommand();
                command.CommandText = "DELETE FROM dbo.SomeTable";
                command.ExecuteNonQuery();
            }

        }
    }
}

This is DataStore class:

using System;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Transactions;

namespace Data.Core
{
    using IsolationLevel = System.Transactions.IsolationLevel;

    public class DataStore
    {

        public DataStore()
        {
            this.DatabaseConnectionString = "Data Source=.;Initial Catalog=SomeDB;Integrated Security=True";
        }

        /// <summary>
        /// Gets or sets the database connection string used to create the database connection.
        /// This cannot be null.
        /// </summary>
        private string DatabaseConnectionString { get; set; }

        /// <summary>
        /// Gets the connection. 
        /// </summary>
        public SqlConnection Connection
        {
            get
            {
                // Set breakpoint on the following line. 2 of 2 breakpoints.
                var connection =  new SqlConnection(this.DatabaseConnectionString);
                
                if (connection.State != ConnectionState.Open)
                {
                    // This line will throw exception
                    connection.Open();
                }

                // See if we need to join an existing transaction scope
                if (Transaction.Current != null)
                {
                    ((DbConnection) connection).EnlistTransaction(Transaction.Current);
                }

                return connection;
            }
        }

        /// <inheritdoc />
        public TransactionScope BeginTransactionScope()
        {
            return new TransactionScope(
                TransactionScopeOption.Required, 
                new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted });
        }
    }
}

I have attached a project to reproduce the error.
Debugger.Issue.zip

[EDIT] Add C# syntax highlighting by @karelz

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions