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 cffb127ec7..9e1df81f9c 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft.Data.SqlClient.csproj @@ -256,6 +256,9 @@ Microsoft\Data\SqlClient\Server\SqlRecordBuffer.cs + + Microsoft\Data\SqlClient\SqlTransaction.Common.cs + Microsoft\Data\SqlClient\Server\SqlUserDefinedAggregateAttribute.cs 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 index 2eecfdc8cc..1c3df017dd 100644 --- a/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netcore/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -4,136 +4,22 @@ using System; using System.ComponentModel; -using System.Data; using System.Data.Common; -using System.Diagnostics; using Microsoft.Data.Common; namespace Microsoft.Data.SqlClient { /// - public sealed class SqlTransaction : DbTransaction + public sealed partial class SqlTransaction : DbTransaction { - private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); - private static int _objectTypeCount; // EventSource Counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; - - private SqlInternalTransaction _internalTransaction; - private SqlConnection _connection; - - private bool _isFromAPI; - - internal SqlTransaction(SqlInternalConnection internalConnection, SqlConnection con, - IsolationLevel iso, SqlInternalTransaction internalTransaction) - { - _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 - //////////////////////////////////////////////////////////////////////////////////////// - - /// - new public SqlConnection Connection - { - get - { - if (IsZombied) - { - return null; - } - else - { - return _connection; - } - } - } - - /// - override protected DbConnection DbConnection - { - get - { - return Connection; - } - } - - internal SqlInternalTransaction InternalTransaction - { - get - { - return _internalTransaction; - } - } - - /// - override public IsolationLevel IsolationLevel - { - get - { - ZombieCheck(); - return _isolationLevel; - } - } - - private bool Is2005PartialZombie - { - get - { - return (null != _internalTransaction && _internalTransaction.IsCompleted); - } - } - - internal bool IsZombied - { - get - { - return (null == _internalTransaction || _internalTransaction.IsCompleted); - } - } - - internal int ObjectID - { - get - { - return _objectID; - } - } - - internal SqlStatistics Statistics - { - get - { - if (null != _connection) - { - if (_connection.StatisticsEnabled) - { - return _connection.Statistics; - } - } - return null; - } - } + private static readonly SqlDiagnosticListener s_diagnosticListener = new(SqlClientDiagnosticListenerExtensions.DiagnosticListenerName); //////////////////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS //////////////////////////////////////////////////////////////////////////////////////// /// - override public void Commit() + public override void Commit() { using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionCommitScope(_isolationLevel, _connection, InternalTransaction)) { @@ -191,7 +77,7 @@ protected override void Dispose(bool disposing) } /// - override public void Rollback() + public override void Rollback() { using (DiagnosticTransactionScope diagnosticScope = s_diagnosticListener.CreateTransactionRollbackScope(_isolationLevel, _connection, InternalTransaction, null)) { @@ -284,45 +170,5 @@ public void Save(string savePointName) } } } - - //////////////////////////////////////////////////////////////////////////////////////// - // INTERNAL METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - internal void Zombie() - { - // For 2005, 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 (null != internalConnection && !_isFromAPI) - { - SqlClientEventSource.Log.TryAdvancedTraceEvent("SqlTransaction.Zombie | ADV | Object Id {0} 2005 deferred zombie", ObjectID); - } - else - { - _internalTransaction = null; // pre-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; // 2005 zombification - } - - throw ADP.TransactionZombied(this); - } - } } } 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 ab43046cb3..b659a27953 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft.Data.SqlClient.csproj @@ -320,6 +320,9 @@ 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/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs index 6b10dc40ef..d5a93b1b86 100644 --- a/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs +++ b/src/Microsoft.Data.SqlClient/netfx/src/Microsoft/Data/SqlClient/SqlTransaction.cs @@ -3,139 +3,21 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; -using System.Data; using System.Data.Common; -using System.Diagnostics; using System.Runtime.CompilerServices; using Microsoft.Data.Common; - namespace Microsoft.Data.SqlClient { /// - public sealed class SqlTransaction : DbTransaction + public sealed partial class SqlTransaction : DbTransaction { - private static int _objectTypeCount; // EventSource Counter - internal readonly int _objectID = System.Threading.Interlocked.Increment(ref _objectTypeCount); - internal readonly IsolationLevel _isolationLevel = IsolationLevel.ReadCommitted; - - private SqlInternalTransaction _internalTransaction; - private SqlConnection _connection; - - private bool _isFromAPI; - - internal SqlTransaction(SqlInternalConnection internalConnection, SqlConnection con, - IsolationLevel iso, SqlInternalTransaction internalTransaction) - { - SqlConnection.VerifyExecutePermission(); - - _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 - //////////////////////////////////////////////////////////////////////////////////////// - - /// - new public SqlConnection Connection - { // MDAC 66655 - get - { - if (IsZombied) - { - return null; - } - else - { - return _connection; - } - } - } - - /// - override protected DbConnection DbConnection - { - get - { - return Connection; - } - } - - internal SqlInternalTransaction InternalTransaction - { - get - { - return _internalTransaction; - } - } - - /// - override public IsolationLevel IsolationLevel - { - get - { - ZombieCheck(); - return _isolationLevel; - } - } - - private bool Is2005PartialZombie - { - get - { - return (null != _internalTransaction && _internalTransaction.IsCompleted); - } - } - - internal bool IsZombied - { - get - { - return (null == _internalTransaction || _internalTransaction.IsCompleted); - } - } - - internal int ObjectID - { - get - { - return _objectID; - } - } - - internal SqlStatistics Statistics - { - get - { - if (null != _connection) - { - if (_connection.StatisticsEnabled) - { - return _connection.Statistics; - } - } - return null; - } - } - //////////////////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS //////////////////////////////////////////////////////////////////////////////////////// /// - override public void Commit() + public override void Commit() { SqlConnection.ExecutePermission.Demand(); // MDAC 81476 @@ -151,14 +33,14 @@ override public void Commit() try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else - { + { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); @@ -195,8 +77,7 @@ override public void Commit() // 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. - var innerException = e.InnerException as Win32Exception; - if (innerException != null && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) + if (e.InnerException is Win32Exception innerException && innerException.NativeErrorCode == TdsEnums.SNI_WAIT_TIMEOUT) { _connection.Abort(e); } @@ -221,7 +102,7 @@ protected override void Dispose(bool disposing) try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new(); RuntimeHelpers.PrepareConstrainedRegions(); try @@ -264,7 +145,7 @@ protected override void Dispose(bool disposing) } /// - override public void Rollback() + public override void Rollback() { if (Is2005PartialZombie) { @@ -287,14 +168,14 @@ override public void Rollback() try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else - { + { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); @@ -351,14 +232,14 @@ public void Rollback(string transactionName) try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else - { + { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); @@ -415,14 +296,14 @@ public void Save(string savePointName) try { #if DEBUG - TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection(); + TdsParser.ReliabilitySection tdsReliabilitySection = new(); RuntimeHelpers.PrepareConstrainedRegions(); try { tdsReliabilitySection.Start(); #else - { + { #endif //DEBUG bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_connection); statistics = SqlStatistics.StartTimer(Statistics); @@ -458,49 +339,5 @@ public void Save(string savePointName) } } } - - //////////////////////////////////////////////////////////////////////////////////////// - // INTERNAL METHODS - //////////////////////////////////////////////////////////////////////////////////////// - - internal void Zombie() - { - // SQLBUDT #402544 For 2005, 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 aready closed, - // then we're free to zombify... - SqlInternalConnection internalConnection = (_connection.InnerConnection as SqlInternalConnection); - - if (null != internalConnection && internalConnection.Is2005OrNewer && !_isFromAPI) - { - SqlClientEventSource.Log.TryAdvancedTraceEvent(" {0} 2005 deferred zombie", ObjectID); - } - else - { - _internalTransaction = null; // pre-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; // 2005 zombification - } - - throw ADP.TransactionZombied(this); - } - } } } - 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 new file mode 100644 index 0000000000..19996684b9 --- /dev/null +++ b/src/Microsoft.Data.SqlClient/src/Microsoft/Data/SqlClient/SqlTransaction.Common.cs @@ -0,0 +1,145 @@ +// 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 + { + 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 (null != _connection) + { + 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 (null != internalConnection +#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 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); + } + } + } +}