//----------------------------------------------------------------------------- // // Copyright 2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"). // You may not use this file except in compliance with the License. // A copy of the License is located at // // http://aws.amazon.com/apache2.0 // // or in the "license" file accompanying this file. This file is distributed // on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either // express or implied. See the License for the specific language governing // permissions and limitations under the License. // //----------------------------------------------------------------------------- using System; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Threading; using System.Threading.Tasks; using System.Xml; using Amazon.XRay.Recorder.Core; using Amazon.XRay.Recorder.Core.Internal.Utils; namespace Amazon.XRay.Recorder.Handlers.SqlServer { /// /// Traceable wrapper of . Currently synchronized and asynchronized call /// are traced, which includes ExecuteNonQuery, ExecuteReader, ExecuteScalar and ExecuteXmlReader. /// /// public class TraceableSqlCommand : DbCommand, ICloneable { private IDbCommandInterceptor _interceptor { get; set; } /// /// Initializes a new instance of the class. /// /// /// Include the in the sanitized_query section of /// the SQL subsegment. Parameterized values will appear in their tokenized form and will not be expanded. /// You should not enable this flag if you are including sensitive information as clear text. /// This flag will override any behavior configured by . /// If a value is not provided, then the globally configured value will be used, which is false by default. /// See the official documentation on SqlCommand.Parameters /// public TraceableSqlCommand(bool? collectSqlQueries = null) { InnerSqlCommand = new SqlCommand(); _interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries); } /// /// Initializes a new instance of the class. /// /// The text of the query. /// /// Include the in the sanitized_query section of /// the SQL subsegment. Parameterized values will appear in their tokenized form and will not be expanded. /// You should not enable this flag if you are including sensitive information as clear text. /// This flag will override any behavior configured by . /// If a value is not provided, then the globally configured value will be used, which is false by default. /// See the official documentation on SqlCommand.Parameters /// public TraceableSqlCommand(string cmdText, bool? collectSqlQueries = null) { InnerSqlCommand = new SqlCommand(cmdText); _interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries); } /// /// Initializes a new instance of the class. /// /// The text of the query. /// The connection to an instance of SQL Server. /// /// Include the in the sanitized_query section of /// the SQL subsegment. Parameterized values will appear in their tokenized form and will not be expanded. /// You should not enable this flag if you are including sensitive information as clear text. /// This flag will override any behavior configured by . /// If a value is not provided, then the globally configured value will be used, which is false by default. /// See the official documentation on SqlCommand.Parameters /// public TraceableSqlCommand(string cmdText, SqlConnection connection, bool? collectSqlQueries = null) { InnerSqlCommand = new SqlCommand(cmdText, connection); _interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries); } /// /// Initializes a new instance of the class. /// /// The text of the query. /// The connection to an instance of SQL Server. /// The in which the executes. /// /// Include the in the sanitized_query section of /// the SQL subsegment. Parameterized values will appear in their tokenized form and will not be expanded. /// You should not enable this flag if you are including sensitive information as clear text. /// This flag will override any behavior configured by . /// If a value is not provided, then the globally configured value will be used, which is false by default. /// See the official documentation on SqlCommand.Parameters /// public TraceableSqlCommand(string cmdText, SqlConnection connection, SqlTransaction transaction, bool? collectSqlQueries = null) { InnerSqlCommand = new SqlCommand(cmdText, connection, transaction); _interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries); } private TraceableSqlCommand(TraceableSqlCommand from) { InnerSqlCommand = from.InnerSqlCommand.Clone(); } /// /// Occurs when the execution of a Transact-SQL statement completes. /// public event StatementCompletedEventHandler StatementCompleted { add { InnerSqlCommand.StatementCompleted += value; } remove { InnerSqlCommand.StatementCompleted -= value; } } /// /// Gets the inner SQL command. /// public SqlCommand InnerSqlCommand { get; private set; } /// /// Gets or sets the text command to run against the data source. /// public override string CommandText { get { return InnerSqlCommand.CommandText; } set { InnerSqlCommand.CommandText = value; } } /// /// Gets or sets the wait time before terminating the attempt to execute a command and generating an error. /// public override int CommandTimeout { get { return InnerSqlCommand.CommandTimeout; } set { InnerSqlCommand.CommandTimeout = value; } } /// /// Indicates or specifies how the property is interpreted. /// public override CommandType CommandType { get { return InnerSqlCommand.CommandType; } set { InnerSqlCommand.CommandType = value; } } /// /// Gets or sets the used by this . /// public new SqlConnection Connection { get { return InnerSqlCommand.Connection; } set { InnerSqlCommand.Connection = value; } } /// /// Gets or sets a value indicating whether the command object should be visible in a customized interface control. /// public override bool DesignTimeVisible { get { return InnerSqlCommand.DesignTimeVisible; } set { InnerSqlCommand.DesignTimeVisible = value; } } /// /// Gets the collection of objects. /// public new SqlParameterCollection Parameters { get { return InnerSqlCommand.Parameters; } } /// /// Gets or sets the within which this object executes. /// public new SqlTransaction Transaction { get { return InnerSqlCommand.Transaction; } set { InnerSqlCommand.Transaction = value; } } /// /// Gets or sets how command results are applied to the when used by the Update method of a . /// public override UpdateRowSource UpdatedRowSource { get { return InnerSqlCommand.UpdatedRowSource; } set { InnerSqlCommand.UpdatedRowSource = value; } } /// /// Gets or sets the used by this . /// protected override DbConnection DbConnection { get { return Connection; } set { Connection = (SqlConnection)value; } } /// /// Gets or sets the within which this object executes. /// protected override DbTransaction DbTransaction { get { return Transaction; } set { Transaction = (SqlTransaction)value; } } /// /// Gets the collection of objects. /// protected override DbParameterCollection DbParameterCollection { get { return Parameters; } } /// /// Attempts to cancels the execution of a . /// public override void Cancel() { InnerSqlCommand.Cancel(); } /// /// Clones this instance. /// /// A new object that is a copy of this instance. public TraceableSqlCommand Clone() { return new TraceableSqlCommand(this); } object ICloneable.Clone() { return Clone(); } /// /// Creates the parameter. Wrapper of the same function in . /// /// A System.Data.SqlClient.SqlParameter object. public new SqlParameter CreateParameter() { return InnerSqlCommand.CreateParameter(); } /// /// Executes a SQL statement against a connection object. Wrapper of the same function in . /// /// /// The number of rows affected. /// public override int ExecuteNonQuery() { return Intercept(() => InnerSqlCommand.ExecuteNonQuery()); } /// /// This is the asynchronous version of . Providers should override with an appropriate implementation. /// The cancellation token may optionally be ignored.The default implementation invokes the synchronous method and /// returns a completed task, blocking the calling thread. The default implementation will return a cancelled task if passed an already cancelled cancellation token. /// Exceptions thrown by will be communicated via the returned Task Exception property.Do not invoke other methods /// and properties of the DbCommand object until the returned Task is complete. /// Wrapper of the same function in . /// /// The token to monitor for cancellation requests. /// /// A task representing the asynchronous operation. /// public override Task ExecuteNonQueryAsync(CancellationToken cancellationToken) { return InterceptAsync(() => InnerSqlCommand.ExecuteNonQueryAsync(cancellationToken)); } /// /// Executes the reader. Wrapper of the same function in . /// /// A System.Data.SqlClient.SqlDataReader object. public new SqlDataReader ExecuteReader() { return Intercept(() => InnerSqlCommand.ExecuteReader()); } /// /// Executes the reader. Wrapper of the same function in . /// /// The behavior. /// A System.Data.SqlClient.SqlDataReader object. public new SqlDataReader ExecuteReader(CommandBehavior behavior) { return Intercept(() => InnerSqlCommand.ExecuteReader(behavior)); } /// /// Executes the reader asynchronous. Wrapper of the same function in . /// /// A task representing the asynchronous operation. public new Task ExecuteReaderAsync() { return InterceptAsync(() => InnerSqlCommand.ExecuteReaderAsync()); } /// /// Executes the reader asynchronous. Wrapper of the same function in . /// /// The cancellation token. /// A task representing the asynchronous operation. public new Task ExecuteReaderAsync(CancellationToken cancellationToken) { return InterceptAsync(() => InnerSqlCommand.ExecuteReaderAsync(cancellationToken)); } /// /// Executes the reader asynchronous. Wrapper of the same function in . /// /// The behavior. /// A task representing the asynchronous operation. public new Task ExecuteReaderAsync(CommandBehavior behavior) { return InterceptAsync(() => InnerSqlCommand.ExecuteReaderAsync(behavior)); } /// /// Executes the reader asynchronous. Wrapper of the same function in . /// /// The behavior. /// The cancellation token. /// A task representing the asynchronous operation. public new Task ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { return InterceptAsync(() => InnerSqlCommand.ExecuteReaderAsync(behavior, cancellationToken)); } /// /// Executes the query and returns the first column of the first row in the result set returned by the query. All other columns and rows are ignored. /// Wrapper of the same function in . /// /// /// The first column of the first row in the result set. /// public override object ExecuteScalar() { return Intercept(() => InnerSqlCommand.ExecuteScalar()); } /// /// This is the asynchronous version of . Providers should override with an appropriate implementation. /// The cancellation token may optionally be ignored.The default implementation invokes the synchronous method and returns /// a completed task, blocking the calling thread. The default implementation will return a cancelled task if passed an already cancelled cancellation token. Exceptions thrown by ExecuteScalar will be /// communicated via the returned Task Exception property.Do not invoke other methods and properties of the DbCommand object until the returned Task is complete. /// Wrapper of the same function in . /// /// The token to monitor for cancellation requests. /// /// A task representing the asynchronous operation. /// public override Task ExecuteScalarAsync(CancellationToken cancellationToken) { return InterceptAsync(() => InnerSqlCommand.ExecuteScalarAsync(cancellationToken)); } /// /// Executes the XML reader. Wrapper of the same function in . /// /// An System.Xml.XmlReader object. public XmlReader ExecuteXmlReader() { return Intercept(() => InnerSqlCommand.ExecuteXmlReader()); } /// /// Executes the XML reader asynchronous. Wrapper of the same function in . /// /// A task representing the asynchronous operation. public Task ExecuteXmlReaderAsync() { return InterceptAsync(() => InnerSqlCommand.ExecuteXmlReaderAsync()); } /// /// Executes the XML reader asynchronous. Wrapper of the same function in . /// /// The cancellation token. /// A task representing the asynchronous operation. public Task ExecuteXmlReaderAsync(CancellationToken cancellationToken) { return InterceptAsync(() => InnerSqlCommand.ExecuteXmlReaderAsync(cancellationToken)); } /// /// Creates a prepared (or compiled) version of the command on the data source. Wrapper of the same function in . /// public override void Prepare() { InnerSqlCommand.Prepare(); } /// /// Creates a new instance of a object. Wrapper of the same function in . /// /// /// A object. /// protected override DbParameter CreateDbParameter() { return InnerSqlCommand.CreateParameter(); } /// /// Executes the command text against the connection. Wrapper of the same function in . /// /// An instance of . /// /// A task representing the operation. /// protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) { return Intercept(() => InnerSqlCommand.ExecuteReader(behavior)); } protected virtual async Task InterceptAsync(Func> method) => await _interceptor.InterceptAsync(method, this); protected virtual TResult Intercept(Func method) => _interceptor.Intercept(method, this); } }