From cf100eb0433fa72c68d9095861947bce01fd7c3c Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Tue, 11 Mar 2025 16:11:33 -0500 Subject: [PATCH 01/13] Create merge file --- .../Data/SqlClient/SqlTransaction.cs | 332 ++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs new file mode 100644 index 0000000000..d72aec00b7 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -0,0 +1,332 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.ComponentModel; +using System.Data.Common; +using System.Runtime.CompilerServices; +using Microsoft.Data.Common; + +namespace Microsoft.Data.SqlClient +{ + /// + public sealed partial class SqlTransaction : DbTransaction + { + //netcore private static readonly SqlDiagnosticListener s_diagnosticListener = new(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); + + //////////////////////////////////////////////////////////////////////////////////////// + // PUBLIC METHODS + //////////////////////////////////////////////////////////////////////////////////////// + + /// + public override void Commit() + { + //netcore using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionCommitScope(_isolationLevel, _connection, InternalTransaction)) + //netcore { + ZombieCheck(); + + //netcore using (TryEventScope.Create("SqlTransaction.Commit | API | Object Id {0}", ObjectID)) + //netfx using (TryEventScope.Create(" {0}", ObjectID)) + { + SqlStatistics statistics = null; + TdsParser bestEffortCleanupTarget = null; + + //netcore SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlTransaction.Commit | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId); + //netfx SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + + #if NETFRAMEWORK + RuntimeHelpers.PrepareConstrainedRegions(); + #endif + try + { + bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); + statistics = SqlStatistics.StartTimer(Statistics); + + _isFromAPI = true; + + _internalTransaction.Commit(); + } + catch (System.OutOfMemoryException e) + { + //netcore diagnosticScope.SetException(e); + _connection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + //netcore diagnosticScope.SetException(e); + _connection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + //netcore diagnosticScope.SetException(e); + _connection.Abort(e); + + #if NETFRAMEWORK + SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); + #endif + throw; + } + catch (SqlException ex) + { + diagnosticScope.SetException(ex); + // GitHub Issue #130 - When a timeout exception has occurred on transaction completion request, + // this connection may not be in reusable state. + // We will abort this connection and make sure it does not go back to the pool. + if (ex.InnerException is Win32Exception innerException && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) + { + _connection.Abort(ex); + } + throw; + } + //netcore--- + catch (Exception ex) + { + diagnosticScope.SetException(ex); + throw; + } + //---netcore + finally + { + SqlStatistics.StopTimer(statistics); + _isFromAPI = false; + } + } + //netcore } + } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + TdsParser bestEffortCleanupTarget = null; + + #if NETFRAMEWORK + RuntimeHelpers.PrepareConstrainedRegions(); + #endif + try + { + bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); + if (!IsZombied && !Is2005PartialZombie) + { + _internalTransaction.Dispose(); + } + } + catch (System.OutOfMemoryException e) + { + _connection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + _connection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + _connection.Abort(e); + + #if NETFRAMEWORK + SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); + #endif + throw; + } + } + base.Dispose(disposing); + } + + /// + public override void Rollback() + { + //netcore using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope(_isolationLevel, _connection, InternalTransaction, null)) + //netcore { + if (Is2005PartialZombie) + { + // Put something in the trace in case a customer has an issue + //netcore SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Rollback | ADV | Object Id {0}, partial zombie no rollback required", ObjectID); + //netfx SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} partial zombie no rollback required", ObjectID); + _internalTransaction = null; // 2005 zombification + } + else + { + ZombieCheck(); + + SqlStatistics statistics = null; + //netcore using (TryEventScope.Create("SqlTransaction.Rollback | API | Object Id {0}", ObjectID)) + //netfx using (TryEventScope.Create(" {0}", ObjectID)) + { + //netcore SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlTransaction.Rollback | API | Correlation | Object Id {0}, ActivityID {1}, Client Connection Id {2}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId); + //netfx SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + + TdsParser bestEffortCleanupTarget = null; + + #if NETFRAMEWORK + RuntimeHelpers.PrepareConstrainedRegions(); + #endif + try + { + bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); + statistics = SqlStatistics.StartTimer(Statistics); + + _isFromAPI = true; + + _internalTransaction.Rollback(); + } + catch (System.OutOfMemoryException e) + { + //netcore diagnosticScope.SetException(e); + _connection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + //netcore diagnosticScope.SetException(e); + _connection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + //netcore diagnosticScope.SetException(e); + _connection.Abort(e); + + #if NETFRAMEWORK + SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); + #endif + throw; + } + //netcore--- + catch (Exception ex) + { + diagnosticScope.SetException(ex); + throw; + } + //---netcore + finally + { + SqlStatistics.StopTimer(statistics); + _isFromAPI = false; + } + } + } + //netcore } + } + + /// + public override void Rollback(string transactionName) + { + //netcore using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope(_isolationLevel, _connection, InternalTransaction, transactionName)) + //netcore { + + //netfx SqlConnection.ExecutePermission.Demand(); // MDAC 81476 + + ZombieCheck(); + + //netcore using (TryEventScope.Create(SqlClientEventSource.Log.TryScopeEnterEvent("SqlTransaction.Rollback | API | Object Id {0}, Transaction Name='{1}', ActivityID {2}, Client Connection Id {3}", ObjectID, transactionName, ActivityCorrelator.Current, Connection?.ClientConnectionId))) + //netfx using (TryEventScope.Create(" {0} transactionName='{1}'", ObjectID, transactionName)) + { + SqlStatistics statistics = null; + TdsParser bestEffortCleanupTarget = null; + + #if NETFRAMEWORK + RuntimeHelpers.PrepareConstrainedRegions(); + #endif + try + { + bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); + statistics = SqlStatistics.StartTimer(Statistics); + + _isFromAPI = true; + + _internalTransaction.Rollback(transactionName); + } + catch (System.OutOfMemoryException e) + { + //netcore diagnosticScope.SetException(e); + _connection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + //netcore diagnosticScope.SetException(e); + _connection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + //netcore diagnosticScope.SetException(e); + _connection.Abort(e); + + #if NETFRAMEWORK + SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); + #endif + throw; + } + //netcore--- + catch (Exception ex) + { + diagnosticScope.SetException(ex); + throw; + } + //---netcore + finally + { + SqlStatistics.StopTimer(statistics); + _isFromAPI = false; + } + } + //netcore } + } + + /// + public override void Save(string savePointName) + { + //netfx SqlConnection.ExecutePermission.Demand(); // MDAC 81476 + + ZombieCheck(); + + SqlStatistics statistics = null; + //netcore using (TryEventScope.Create("SqlTransaction.Save | API | Object Id {0} | Save Point Name '{1}'", ObjectID, savePointName)) + //netfx using (" {0} savePointName='{1}'", ObjectID, savePointName)) + { + TdsParser bestEffortCleanupTarget = null; + + #if NETFRAMEWORK + RuntimeHelpers.PrepareConstrainedRegions(); + #endif + try + { + bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); + statistics = SqlStatistics.StartTimer(Statistics); + + _internalTransaction.Save(savePointName); + } + catch (System.OutOfMemoryException e) + { + _connection.Abort(e); + throw; + } + catch (System.StackOverflowException e) + { + _connection.Abort(e); + throw; + } + catch (System.Threading.ThreadAbortException e) + { + _connection.Abort(e); + + #if NETFRAMEWORK + SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); + #endif + throw; + } + finally + { + SqlStatistics.StopTimer(statistics); + } + } + } + } +} From d04c7de670aa0c2b79f8061d6b21370523483b5c Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 13 Mar 2025 13:14:49 -0500 Subject: [PATCH 02/13] Merging Commit method --- .../Data/SqlClient/SqlConnection.stub.cs | 2 + .../Data/SqlClient/SqlTransaction.cs | 142 ++++++++++-------- 2 files changed, 81 insertions(+), 63 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.stub.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.stub.cs index d16088849e..fa453e7053 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.stub.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.stub.cs @@ -13,5 +13,7 @@ public class SqlConnection internal Guid ClientConnectionId { get; set; } internal SqlStatistics Statistics { get; set; } + + internal void Abort(Exception e) { } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index d72aec00b7..38331e2e08 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -8,12 +8,20 @@ using System.Runtime.CompilerServices; using Microsoft.Data.Common; +#if NET +using Microsoft.Data.SqlClient.Diagnostics; +#else +using System.Threading; +#endif + namespace Microsoft.Data.SqlClient { /// public sealed partial class SqlTransaction : DbTransaction { - //netcore private static readonly SqlDiagnosticListener s_diagnosticListener = new(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); + #if NET + private static readonly SqlDiagnosticListener s_diagnosticListener = new(SqlDiagnosticListener.DiagnosticListenerName); + #endif //////////////////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS @@ -22,79 +30,87 @@ public sealed partial class SqlTransaction : DbTransaction /// public override void Commit() { - //netcore using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionCommitScope(_isolationLevel, _connection, InternalTransaction)) - //netcore { - ZombieCheck(); + #if NET + using DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionCommitScope( + _isolationLevel, + _connection, + InternalTransaction); + #endif - //netcore using (TryEventScope.Create("SqlTransaction.Commit | API | Object Id {0}", ObjectID)) - //netfx using (TryEventScope.Create(" {0}", ObjectID)) - { - SqlStatistics statistics = null; - TdsParser bestEffortCleanupTarget = null; + ZombieCheck(); - //netcore SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlTransaction.Commit | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId); - //netfx SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); + using (TryEventScope.Create("SqlTransaction.Commit | API | Object Id {0}", ObjectID)) + { + SqlStatistics statistics = null; + + SqlClientEventSource.Log.TryCorrelationTraceEvent( + "SqlTransaction.Commit | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}", + ObjectID, + ActivityCorrelator.Current, + Connection?.ClientConnectionId); + #if NETFRAMEWORK + TdsParser bestEffortCleanupTarget = null; + RuntimeHelpers.PrepareConstrainedRegions(); + #endif + try + { #if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); + bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); #endif - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - statistics = SqlStatistics.StartTimer(Statistics); - _isFromAPI = true; + statistics = SqlStatistics.StartTimer(Statistics); - _internalTransaction.Commit(); - } - catch (System.OutOfMemoryException e) - { - //netcore diagnosticScope.SetException(e); - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - //netcore diagnosticScope.SetException(e); - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - //netcore diagnosticScope.SetException(e); - _connection.Abort(e); + _isFromAPI = true; - #if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - #endif - throw; - } - catch (SqlException ex) - { - diagnosticScope.SetException(ex); - // GitHub Issue #130 - When a timeout exception has occurred on transaction completion request, - // this connection may not be in reusable state. - // We will abort this connection and make sure it does not go back to the pool. - if (ex.InnerException is Win32Exception innerException && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) - { - _connection.Abort(ex); - } - throw; - } - //netcore--- - catch (Exception ex) - { - diagnosticScope.SetException(ex); - throw; - } - //---netcore - finally + _internalTransaction.Commit(); + } + #if NETFRAMEWORK + catch (OutOfMemoryException e) + { + _connection.Abort(e); + throw; + } + catch (StackOverflowException e) + { + _connection.Abort(e); + throw; + } + catch (ThreadAbortException e) + { + _connection.Abort(e); + SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); + throw; + } + #endif + catch (SqlException ex) + { + #if NET + diagnosticScope.SetException(ex); + #endif + + // GitHub Issue #130 - When a timeout exception has occurred on transaction completion request, + // this connection may not be in reusable state. + // We will abort this connection and make sure it does not go back to the pool. + if (ex.InnerException is Win32Exception innerException && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) { - SqlStatistics.StopTimer(statistics); - _isFromAPI = false; + _connection.Abort(ex); } + throw; } - //netcore } + #if NET + catch (Exception ex) + { + diagnosticScope.SetException(ex); + throw; + } + #endif + finally + { + SqlStatistics.StopTimer(statistics); + _isFromAPI = false; + } + } } /// From 14ef8c13b980256ce7c3b13558cac0ca6027cdf5 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 13 Mar 2025 13:32:49 -0500 Subject: [PATCH 03/13] Merge Dispose method --- .../Data/SqlClient/SqlTransaction.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 38331e2e08..c8b0c2e855 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -6,12 +6,13 @@ using System.ComponentModel; using System.Data.Common; using System.Runtime.CompilerServices; +using System.Threading; using Microsoft.Data.Common; #if NET using Microsoft.Data.SqlClient.Diagnostics; #else -using System.Threading; + #endif namespace Microsoft.Data.SqlClient @@ -118,39 +119,43 @@ protected override void Dispose(bool disposing) { if (disposing) { - TdsParser bestEffortCleanupTarget = null; - #if NETFRAMEWORK + TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); #endif try { + #if NETFRAMEWORK bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); + #endif + if (!IsZombied && !Is2005PartialZombie) { _internalTransaction.Dispose(); } } - catch (System.OutOfMemoryException e) + catch (OutOfMemoryException e) { _connection.Abort(e); throw; } - catch (System.StackOverflowException e) + catch (StackOverflowException e) { _connection.Abort(e); throw; } - catch (System.Threading.ThreadAbortException e) + catch (ThreadAbortException e) { _connection.Abort(e); - + #if NETFRAMEWORK SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); #endif + throw; } } + base.Dispose(disposing); } From 6527dc73fdad89313a684ae75550693bef10b1bf Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 13 Mar 2025 13:37:30 -0500 Subject: [PATCH 04/13] Merge Rollback method w/o transaction name --- .../Data/SqlClient/SqlTransaction.cs | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index c8b0c2e855..fecad24f66 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -162,13 +162,20 @@ protected override void Dispose(bool disposing) /// public override void Rollback() { - //netcore using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope(_isolationLevel, _connection, InternalTransaction, null)) - //netcore { + #if NET + using DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope( + _isolationLevel, + _connection, + InternalTransaction, + transactionName: null); + if (Is2005PartialZombie) { // Put something in the trace in case a customer has an issue - //netcore SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Rollback | ADV | Object Id {0}, partial zombie no rollback required", ObjectID); - //netfx SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} partial zombie no rollback required", ObjectID); + SqlClientEventSource.Log.TryAdvancedTraceEvent( + "SqlTransaction.Rollback | ADV | Object Id {0}, partial zombie no rollback required", + ObjectID); + _internalTransaction = null; // 2005 zombification } else @@ -176,55 +183,53 @@ public override void Rollback() ZombieCheck(); SqlStatistics statistics = null; - //netcore using (TryEventScope.Create("SqlTransaction.Rollback | API | Object Id {0}", ObjectID)) - //netfx using (TryEventScope.Create(" {0}", ObjectID)) + using (TryEventScope.Create("SqlTransaction.Rollback | API | Object Id {0}", ObjectID)) { - //netcore SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlTransaction.Rollback | API | Correlation | Object Id {0}, ActivityID {1}, Client Connection Id {2}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId); - //netfx SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - - TdsParser bestEffortCleanupTarget = null; + SqlClientEventSource.Log.TryCorrelationTraceEvent( + "SqlTransaction.Rollback | API | Correlation | Object Id {0}, ActivityID {1}, Client Connection Id {2}", + ObjectID, + ActivityCorrelator.Current, + Connection?.ClientConnectionId); #if NETFRAMEWORK + TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); #endif try { + #if NETFRAMEWORK bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); + #endif + statistics = SqlStatistics.StartTimer(Statistics); _isFromAPI = true; - _internalTransaction.Rollback(); } + #if NETFRAMEWORK catch (System.OutOfMemoryException e) { - //netcore diagnosticScope.SetException(e); _connection.Abort(e); throw; } catch (System.StackOverflowException e) { - //netcore diagnosticScope.SetException(e); _connection.Abort(e); throw; } catch (System.Threading.ThreadAbortException e) { - //netcore diagnosticScope.SetException(e); _connection.Abort(e); - - #if NETFRAMEWORK SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - #endif throw; } - //netcore--- + #else catch (Exception ex) { diagnosticScope.SetException(ex); throw; } - //---netcore + #endif finally { SqlStatistics.StopTimer(statistics); @@ -232,7 +237,6 @@ public override void Rollback() } } } - //netcore } } /// From 904b6dd149dbd5b521528e3a433b216a0c014883 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 13 Mar 2025 13:50:45 -0500 Subject: [PATCH 05/13] Merge Rollback method w/transaction name --- .../Data/SqlClient/SqlConnection.stub.cs | 4 ++ .../Data/SqlClient/SqlTransaction.cs | 44 +++++++++++-------- 2 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.stub.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.stub.cs index fa453e7053..791201951d 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.stub.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlConnection.stub.cs @@ -12,6 +12,10 @@ public class SqlConnection { internal Guid ClientConnectionId { get; set; } + #if NETFRAMEWORK + internal static System.Security.CodeAccessPermission ExecutePermission { get; set; } + #endif + internal SqlStatistics Statistics { get; set; } internal void Abort(Exception e) { } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index fecad24f66..b4667def19 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -168,6 +168,7 @@ public override void Rollback() _connection, InternalTransaction, transactionName: null); + #endif if (Is2005PartialZombie) { @@ -242,20 +243,32 @@ public override void Rollback() /// public override void Rollback(string transactionName) { - //netcore using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope(_isolationLevel, _connection, InternalTransaction, transactionName)) - //netcore { + #if NET + using DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope( + _isolationLevel, + _connection, + InternalTransaction, + transactionName); + #endif - //netfx SqlConnection.ExecutePermission.Demand(); // MDAC 81476 + #if NETFRAMEWORK + SqlConnection.ExecutePermission.Demand(); // MDAC 81476 + #endif ZombieCheck(); - //netcore using (TryEventScope.Create(SqlClientEventSource.Log.TryScopeEnterEvent("SqlTransaction.Rollback | API | Object Id {0}, Transaction Name='{1}', ActivityID {2}, Client Connection Id {3}", ObjectID, transactionName, ActivityCorrelator.Current, Connection?.ClientConnectionId))) - //netfx using (TryEventScope.Create(" {0} transactionName='{1}'", ObjectID, transactionName)) + var eventScopeEnter = TryEventScope.Create(SqlClientEventSource.Log.TryScopeEnterEvent( + "SqlTransaction.Rollback | API | Object Id {0}, Transaction Name='{1}', ActivityID {2}, Client Connection Id {3}", + ObjectID, + transactionName, + ActivityCorrelator.Current, + Connection?.ClientConnectionId)); + using (eventScopeEnter) { SqlStatistics statistics = null; - TdsParser bestEffortCleanupTarget = null; #if NETFRAMEWORK + TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); #endif try @@ -264,45 +277,38 @@ public override void Rollback(string transactionName) statistics = SqlStatistics.StartTimer(Statistics); _isFromAPI = true; - _internalTransaction.Rollback(transactionName); } - catch (System.OutOfMemoryException e) + #if NETFRAMEWORK + catch (OutOfMemoryException e) { - //netcore diagnosticScope.SetException(e); _connection.Abort(e); throw; } - catch (System.StackOverflowException e) + catch (StackOverflowException e) { - //netcore diagnosticScope.SetException(e); _connection.Abort(e); throw; } - catch (System.Threading.ThreadAbortException e) + catch (ThreadAbortException e) { - //netcore diagnosticScope.SetException(e); _connection.Abort(e); - - #if NETFRAMEWORK SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - #endif throw; } - //netcore--- + #else catch (Exception ex) { diagnosticScope.SetException(ex); throw; } - //---netcore + #endif finally { SqlStatistics.StopTimer(statistics); _isFromAPI = false; } } - //netcore } } /// From de99c30a955391324a12460641e5c8c8e9b4369f Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 13 Mar 2025 13:55:36 -0500 Subject: [PATCH 06/13] Merge Save method --- .../Data/SqlClient/SqlTransaction.cs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index b4667def19..2256f7afdc 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -314,43 +314,47 @@ public override void Rollback(string transactionName) /// public override void Save(string savePointName) { - //netfx SqlConnection.ExecutePermission.Demand(); // MDAC 81476 + #if NETFRAMEWORK + SqlConnection.ExecutePermission.Demand(); // MDAC 81476 + #endif ZombieCheck(); SqlStatistics statistics = null; - //netcore using (TryEventScope.Create("SqlTransaction.Save | API | Object Id {0} | Save Point Name '{1}'", ObjectID, savePointName)) - //netfx using (" {0} savePointName='{1}'", ObjectID, savePointName)) + using (TryEventScope.Create("SqlTransaction.Save | API | Object Id {0} | Save Point Name '{1}'", ObjectID, savePointName)) { - TdsParser bestEffortCleanupTarget = null; - #if NETFRAMEWORK + TdsParser bestEffortCleanupTarget = null; RuntimeHelpers.PrepareConstrainedRegions(); #endif try { + #if NETFRAMEWORK bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); + #endif + statistics = SqlStatistics.StartTimer(Statistics); _internalTransaction.Save(savePointName); } - catch (System.OutOfMemoryException e) + catch (OutOfMemoryException e) { _connection.Abort(e); throw; } - catch (System.StackOverflowException e) + catch (StackOverflowException e) { _connection.Abort(e); throw; } - catch (System.Threading.ThreadAbortException e) + catch (ThreadAbortException e) { _connection.Abort(e); #if NETFRAMEWORK SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); #endif + throw; } finally From e9eff97fe154b8dc83253ed2629757be4fe56671 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 13 Mar 2025 13:57:21 -0500 Subject: [PATCH 07/13] Final touchups of merge * Tweak usings * Fix doc hrefs --- .../Data/SqlClient/SqlTransaction.cs | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 2256f7afdc..fde44cb0ff 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -5,30 +5,25 @@ using System; using System.ComponentModel; using System.Data.Common; -using System.Runtime.CompilerServices; using System.Threading; using Microsoft.Data.Common; #if NET using Microsoft.Data.SqlClient.Diagnostics; #else - +using System.Runtime.CompilerServices; #endif namespace Microsoft.Data.SqlClient { - /// + /// public sealed partial class SqlTransaction : DbTransaction { #if NET private static readonly SqlDiagnosticListener s_diagnosticListener = new(SqlDiagnosticListener.DiagnosticListenerName); #endif - //////////////////////////////////////////////////////////////////////////////////////// - // PUBLIC METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - /// + /// public override void Commit() { #if NET @@ -114,7 +109,7 @@ public override void Commit() } } - /// + /// protected override void Dispose(bool disposing) { if (disposing) @@ -159,7 +154,7 @@ protected override void Dispose(bool disposing) base.Dispose(disposing); } - /// + /// public override void Rollback() { #if NET @@ -208,17 +203,17 @@ public override void Rollback() _internalTransaction.Rollback(); } #if NETFRAMEWORK - catch (System.OutOfMemoryException e) + catch (OutOfMemoryException e) { _connection.Abort(e); throw; } - catch (System.StackOverflowException e) + catch (StackOverflowException e) { _connection.Abort(e); throw; } - catch (System.Threading.ThreadAbortException e) + catch (ThreadAbortException e) { _connection.Abort(e); SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); @@ -240,8 +235,12 @@ public override void Rollback() } } - /// - public override void Rollback(string transactionName) + /// + #if NET + public override void Rollback(string transactionName) + #else + public void Rollback(string transactionName) + #endif { #if NET using DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope( @@ -311,8 +310,12 @@ public override void Rollback(string transactionName) } } - /// + /// + #if NET public override void Save(string savePointName) + #else + public void Save(string savePointName) + #endif { #if NETFRAMEWORK SqlConnection.ExecutePermission.Demand(); // MDAC 81476 From a973214ec94ddab00f2ceccbed8c09316d4b7c67 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 13 Mar 2025 17:58:12 -0500 Subject: [PATCH 08/13] Deleting old SqlTransaction files, adding references to the common project instance --- .../src/Microsoft.Data.SqlClient.csproj | 4 +- .../Data/SqlClient/SqlTransaction.cs | 304 ------------------ .../netfx/src/Microsoft.Data.SqlClient.csproj | 4 +- .../Data/SqlClient/SqlTransaction.cs | 257 --------------- .../Data/SqlClient/SqlTransaction.cs | 3 + 5 files changed, 9 insertions(+), 563 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs delete mode 100644 src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index efc9d50ac1..f9be98b1f8 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -620,6 +620,9 @@ Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs + + Microsoft\Data\SqlClient\SqlTransaction.cs + Microsoft\Data\SqlClient\SqlUdtInfo.cs @@ -724,7 +727,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs deleted file mode 100644 index 89e9919f77..0000000000 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ /dev/null @@ -1,304 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System; -using System.ComponentModel; -using System.Data.Common; -using Microsoft.Data.Common; -using Microsoft.Data.SqlClient.Diagnostics; - -namespace Microsoft.Data.SqlClient -{ - /// - public sealed partial class SqlTransaction : DbTransaction - { - private static readonly SqlDiagnosticListener s_diagnosticListener = new(SqlDiagnosticListener.DiagnosticListenerName); - - //////////////////////////////////////////////////////////////////////////////////////// - // PUBLIC METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - /// - public override void Commit() - { - using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionCommitScope(_isolationLevel, _connection, InternalTransaction)) - { - ZombieCheck(); - - using (TryEventScope.Create("SqlTransaction.Commit | API | Object Id {0}", ObjectID)) - { - SqlStatistics statistics = null; - TdsParser bestEffortCleanupTarget = null; - - SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlTransaction.Commit | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId); -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - statistics = SqlStatistics.StartTimer(Statistics); - - _isFromAPI = true; - - _internalTransaction.Commit(); - } - catch (System.OutOfMemoryException e) - { - diagnosticScope.SetException(e); - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - diagnosticScope.SetException(e); - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - diagnosticScope.SetException(e); - _connection.Abort(e); -#if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } - catch (SqlException ex) - { - diagnosticScope.SetException(ex); - // GitHub Issue #130 - When a timeout exception has occurred on transaction completion request, - // this connection may not be in reusable state. - // We will abort this connection and make sure it does not go back to the pool. - if (ex.InnerException is Win32Exception innerException && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) - { - _connection.Abort(ex); - } - throw; - } - catch (Exception ex) - { - diagnosticScope.SetException(ex); - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - _isFromAPI = false; - } - } - } - } - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - TdsParser bestEffortCleanupTarget = null; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - if (!IsZombied && !Is2005PartialZombie) - { - _internalTransaction.Dispose(); - } - } - catch (System.OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _connection.Abort(e); -#if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } - } - base.Dispose(disposing); - } - - /// - public override void Rollback() - { - using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope(_isolationLevel, _connection, InternalTransaction, null)) - { - if (Is2005PartialZombie) - { - // Put something in the trace in case a customer has an issue - SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Rollback | ADV | Object Id {0}, partial zombie no rollback required", ObjectID); - _internalTransaction = null; // 2005 zombification - } - else - { - ZombieCheck(); - - SqlStatistics statistics = null; - using (TryEventScope.Create("SqlTransaction.Rollback | API | Object Id {0}", ObjectID)) - { - SqlClientEventSource.Log.TryCorrelationTraceEvent("SqlTransaction.Rollback | API | Correlation | Object Id {0}, ActivityID {1}, Client Connection Id {2}", ObjectID, ActivityCorrelator.Current, Connection?.ClientConnectionId); - - TdsParser bestEffortCleanupTarget = null; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - statistics = SqlStatistics.StartTimer(Statistics); - - _isFromAPI = true; - - _internalTransaction.Rollback(); - } - catch (System.OutOfMemoryException e) - { - diagnosticScope.SetException(e); - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - diagnosticScope.SetException(e); - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - diagnosticScope.SetException(e); - _connection.Abort(e); -#if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } - catch (Exception ex) - { - diagnosticScope.SetException(ex); - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - _isFromAPI = false; - } - } - } - } - } - - /// - public override void Rollback(string transactionName) - { - using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope(_isolationLevel, _connection, InternalTransaction, transactionName)) - { - ZombieCheck(); - - using (TryEventScope.Create(SqlClientEventSource.Log.TryScopeEnterEvent("SqlTransaction.Rollback | API | Object Id {0}, Transaction Name='{1}', ActivityID {2}, Client Connection Id {3}", ObjectID, transactionName, ActivityCorrelator.Current, Connection?.ClientConnectionId))) - { - SqlStatistics statistics = null; - TdsParser bestEffortCleanupTarget = null; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - statistics = SqlStatistics.StartTimer(Statistics); - - _isFromAPI = true; - - _internalTransaction.Rollback(transactionName); - } - catch (System.OutOfMemoryException e) - { - diagnosticScope.SetException(e); - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - diagnosticScope.SetException(e); - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - diagnosticScope.SetException(e); - _connection.Abort(e); -#if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } - catch (Exception ex) - { - diagnosticScope.SetException(ex); - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - _isFromAPI = false; - } - } - } - } - - /// - public override void Save(string savePointName) - { - ZombieCheck(); - - SqlStatistics statistics = null; - using (TryEventScope.Create("SqlTransaction.Save | API | Object Id {0} | Save Point Name '{1}'", ObjectID, savePointName)) - { - TdsParser bestEffortCleanupTarget = null; -#if NETFRAMEWORK - RuntimeHelpers.PrepareConstrainedRegions(); -#endif - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - statistics = SqlStatistics.StartTimer(Statistics); - - _internalTransaction.Save(savePointName); - } - catch (System.OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _connection.Abort(e); -#if NETFRAMEWORK - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); -#endif - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - } - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index e1ddaa6926..72557b1bef 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -793,6 +793,9 @@ Microsoft\Data\SqlClient\SqlSymmetricKeyCache.cs + + Microsoft\Data\SqlClient\SqlTransaction.cs + Microsoft\Data\SqlClient\SqlUdtInfo.cs @@ -890,7 +893,6 @@ - diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs deleted file mode 100644 index b08f109e65..0000000000 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ /dev/null @@ -1,257 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.ComponentModel; -using System.Data.Common; -using System.Runtime.CompilerServices; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - /// - public sealed partial class SqlTransaction : DbTransaction - { - //////////////////////////////////////////////////////////////////////////////////////// - // PUBLIC METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - /// - public override void Commit() - { - SqlConnection.ExecutePermission.Demand(); // MDAC 81476 - - ZombieCheck(); - - using (TryEventScope.Create(" {0}", ObjectID)) - { - SqlStatistics statistics = null; - TdsParser bestEffortCleanupTarget = null; - - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - statistics = SqlStatistics.StartTimer(Statistics); - - _isFromAPI = true; - - _internalTransaction.Commit(); - } - catch (System.OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _connection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } - catch (SqlException e) - { - // GitHub Issue #130 - When a timeout exception has occurred on transaction completion request, - // this connection may not be in reusable state. - // We will abort this connection and make sure it does not go back to the pool. - if (e.InnerException is Win32Exception innerException && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) - { - _connection.Abort(e); - } - throw; - } - finally - { - _isFromAPI = false; - - SqlStatistics.StopTimer(statistics); - } - } - } - - /// - protected override void Dispose(bool disposing) - { - if (disposing) - { - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - if (!IsZombied && !Is2005PartialZombie) - { - _internalTransaction.Dispose(); - } - } - catch (System.OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _connection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } - } - base.Dispose(disposing); - } - - /// - public override void Rollback() - { - if (Is2005PartialZombie) - { - // Put something in the trace in case a customer has an issue - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} partial zombie no rollback required", ObjectID); - - _internalTransaction = null; // 2005 zombification - } - else - { - ZombieCheck(); - - SqlStatistics statistics = null; - using (TryEventScope.Create(" {0}", ObjectID)) - { - SqlClientEventSource.Log.TryCorrelationTraceEvent(" ObjectID {0}, ActivityID {1}", ObjectID, ActivityCorrelator.Current); - - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - statistics = SqlStatistics.StartTimer(Statistics); - - _isFromAPI = true; - - _internalTransaction.Rollback(); - } - catch (System.OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _connection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } - finally - { - _isFromAPI = false; - - SqlStatistics.StopTimer(statistics); - } - } - } - } - - /// - public void Rollback(string transactionName) - { - SqlConnection.ExecutePermission.Demand(); // MDAC 81476 - - ZombieCheck(); - - using (TryEventScope.Create(" {0} transactionName='{1}'", ObjectID, transactionName)) - { - SqlStatistics statistics = null; - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - statistics = SqlStatistics.StartTimer(Statistics); - - _isFromAPI = true; - - _internalTransaction.Rollback(transactionName); - } - catch (System.OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _connection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } - finally - { - _isFromAPI = false; - - SqlStatistics.StopTimer(statistics); - } - } - } - - /// - public void Save(string savePointName) - { - SqlConnection.ExecutePermission.Demand(); // MDAC 81476 - - ZombieCheck(); - - SqlStatistics statistics = null; - using (TryEventScope.Create(" {0} savePointName='{1}'", ObjectID, savePointName)) - { - TdsParser bestEffortCleanupTarget = null; - RuntimeHelpers.PrepareConstrainedRegions(); - try - { - bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); - statistics = SqlStatistics.StartTimer(Statistics); - - _internalTransaction.Save(savePointName); - } - catch (System.OutOfMemoryException e) - { - _connection.Abort(e); - throw; - } - catch (System.StackOverflowException e) - { - _connection.Abort(e); - throw; - } - catch (System.Threading.ThreadAbortException e) - { - _connection.Abort(e); - SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget); - throw; - } - finally - { - SqlStatistics.StopTimer(statistics); - } - } - } - } -} diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index fde44cb0ff..726fa58183 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -272,7 +272,10 @@ public void Rollback(string transactionName) #endif try { + #if NETFRAMEWORK bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); + #endif + statistics = SqlStatistics.StartTimer(Statistics); _isFromAPI = true; From d5a004d9390963ff58b1171096b1e6ed57fe3b66 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 13 Mar 2025 18:18:12 -0500 Subject: [PATCH 09/13] Move code from SqlTransaction.Common into the class --- .../Data/SqlClient/SqlTransaction.Common.cs | 114 +--------------- .../Data/SqlClient/SqlTransaction.cs | 128 +++++++++++++++++- 2 files changed, 128 insertions(+), 114 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs index e099712fc2..4362a1b99e 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs @@ -12,130 +12,18 @@ namespace Microsoft.Data.SqlClient /// public sealed partial class SqlTransaction : DbTransaction { - private static int s_objectTypeCount; // EventSource Counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref s_objectTypeCount); - internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; - private SqlInternalTransaction _internalTransaction; - private readonly SqlConnection _connection; - - private bool _isFromAPI; - - internal SqlTransaction(SqlInternalConnection internalConnection, SqlConnection con, - IsolationLevel iso, SqlInternalTransaction internalTransaction) - { -#if NETFRAMEWORK - SqlConnection.VerifyExecutePermission(); -#endif - _isolationLevel = iso; - _connection = con; - - if (internalTransaction == null) - { - _internalTransaction = new SqlInternalTransaction(internalConnection, TransactionType.LocalFromAPI, this); - } - else - { - Debug.Assert(internalConnection.CurrentTransaction == internalTransaction, "Unexpected Parser.CurrentTransaction state!"); - _internalTransaction = internalTransaction; - _internalTransaction.InitParent(this); - } - } - - //////////////////////////////////////////////////////////////////////////////////////// - // PROPERTIES - //////////////////////////////////////////////////////////////////////////////////////// - - /// - public new SqlConnection Connection - {// MDAC 66655 - get - { - if (IsZombied) - { - return null; - } - else - { - return _connection; - } - } - } - - /// - protected override DbConnection DbConnection => Connection; - - internal SqlInternalTransaction InternalTransaction => _internalTransaction; - - /// - public override IsolationLevel IsolationLevel - { - get - { - ZombieCheck(); - return _isolationLevel; - } - } - - private bool Is2005PartialZombie => _internalTransaction !=null && _internalTransaction.IsCompleted; - - internal bool IsZombied => _internalTransaction == null || _internalTransaction.IsCompleted; - - internal int ObjectID => _objectID; - - internal SqlStatistics Statistics - { - get - { - if (_connection != null) - { - if (_connection.StatisticsEnabled) - { - return _connection.Statistics; - } - } - return null; - } - } //////////////////////////////////////////////////////////////////////////////////////// // INTERNAL METHODS //////////////////////////////////////////////////////////////////////////////////////// - internal void Zombie() - { - // For Yukon, we have to defer "zombification" until - // we get past the users' next rollback, else we'll - // throw an exception there that is a breaking change. - // Of course, if the connection is already closed, - // then we're free to zombify... - SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); - if (internalConnection != null && !_isFromAPI) - { - SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Zombie | ADV | Object Id {0} yukon deferred zombie", ObjectID); - } - else - { - _internalTransaction = null; // pre SQL 2005 zombification - } - } + //////////////////////////////////////////////////////////////////////////////////////// // PRIVATE METHODS //////////////////////////////////////////////////////////////////////////////////////// - private void ZombieCheck() - { - // If this transaction has been completed, throw exception since it is unusable. - if (IsZombied) - { - if (Is2005PartialZombie) - { - _internalTransaction = null; // SQL 2005 zombification - } - throw ADP.TransactionZombied(this); - } - } } } diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 726fa58183..fb39e692a1 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel; using System.Data.Common; +using System.Diagnostics; using System.Threading; using Microsoft.Data.Common; @@ -17,12 +18,101 @@ namespace Microsoft.Data.SqlClient { /// - public sealed partial class SqlTransaction : DbTransaction + public sealed class SqlTransaction : DbTransaction { + private static int s_objectTypeCount; // EventSource Counter + internal readonly int _objectID = System.Threading.Interlocked.Increment(ref s_objectTypeCount); + internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; + + private SqlInternalTransaction _internalTransaction; + private readonly SqlConnection _connection; + + private bool _isFromAPI; + #if NET private static readonly SqlDiagnosticListener s_diagnosticListener = new(SqlDiagnosticListener.DiagnosticListenerName); #endif + internal SqlTransaction( + SqlInternalConnection internalConnection, + SqlConnection con, + IsolationLevel iso, + SqlInternalTransaction internalTransaction) + { + #if NETFRAMEWORK + SqlConnection.VerifyExecutePermission(); + #endif + _isolationLevel = iso; + _connection = con; + + if (internalTransaction == null) + { + _internalTransaction = new SqlInternalTransaction(internalConnection, TransactionType.LocalFromAPI, this); + } + else + { + Debug.Assert(internalConnection.CurrentTransaction == internalTransaction, "Unexpected Parser.CurrentTransaction state!"); + _internalTransaction = internalTransaction; + _internalTransaction.InitParent(this); + } + } + + #region Properties + + /// + public new SqlConnection Connection + {// MDAC 66655 + get + { + if (IsZombied) + { + return null; + } + else + { + return _connection; + } + } + } + + /// + public override IsolationLevel IsolationLevel + { + get + { + ZombieCheck(); + return _isolationLevel; + } + } + + internal SqlInternalTransaction InternalTransaction => _internalTransaction; + + internal bool IsZombied => _internalTransaction == null || _internalTransaction.IsCompleted; + + internal int ObjectID => _objectID; + + internal SqlStatistics Statistics + { + get + { + if (_connection != null) + { + if (_connection.StatisticsEnabled) + { + return _connection.Statistics; + } + } + return null; + } + } + + /// + protected override DbConnection DbConnection => Connection; + + private bool Is2005PartialZombie => _internalTransaction !=null && _internalTransaction.IsCompleted; + + #endregion + /// public override void Commit() { @@ -369,5 +459,41 @@ public void Save(string savePointName) } } } + + internal void Zombie() + { + // For Yukon, we have to defer "zombification" until + // we get past the users' next rollback, else we'll + // throw an exception there that is a breaking change. + // Of course, if the connection is already closed, + // then we're free to zombify... + SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); + if (internalConnection != null +#if NETFRAMEWORK + && internalConnection.Is2005OrNewer +#endif + && !_isFromAPI) + { + SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Zombie | ADV | Object Id {0} yukon deferred zombie", ObjectID); + } + else + { + _internalTransaction = null; // pre SQL 2005 zombification + } + } + + private void ZombieCheck() + { + // If this transaction has been completed, throw exception since it is unusable. + if (IsZombied) + { + if (Is2005PartialZombie) + { + _internalTransaction = null; // SQL 2005 zombification + } + + throw ADP.TransactionZombied(this); + } + } } } From 4ae3b4bc5d1e077f03e6daf41ec169eea9da2248 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 13 Mar 2025 18:38:14 -0500 Subject: [PATCH 10/13] Remove SqlTransaction.Common.cs --- .../src/Microsoft.Data.SqlClient.csproj | 3 -- .../netfx/src/Microsoft.Data.SqlClient.csproj | 3 -- .../Data/SqlClient/SqlTransaction.Common.cs | 29 ------------------- 3 files changed, 35 deletions(-) delete mode 100644 src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs diff --git a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj index f9be98b1f8..bb77ffba3e 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -629,9 +629,6 @@ Microsoft\Data\SqlClient\SqlUtil.cs - - Microsoft\Data\SqlClient\SqlTransaction.Common.cs - Microsoft\Data\SqlClient\SSPI\NegotiateSSPIContextProvider.cs diff --git a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj index 72557b1bef..05d4626119 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -550,9 +550,6 @@ Microsoft\Data\SqlClient\Server\SqlRecordBuffer.cs - - Microsoft\Data\SqlClient\SqlTransaction.Common.cs - Microsoft\Data\SqlClient\Server\ValueUtilsSmi.cs diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs deleted file mode 100644 index 4362a1b99e..0000000000 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Data; -using System.Data.Common; -using System.Diagnostics; -using Microsoft.Data.Common; - -namespace Microsoft.Data.SqlClient -{ - /// - public sealed partial class SqlTransaction : DbTransaction - { - - - //////////////////////////////////////////////////////////////////////////////////////// - // INTERNAL METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - - - //////////////////////////////////////////////////////////////////////////////////////// - // PRIVATE METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - - } -} From d97f12b7c8a8ed98f50f47db6db738665d7e3181 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 13 Mar 2025 18:38:38 -0500 Subject: [PATCH 11/13] Cleanup the transaction after adding back the common partial. --- .../Data/SqlClient/SqlInternalTransaction.cs | 2 +- .../Data/SqlClient/SqlTransaction.cs | 127 +++++++----------- 2 files changed, 51 insertions(+), 78 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs index 31884f4b96..ad89176fe6 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlInternalTransaction.cs @@ -52,7 +52,7 @@ internal SqlInternalTransaction(SqlInternalConnection innerConnection, Transacti internal SqlInternalTransaction(SqlInternalConnection innerConnection, TransactionType type, SqlTransaction outerTransaction, long transactionId) { - SqlClientEventSource.Log.TryPoolerTraceEvent("SqlInternalTransaction.ctor | RES | CPOOL | Object Id {0}, Created for connection {1}, outer transaction {2}, Type {3}", ObjectID, innerConnection.ObjectID, outerTransaction?.ObjectID, (int)type); + SqlClientEventSource.Log.TryPoolerTraceEvent("SqlInternalTransaction.ctor | RES | CPOOL | Object Id {0}, Created for connection {1}, outer transaction {2}, Type {3}", ObjectID, innerConnection.ObjectID, outerTransaction?.ObjectId, (int)type); _innerConnection = innerConnection; _transactionType = type; diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index fb39e692a1..9e76a8ae50 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -4,6 +4,7 @@ using System; using System.ComponentModel; +using System.Data; using System.Data.Common; using System.Diagnostics; using System.Threading; @@ -20,19 +21,16 @@ namespace Microsoft.Data.SqlClient /// public sealed class SqlTransaction : DbTransaction { - private static int s_objectTypeCount; // EventSource Counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref s_objectTypeCount); - internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; - - private SqlInternalTransaction _internalTransaction; - private readonly SqlConnection _connection; - - private bool _isFromAPI; - #if NET private static readonly SqlDiagnosticListener s_diagnosticListener = new(SqlDiagnosticListener.DiagnosticListenerName); #endif + private static int s_objectTypeCount; // EventSource Counter + + private readonly SqlConnection _connection; + private readonly IsolationLevel _isolationLevel; + private bool _isFromApi; + internal SqlTransaction( SqlInternalConnection internalConnection, SqlConnection con, @@ -47,33 +45,20 @@ internal SqlTransaction( if (internalTransaction == null) { - _internalTransaction = new SqlInternalTransaction(internalConnection, TransactionType.LocalFromAPI, this); + InternalTransaction = new SqlInternalTransaction(internalConnection, TransactionType.LocalFromAPI, this); } else { Debug.Assert(internalConnection.CurrentTransaction == internalTransaction, "Unexpected Parser.CurrentTransaction state!"); - _internalTransaction = internalTransaction; - _internalTransaction.InitParent(this); + InternalTransaction = internalTransaction; + InternalTransaction.InitParent(this); } } #region Properties /// - public new SqlConnection Connection - {// MDAC 66655 - get - { - if (IsZombied) - { - return null; - } - else - { - return _connection; - } - } - } + public new SqlConnection Connection => IsZombied ? null : _connection; /// public override IsolationLevel IsolationLevel @@ -85,31 +70,18 @@ public override IsolationLevel IsolationLevel } } - internal SqlInternalTransaction InternalTransaction => _internalTransaction; - - internal bool IsZombied => _internalTransaction == null || _internalTransaction.IsCompleted; + internal SqlInternalTransaction InternalTransaction { get; private set; } - internal int ObjectID => _objectID; + internal bool IsZombied => InternalTransaction == null || InternalTransaction.IsCompleted; - internal SqlStatistics Statistics - { - get - { - if (_connection != null) - { - if (_connection.StatisticsEnabled) - { - return _connection.Statistics; - } - } - return null; - } - } + internal int ObjectId { get; } = Interlocked.Increment(ref s_objectTypeCount); /// protected override DbConnection DbConnection => Connection; - private bool Is2005PartialZombie => _internalTransaction !=null && _internalTransaction.IsCompleted; + private bool Is2005PartialZombie => InternalTransaction?.IsCompleted == true; + + private SqlStatistics Statistics => _connection?.StatisticsEnabled == true ? _connection.Statistics : null; #endregion @@ -125,13 +97,13 @@ public override void Commit() ZombieCheck(); - using (TryEventScope.Create("SqlTransaction.Commit | API | Object Id {0}", ObjectID)) + using (TryEventScope.Create("SqlTransaction.Commit | API | Object Id {0}", ObjectId)) { SqlStatistics statistics = null; SqlClientEventSource.Log.TryCorrelationTraceEvent( "SqlTransaction.Commit | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}", - ObjectID, + ObjectId, ActivityCorrelator.Current, Connection?.ClientConnectionId); @@ -147,9 +119,9 @@ public override void Commit() statistics = SqlStatistics.StartTimer(Statistics); - _isFromAPI = true; + _isFromApi = true; - _internalTransaction.Commit(); + InternalTransaction.Commit(); } #if NETFRAMEWORK catch (OutOfMemoryException e) @@ -194,7 +166,7 @@ public override void Commit() finally { SqlStatistics.StopTimer(statistics); - _isFromAPI = false; + _isFromApi = false; } } } @@ -216,7 +188,7 @@ protected override void Dispose(bool disposing) if (!IsZombied && !Is2005PartialZombie) { - _internalTransaction.Dispose(); + InternalTransaction.Dispose(); } } catch (OutOfMemoryException e) @@ -260,20 +232,20 @@ public override void Rollback() // Put something in the trace in case a customer has an issue SqlClientEventSource.Log.TryAdvancedTraceEvent( "SqlTransaction.Rollback | ADV | Object Id {0}, partial zombie no rollback required", - ObjectID); + ObjectId); - _internalTransaction = null; // 2005 zombification + InternalTransaction = null; // 2005 zombification } else { ZombieCheck(); SqlStatistics statistics = null; - using (TryEventScope.Create("SqlTransaction.Rollback | API | Object Id {0}", ObjectID)) + using (TryEventScope.Create("SqlTransaction.Rollback | API | Object Id {0}", ObjectId)) { SqlClientEventSource.Log.TryCorrelationTraceEvent( "SqlTransaction.Rollback | API | Correlation | Object Id {0}, ActivityID {1}, Client Connection Id {2}", - ObjectID, + ObjectId, ActivityCorrelator.Current, Connection?.ClientConnectionId); @@ -289,8 +261,8 @@ public override void Rollback() statistics = SqlStatistics.StartTimer(Statistics); - _isFromAPI = true; - _internalTransaction.Rollback(); + _isFromApi = true; + InternalTransaction.Rollback(); } #if NETFRAMEWORK catch (OutOfMemoryException e) @@ -319,7 +291,7 @@ public override void Rollback() finally { SqlStatistics.StopTimer(statistics); - _isFromAPI = false; + _isFromApi = false; } } } @@ -348,7 +320,7 @@ public void Rollback(string transactionName) var eventScopeEnter = TryEventScope.Create(SqlClientEventSource.Log.TryScopeEnterEvent( "SqlTransaction.Rollback | API | Object Id {0}, Transaction Name='{1}', ActivityID {2}, Client Connection Id {3}", - ObjectID, + ObjectId, transactionName, ActivityCorrelator.Current, Connection?.ClientConnectionId)); @@ -368,8 +340,8 @@ public void Rollback(string transactionName) statistics = SqlStatistics.StartTimer(Statistics); - _isFromAPI = true; - _internalTransaction.Rollback(transactionName); + _isFromApi = true; + InternalTransaction.Rollback(transactionName); } #if NETFRAMEWORK catch (OutOfMemoryException e) @@ -398,7 +370,7 @@ public void Rollback(string transactionName) finally { SqlStatistics.StopTimer(statistics); - _isFromAPI = false; + _isFromApi = false; } } } @@ -417,7 +389,7 @@ public void Save(string savePointName) ZombieCheck(); SqlStatistics statistics = null; - using (TryEventScope.Create("SqlTransaction.Save | API | Object Id {0} | Save Point Name '{1}'", ObjectID, savePointName)) + using (TryEventScope.Create("SqlTransaction.Save | API | Object Id {0} | Save Point Name '{1}'", ObjectId, savePointName)) { #if NETFRAMEWORK TdsParser bestEffortCleanupTarget = null; @@ -431,7 +403,7 @@ public void Save(string savePointName) statistics = SqlStatistics.StartTimer(Statistics); - _internalTransaction.Save(savePointName); + InternalTransaction.Save(savePointName); } catch (OutOfMemoryException e) { @@ -462,23 +434,23 @@ public void Save(string savePointName) internal void Zombie() { - // For Yukon, we have to defer "zombification" until - // we get past the users' next rollback, else we'll - // throw an exception there that is a breaking change. - // Of course, if the connection is already closed, - // then we're free to zombify... - SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); - if (internalConnection != null -#if NETFRAMEWORK + // For Yukon, we have to defer "zombification" until we get past the users' next + // rollback, else we'll throw an exception there that is a breaking change. Of course, + // if the connection is already closed, then we're free to zombify... + if (_connection.InnerConnection is SqlInternalConnection internalConnection + #if NETFRAMEWORK && internalConnection.Is2005OrNewer -#endif - && !_isFromAPI) + #endif + && !_isFromApi) { - SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Zombie | ADV | Object Id {0} yukon deferred zombie", ObjectID); + SqlClientEventSource.Log.TryAdvancedTraceEvent( + "SqlTransaction.Zombie | ADV | Object Id {0} yukon deferred zombie", + ObjectId); } else { - _internalTransaction = null; // pre SQL 2005 zombification + // pre SQL 2005 zombification + InternalTransaction = null; } } @@ -489,7 +461,8 @@ private void ZombieCheck() { if (Is2005PartialZombie) { - _internalTransaction = null; // SQL 2005 zombification + // SQL 2005 zombification + InternalTransaction = null; } throw ADP.TransactionZombied(this); From 83c810dfd7b7a3ac72fa19d06f87eeab3e292ecb Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Wed, 19 Mar 2025 13:34:23 -0500 Subject: [PATCH 12/13] Remove some <2005 condition that no longer applies --- .../src/Microsoft/Data/SqlClient/SqlTransaction.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 9e76a8ae50..bbf6425147 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -437,11 +437,7 @@ internal void Zombie() // For Yukon, we have to defer "zombification" until we get past the users' next // rollback, else we'll throw an exception there that is a breaking change. Of course, // if the connection is already closed, then we're free to zombify... - if (_connection.InnerConnection is SqlInternalConnection internalConnection - #if NETFRAMEWORK - && internalConnection.Is2005OrNewer - #endif - && !_isFromApi) + if (_connection.InnerConnection is SqlInternalConnection internalConnection && !_isFromApi) { SqlClientEventSource.Log.TryAdvancedTraceEvent( "SqlTransaction.Zombie | ADV | Object Id {0} yukon deferred zombie", From 37fcfd5139f42aeff182e866d96c49bc1e0e8350 Mon Sep 17 00:00:00 2001 From: Ben Russell Date: Thu, 20 Mar 2025 15:39:40 -0500 Subject: [PATCH 13/13] Make Save match exception handling pattern the others are using --- .../src/Microsoft/Data/SqlClient/SqlTransaction.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs index bbf6425147..9f43e352f5 100644 --- a/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -405,6 +405,7 @@ public void Save(string savePointName) InternalTransaction.Save(savePointName); } + #if NETFRAMEWORK catch (OutOfMemoryException e) { _connection.Abort(e); @@ -425,6 +426,7 @@ public void Save(string savePointName) throw; } + #endif finally { SqlStatistics.StopTimer(statistics);