2121using System . Buffers ;
2222using Microsoft . Data . Common ;
2323using Microsoft . Data . Sql ;
24+ using Microsoft . Data . SqlClient . Diagnostics ;
2425using Microsoft . Data . SqlClient . Server ;
2526using System . Transactions ;
2627using System . Collections . Concurrent ;
@@ -162,6 +163,10 @@ protected override void AfterCleared(SqlCommand owner)
162163 /// </summary>
163164 private static bool _forceRetryableEnclaveQueryExecutionExceptionDuringGenerateEnclavePackage = false ;
164165#endif
166+
167+ private static readonly SqlDiagnosticListener s_diagnosticListener = new SqlDiagnosticListener ( ) ;
168+ private bool _parentOperationStarted = false ;
169+
165170 internal static readonly Action < object > s_cancelIgnoreFailure = CancelIgnoreFailureCallback ;
166171
167172 // Prepare
@@ -632,7 +637,8 @@ internal SqlStatistics Statistics
632637 {
633638 if ( _activeConnection != null )
634639 {
635- if ( _activeConnection . StatisticsEnabled )
640+ if ( _activeConnection . StatisticsEnabled ||
641+ s_diagnosticListener . IsEnabled ( SqlClientCommandAfter . Name ) )
636642 {
637643 return _activeConnection . Statistics ;
638644 }
@@ -1228,6 +1234,7 @@ public override object ExecuteScalar()
12281234 _pendingCancel = false ;
12291235 SqlStatistics statistics = null ;
12301236
1237+ using ( DiagnosticScope diagnosticScope = s_diagnosticListener . CreateCommandScope ( this , _transaction ) )
12311238 using ( TryEventScope . Create ( "SqlCommand.ExecuteScalar | API | ObjectId {0}" , ObjectID ) )
12321239 {
12331240 bool success = false ;
@@ -1245,9 +1252,13 @@ public override object ExecuteScalar()
12451252 success = true ;
12461253 return result ;
12471254 }
1248- catch ( SqlException ex )
1255+ catch ( Exception ex )
12491256 {
1250- sqlExceptionNumber = ex . Number ;
1257+ diagnosticScope . SetException ( ex ) ;
1258+ if ( ex is SqlException sqlException )
1259+ {
1260+ sqlExceptionNumber = sqlException . Number ;
1261+ }
12511262 throw ;
12521263 }
12531264 finally
@@ -1318,6 +1329,7 @@ public override int ExecuteNonQuery()
13181329
13191330 SqlStatistics statistics = null ;
13201331
1332+ using ( DiagnosticScope diagnosticScope = s_diagnosticListener . CreateCommandScope ( this , _transaction ) )
13211333 using ( TryEventScope . Create ( "<sc.SqlCommand.ExecuteNonQuery|API> {0}" , ObjectID ) )
13221334 {
13231335 bool success = false ;
@@ -1344,9 +1356,13 @@ public override int ExecuteNonQuery()
13441356 success = true ;
13451357 return _rowsAffected ;
13461358 }
1347- catch ( SqlException ex )
1359+ catch ( Exception ex )
13481360 {
1349- sqlExceptionNumber = ex . Number ;
1361+ diagnosticScope . SetException ( ex ) ;
1362+ if ( ex is SqlException sqlException )
1363+ {
1364+ sqlExceptionNumber = sqlException . Number ;
1365+ }
13501366 throw ;
13511367 }
13521368 finally
@@ -1916,6 +1932,7 @@ public XmlReader ExecuteXmlReader()
19161932
19171933 SqlStatistics statistics = null ;
19181934
1935+ using ( DiagnosticScope diagnosticScope = s_diagnosticListener . CreateCommandScope ( this , _transaction ) )
19191936 using ( TryEventScope . Create ( "SqlCommand.ExecuteXmlReader | API | Object Id {0}" , ObjectID ) )
19201937 {
19211938 bool success = false ;
@@ -1935,9 +1952,13 @@ public XmlReader ExecuteXmlReader()
19351952 success = true ;
19361953 return result ;
19371954 }
1938- catch ( SqlException ex )
1955+ catch ( Exception ex )
19391956 {
1940- sqlExceptionNumber = ex . Number ;
1957+ diagnosticScope . SetException ( ex ) ;
1958+ if ( ex is SqlException sqlException )
1959+ {
1960+ sqlExceptionNumber = sqlException . Number ;
1961+ }
19411962 throw ;
19421963 }
19431964 finally
@@ -2293,6 +2314,8 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
22932314 RuntimeHelpers . PrepareConstrainedRegions ( ) ;
22942315 bool success = false ;
22952316 int ? sqlExceptionNumber = null ;
2317+ Exception e = null ;
2318+ Guid operationId = s_diagnosticListener . WriteCommandBefore ( this , _transaction ) ;
22962319
22972320 using ( TryEventScope . Create ( "SqlCommand.ExecuteReader | API | Object Id {0}" , ObjectID ) )
22982321 {
@@ -2307,31 +2330,44 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
23072330 success = true ;
23082331 return result ;
23092332 }
2310- catch ( SqlException e )
2333+ catch ( System . OutOfMemoryException ex )
23112334 {
2312- sqlExceptionNumber = e . Number ;
2335+ _activeConnection . Abort ( ex ) ;
23132336 throw ;
23142337 }
2315- catch ( System . OutOfMemoryException e )
2338+ catch ( System . StackOverflowException ex )
23162339 {
2317- _activeConnection . Abort ( e ) ;
2340+ _activeConnection . Abort ( ex ) ;
23182341 throw ;
23192342 }
2320- catch ( System . StackOverflowException e )
2343+ catch ( System . Threading . ThreadAbortException ex )
23212344 {
2322- _activeConnection . Abort ( e ) ;
2345+ _activeConnection . Abort ( ex ) ;
2346+ SqlInternalConnection . BestEffortCleanup ( bestEffortCleanupTarget ) ;
23232347 throw ;
23242348 }
2325- catch ( System . Threading . ThreadAbortException e )
2349+ catch ( Exception ex )
23262350 {
2327- _activeConnection . Abort ( e ) ;
2328- SqlInternalConnection . BestEffortCleanup ( bestEffortCleanupTarget ) ;
2351+ if ( ex is SqlException sqlException )
2352+ {
2353+ sqlExceptionNumber = sqlException . Number ;
2354+ }
2355+
2356+ e = ex ;
23292357 throw ;
23302358 }
23312359 finally
23322360 {
23332361 SqlStatistics . StopTimer ( statistics ) ;
23342362 WriteEndExecuteEvent ( success , sqlExceptionNumber , synchronous : true ) ;
2363+ if ( e != null )
2364+ {
2365+ s_diagnosticListener . WriteCommandError ( operationId , this , _transaction , e ) ;
2366+ }
2367+ else
2368+ {
2369+ s_diagnosticListener . WriteCommandAfter ( operationId , this , _transaction ) ;
2370+ }
23352371 }
23362372 }
23372373 }
@@ -2433,10 +2469,18 @@ private void CleanupExecuteReaderAsync(Task<SqlDataReader> task, TaskCompletionS
24332469 if ( task . IsFaulted )
24342470 {
24352471 Exception e = task . Exception . InnerException ;
2472+ if ( ! _parentOperationStarted )
2473+ {
2474+ s_diagnosticListener . WriteCommandError ( operationId , this , _transaction , e ) ;
2475+ }
24362476 source . SetException ( e ) ;
24372477 }
24382478 else
24392479 {
2480+ if ( ! _parentOperationStarted )
2481+ {
2482+ s_diagnosticListener . WriteCommandAfter ( operationId , this , _transaction ) ;
2483+ }
24402484 if ( task . IsCanceled )
24412485 {
24422486 source . SetCanceled ( ) ;
@@ -2832,7 +2876,7 @@ private Task<int> InternalExecuteNonQueryAsync(CancellationToken cancellationTok
28322876 {
28332877 SqlClientEventSource . Log . TryCorrelationTraceEvent ( "SqlCommand.InternalExecuteNonQueryAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'" , ObjectID , ActivityCorrelator . Current , Connection ? . ClientConnectionId , CommandText ) ;
28342878 SqlConnection . ExecutePermission . Demand ( ) ;
2835- Guid operationId = Guid . Empty ;
2879+ Guid operationId = s_diagnosticListener . WriteCommandBefore ( this , _transaction ) ;
28362880
28372881 // connection can be used as state in RegisterForConnectionCloseNotification continuation
28382882 // to avoid an allocation so use it as the state value if possible but it can be changed if
@@ -2885,6 +2929,7 @@ private Task<int> InternalExecuteNonQueryAsync(CancellationToken cancellationTok
28852929 }
28862930 catch ( Exception e )
28872931 {
2932+ s_diagnosticListener . WriteCommandError ( operationId , this , _transaction , e ) ;
28882933 source . SetException ( e ) ;
28892934 context . Dispose ( ) ;
28902935 }
@@ -2897,6 +2942,7 @@ private void CleanupAfterExecuteNonQueryAsync(Task<int> task, TaskCompletionSour
28972942 if ( task . IsFaulted )
28982943 {
28992944 Exception e = task . Exception . InnerException ;
2945+ s_diagnosticListener . WriteCommandError ( operationId , this , _transaction , e ) ;
29002946 source . SetException ( e ) ;
29012947 }
29022948 else
@@ -2909,6 +2955,7 @@ private void CleanupAfterExecuteNonQueryAsync(Task<int> task, TaskCompletionSour
29092955 {
29102956 source . SetResult ( task . Result ) ;
29112957 }
2958+ s_diagnosticListener . WriteCommandAfter ( operationId , this , _transaction ) ;
29122959 }
29132960 }
29142961
@@ -2960,6 +3007,10 @@ private Task<SqlDataReader> InternalExecuteReaderAsync(CommandBehavior behavior,
29603007 SqlClientEventSource . Log . TryTraceEvent ( "SqlCommand.InternalExecuteReaderAsync | API> {0}, Client Connection Id {1}, Command Text = '{2}'" , ObjectID , Connection ? . ClientConnectionId , CommandText ) ;
29613008 SqlConnection . ExecutePermission . Demand ( ) ;
29623009 Guid operationId = default ( Guid ) ;
3010+ if ( ! _parentOperationStarted )
3011+ {
3012+ operationId = s_diagnosticListener . WriteCommandBefore ( this , _transaction ) ;
3013+ }
29633014
29643015 // connection can be used as state in RegisterForConnectionCloseNotification continuation
29653016 // to avoid an allocation so use it as the state value if possible but it can be changed if
@@ -3021,6 +3072,11 @@ private Task<SqlDataReader> InternalExecuteReaderAsync(CommandBehavior behavior,
30213072 }
30223073 catch ( Exception e )
30233074 {
3075+ if ( ! _parentOperationStarted )
3076+ {
3077+ s_diagnosticListener . WriteCommandError ( operationId , this , _transaction , e ) ;
3078+ }
3079+
30243080 source . SetException ( e ) ;
30253081 context ? . Dispose ( ) ;
30263082 }
@@ -3059,6 +3115,9 @@ public override Task<object> ExecuteScalarAsync(CancellationToken cancellationTo
30593115
30603116 private Task < object > InternalExecuteScalarAsync ( CancellationToken cancellationToken )
30613117 {
3118+ _parentOperationStarted = true ;
3119+ Guid operationId = s_diagnosticListener . WriteCommandBefore ( this , _transaction ) ;
3120+
30623121 return ExecuteReaderAsync ( cancellationToken ) . ContinueWith ( ( executeTask ) =>
30633122 {
30643123 TaskCompletionSource < object > source = new TaskCompletionSource < object > ( ) ;
@@ -3068,6 +3127,7 @@ private Task<object> InternalExecuteScalarAsync(CancellationToken cancellationTo
30683127 }
30693128 else if ( executeTask . IsFaulted )
30703129 {
3130+ s_diagnosticListener . WriteCommandError ( operationId , this , _transaction , executeTask . Exception . InnerException ) ;
30713131 source . SetException ( executeTask . Exception . InnerException ) ;
30723132 }
30733133 else
@@ -3086,6 +3146,7 @@ private Task<object> InternalExecuteScalarAsync(CancellationToken cancellationTo
30863146 else if ( readTask . IsFaulted )
30873147 {
30883148 reader . Dispose ( ) ;
3149+ s_diagnosticListener . WriteCommandError ( operationId , this , _transaction , readTask . Exception . InnerException ) ;
30893150 source . SetException ( readTask . Exception . InnerException ) ;
30903151 }
30913152 else
@@ -3113,10 +3174,12 @@ private Task<object> InternalExecuteScalarAsync(CancellationToken cancellationTo
31133174 }
31143175 if ( exception != null )
31153176 {
3177+ s_diagnosticListener . WriteCommandError ( operationId , this , _transaction , exception ) ;
31163178 source . SetException ( exception ) ;
31173179 }
31183180 else
31193181 {
3182+ s_diagnosticListener . WriteCommandAfter ( operationId , this , _transaction ) ;
31203183 source . SetResult ( result ) ;
31213184 }
31223185 }
@@ -3130,6 +3193,7 @@ private Task<object> InternalExecuteScalarAsync(CancellationToken cancellationTo
31303193 TaskScheduler . Default
31313194 ) ;
31323195 }
3196+ _parentOperationStarted = false ;
31333197 return source . Task ;
31343198 } , TaskScheduler . Default ) . Unwrap ( ) ;
31353199 }
@@ -3154,7 +3218,7 @@ private Task<XmlReader> InternalExecuteXmlReaderAsync(CancellationToken cancella
31543218 {
31553219 SqlClientEventSource . Log . TryCorrelationTraceEvent ( "SqlCommand.InternalExecuteXmlReaderAsync | API | Correlation | Object Id {0}, Activity Id {1}, Client Connection Id {2}, Command Text '{3}'" , ObjectID , ActivityCorrelator . Current , Connection ? . ClientConnectionId , CommandText ) ;
31563220 SqlConnection . ExecutePermission . Demand ( ) ;
3157- Guid operationId = Guid . Empty ;
3221+ Guid operationId = s_diagnosticListener . WriteCommandBefore ( this , _transaction ) ;
31583222
31593223 // connection can be used as state in RegisterForConnectionCloseNotification continuation
31603224 // to avoid an allocation so use it as the state value if possible but it can be changed if
@@ -3214,6 +3278,7 @@ private Task<XmlReader> InternalExecuteXmlReaderAsync(CancellationToken cancella
32143278 }
32153279 catch ( Exception e )
32163280 {
3281+ s_diagnosticListener . WriteCommandError ( operationId , this , _transaction , e ) ;
32173282 source . SetException ( e ) ;
32183283 }
32193284
@@ -3225,6 +3290,7 @@ private void CleanupAfterExecuteXmlReaderAsync(Task<XmlReader> task, TaskComplet
32253290 if ( task . IsFaulted )
32263291 {
32273292 Exception e = task . Exception . InnerException ;
3293+ s_diagnosticListener . WriteCommandError ( operationId , this , _transaction , e ) ;
32283294 source . SetException ( e ) ;
32293295 }
32303296 else
@@ -3237,6 +3303,7 @@ private void CleanupAfterExecuteXmlReaderAsync(Task<XmlReader> task, TaskComplet
32373303 {
32383304 source . SetResult ( task . Result ) ;
32393305 }
3306+ s_diagnosticListener . WriteCommandAfter ( operationId , this , _transaction ) ;
32403307 }
32413308 }
32423309
0 commit comments