sdk/src/Handlers/SqlServer/TraceableSqlCommand.netframework.cs (245 lines of code) (raw):
//-----------------------------------------------------------------------------
// <copyright file="TraceableSqlCommand.netframework.cs" company="Amazon.com">
// 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.
// </copyright>
//-----------------------------------------------------------------------------
using System;
using System.Data;
using System.Data.Common;
using System.Data.Sql;
using System.Data.SqlClient;
using System.Diagnostics.CodeAnalysis;
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
{
/// <summary>
/// Traceable wrapper of <see cref="SqlCommand"/>. Currently synchronized and asynchronized call
/// are traced, which includes ExecuteNonQuery, ExecuteReader, ExecuteScalar and ExecuteXmlReader.
/// </summary>
/// <seealso cref="SqlCommand" />
public class TraceableSqlCommand : DbCommand, ICloneable
{
private const string DataBaseTypeString = "sqlserver";
private IDbCommandInterceptor _interceptor { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="TraceableSqlCommand"/> class.
/// </summary>
/// <param name="collectSqlQueries">
/// Include the <see cref="TraceableSqlCommand.CommandText" /> 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 <see cref="AppSettings.CollectSqlQueries" />.
/// If a value is not provided, then the globally configured value will be used, which is false by default.
/// See the official documentation on <a href="https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.parameters?view=netframework-4.7.2">SqlCommand.Parameters</a>
/// </param>
public TraceableSqlCommand(bool? collectSqlQueries = null)
{
InnerSqlCommand = new SqlCommand();
_interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries);
}
/// <summary>
/// Initializes a new instance of the <see cref="TraceableSqlCommand"/> class.
/// </summary>
/// <param name="cmdText">The text of the query.</param>
/// <param name="collectSqlQueries">
/// Include the <see cref="TraceableSqlCommand.CommandText" /> 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 <see cref="AppSettings.CollectSqlQueries" />.
/// If a value is not provided, then the globally configured value will be used, which is false by default.
/// See the official documentation on <a href="https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.parameters?view=netframework-4.7.2">SqlCommand.Parameters</a>
/// </param>
public TraceableSqlCommand(string cmdText, bool? collectSqlQueries = null)
{
InnerSqlCommand = new SqlCommand(cmdText);
_interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries);
}
/// <summary>
/// Initializes a new instance of the <see cref="TraceableSqlCommand"/> class.
/// </summary>
/// <param name="cmdText">The text of the query.</param>
/// <param name="connection">The connection to an instance of SQL Server.</param>
/// <param name="collectSqlQueries">
/// Include the <see cref="TraceableSqlCommand.CommandText" /> 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 <see cref="AppSettings.CollectSqlQueries" />.
/// If a value is not provided, then the globally configured value will be used, which is false by default.
/// See the official documentation on <a href="https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.parameters?view=netframework-4.7.2">SqlCommand.Parameters</a>
/// </param>
public TraceableSqlCommand(string cmdText, SqlConnection connection, bool? collectSqlQueries = null)
{
InnerSqlCommand = new SqlCommand(cmdText, connection);
_interceptor = new DbCommandInterceptor(AWSXRayRecorder.Instance, collectSqlQueries);
}
/// <summary>
/// Initializes a new instance of the <see cref="TraceableSqlCommand"/> class.
/// </summary>
/// <param name="cmdText">The text of the query.</param>
/// <param name="connection">The connection to an instance of SQL Server.</param>
/// <param name="transaction">The <see cref="SqlTransaction"/> in which the <see cref="SqlCommand"/> executes.</param>
/// <param name="collectSqlQueries">
/// Include the <see cref="TraceableSqlCommand.CommandText" /> 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 <see cref="AppSettings.CollectSqlQueries" />.
/// If a value is not provided, then the globally configured value will be used, which is false by default.
/// See the official documentation on <a href="https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.parameters?view=netframework-4.7.2">SqlCommand.Parameters</a>
/// </param>
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();
}
/// <summary>
/// Occurs when the execution of a Transact-SQL statement completes.
/// </summary>
public event StatementCompletedEventHandler StatementCompleted
{
add { InnerSqlCommand.StatementCompleted += value; }
remove { InnerSqlCommand.StatementCompleted -= value; }
}
/// <summary>
/// Gets the inner SQL command.
/// </summary>
public SqlCommand InnerSqlCommand { get; private set; }
/// <summary>
/// Gets or sets the text command to run against the data source.
/// </summary>
public override string CommandText
{
get { return InnerSqlCommand.CommandText; }
set { InnerSqlCommand.CommandText = value; }
}
/// <summary>
/// Gets or sets the wait time before terminating the attempt to execute a command and generating an error.
/// </summary>
public override int CommandTimeout
{
get { return InnerSqlCommand.CommandTimeout; }
set { InnerSqlCommand.CommandTimeout = value; }
}
/// <summary>
/// Indicates or specifies how the <see cref="P:System.Data.Common.DbCommand.CommandText" /> property is interpreted.
/// </summary>
public override CommandType CommandType
{
get { return InnerSqlCommand.CommandType; }
set { InnerSqlCommand.CommandType = value; }
}
/// <summary>
/// Gets or sets the <see cref="T:System.Data.Common.DbConnection" /> used by this <see cref="T:System.Data.Common.DbCommand" />.
/// </summary>
public new SqlConnection Connection
{
get { return InnerSqlCommand.Connection; }
set { InnerSqlCommand.Connection = value; }
}
/// <summary>
/// Gets or sets a value indicating whether the command object should be visible in a customized interface control.
/// </summary>
public override bool DesignTimeVisible
{
get { return InnerSqlCommand.DesignTimeVisible; }
set { InnerSqlCommand.DesignTimeVisible = value; }
}
/// <summary>
/// Gets or sets a value that specifies the System.Data.Sql.SqlNotificationRequest
/// object bound to this command.
/// </summary>
public SqlNotificationRequest Notification
{
get { return InnerSqlCommand.Notification; }
set { InnerSqlCommand.Notification = value; }
}
/// <summary>
/// Gets or sets a value indicating whether the application should automatically
/// receive query notifications from a common System.Data.SqlClient.SqlDependency
/// object.
/// </summary>
public bool NotificationAutoEnlist
{
get { return InnerSqlCommand.NotificationAutoEnlist; }
set { InnerSqlCommand.NotificationAutoEnlist = value; }
}
/// <summary>
/// Gets the collection of <see cref="T:System.Data.SqlClient.SqlParameter" /> objects.
/// </summary>
public new SqlParameterCollection Parameters
{
get { return InnerSqlCommand.Parameters; }
}
/// <summary>
/// Gets or sets the <see cref="T:System.Data.SqlClient.SqlTransaction" /> within which this <see cref="T:System.Data.SqlClient.SqlCommand" /> object executes.
/// </summary>
public new SqlTransaction Transaction
{
get { return InnerSqlCommand.Transaction; }
set { InnerSqlCommand.Transaction = value; }
}
/// <summary>
/// Gets or sets how command results are applied to the <see cref="T:System.Data.DataRow" /> when used by the Update method of a <see cref="T:System.Data.Common.DbDataAdapter" />.
/// </summary>
public override UpdateRowSource UpdatedRowSource
{
get { return InnerSqlCommand.UpdatedRowSource; }
set { InnerSqlCommand.UpdatedRowSource = value; }
}
/// <summary>
/// Gets or sets the <see cref="T:System.Data.Common.DbConnection" /> used by this <see cref="T:System.Data.Common.DbCommand" />.
/// </summary>
protected override DbConnection DbConnection
{
get { return Connection; }
set { Connection = (SqlConnection)value; }
}
/// <summary>
/// Gets or sets the <see cref="P:System.Data.Common.DbCommand.DbTransaction" /> within which this <see cref="T:System.Data.Common.DbCommand" /> object executes.
/// </summary>
protected override DbTransaction DbTransaction
{
get { return Transaction; }
set { Transaction = (SqlTransaction)value; }
}
/// <summary>
/// Gets the collection of <see cref="T:System.Data.Common.DbParameter" /> objects.
/// </summary>
protected override DbParameterCollection DbParameterCollection
{
get { return Parameters; }
}
/// <summary>
/// Begins the execute non query. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>An System.IAsyncResult that can be used to poll or wait for results, or both;</returns>
public IAsyncResult BeginExecuteNonQuery()
{
return InnerSqlCommand.BeginExecuteNonQuery();
}
/// <summary>
/// Begins the execute non query. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="callback">The callback.</param>
/// <param name="stateObject">The state object.</param>
/// <returns>An System.IAsyncResult that can be used to poll or wait for results, or both;</returns>
public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject)
{
return InnerSqlCommand.BeginExecuteNonQuery(callback, stateObject);
}
/// <summary>
/// Begins the execute reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>An System.IAsyncResult that can be used to poll or wait for results, or both;</returns>
public IAsyncResult BeginExecuteReader()
{
return InnerSqlCommand.BeginExecuteReader();
}
/// <summary>
/// Begins the execute reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="behavior">The behavior.</param>
/// <returns>An System.IAsyncResult that can be used to poll or wait for results, or both;</returns>
public IAsyncResult BeginExecuteReader(CommandBehavior behavior)
{
return InnerSqlCommand.BeginExecuteReader(behavior);
}
/// <summary>
/// Begins the execute reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="callback">The callback.</param>
/// <param name="stateObject">The state object.</param>
/// <returns>An System.IAsyncResult that can be used to poll or wait for results, or both;</returns>
public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject)
{
return InnerSqlCommand.BeginExecuteReader(callback, stateObject);
}
/// <summary>
/// Begins the execute reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="callback">The callback.</param>
/// <param name="stateObject">The state object.</param>
/// <param name="behavior">The behavior.</param>
/// <returns>An System.IAsyncResult that can be used to poll or wait for results, or both;</returns>
public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject, CommandBehavior behavior)
{
return InnerSqlCommand.BeginExecuteReader(callback, stateObject, behavior);
}
/// <summary>
/// Begins the execute XML reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>An System.IAsyncResult that can be used to poll or wait for results, or both;</returns>
public IAsyncResult BeginExecuteXmlReader()
{
return InnerSqlCommand.BeginExecuteXmlReader();
}
/// <summary>
/// Begins the execute XML reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="callback">The callback.</param>
/// <param name="stateObject">The state object.</param>
/// <returns>An System.IAsyncResult that can be used to poll or wait for results, or both;</returns>
public IAsyncResult BeginExecuteXmlReader(AsyncCallback callback, object stateObject)
{
return InnerSqlCommand.BeginExecuteXmlReader(callback, stateObject);
}
/// <summary>
/// Attempts to cancels the execution of a <see cref="T:System.Data.SqlClient.SqlCommand" />.
/// </summary>
public override void Cancel()
{
InnerSqlCommand.Cancel();
}
/// <summary>
/// Clones this instance.
/// </summary>
/// <returns>A new <see cref="TraceableSqlCommand"/> object that is a copy of this instance.</returns>
public TraceableSqlCommand Clone()
{
return new TraceableSqlCommand(this);
}
object ICloneable.Clone()
{
return Clone();
}
/// <summary>
/// Creates the parameter. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>A System.Data.SqlClient.SqlParameter object.</returns>
public new SqlParameter CreateParameter()
{
return InnerSqlCommand.CreateParameter();
}
/// <summary>
/// Ends the execute non query. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="asyncResult">The asynchronous result.</param>
/// <returns>The number of rows affected (the same behavior as System.Data.SqlClient.SqlCommand.ExecuteNonQuery()).</returns>
public int EndExecuteNonQuery(IAsyncResult asyncResult)
{
return InnerSqlCommand.EndExecuteNonQuery(asyncResult);
}
/// <summary>
/// Ends the execute reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="asyncResult">The asynchronous result.</param>
/// <returns>A System.Data.SqlClient.SqlDataReader object that can be used to retrieve the requested rows.</returns>
public SqlDataReader EndExecuteReader(IAsyncResult asyncResult)
{
return InnerSqlCommand.EndExecuteReader(asyncResult);
}
/// <summary>
/// Ends the execute XML reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="asyncResult">The asynchronous result.</param>
/// <returns>An System.Xml.XmlReader object that can be used to fetch the resulting XML data.</returns>
public XmlReader EndExecuteXmlReader(IAsyncResult asyncResult)
{
return InnerSqlCommand.EndExecuteXmlReader(asyncResult);
}
/// <summary>
/// Executes a SQL statement against a connection object. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>
/// The number of rows affected.
/// </returns>
public override int ExecuteNonQuery()
{
return Intercept(() => InnerSqlCommand.ExecuteNonQuery());
}
/// <summary>
/// This is the asynchronous version of <see cref="M:System.Data.Common.DbCommand.ExecuteNonQuery" />. Providers should override with an appropriate implementation.
/// The cancellation token may optionally be ignored.The default implementation invokes the synchronous <see cref="M:System.Data.Common.DbCommand.ExecuteNonQuery" /> 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 <see cref="M:System.Data.Common.DbCommand.ExecuteNonQuery" /> 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 <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// </returns>
public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken)
{
return InterceptAsync(() => InnerSqlCommand.ExecuteNonQueryAsync(cancellationToken));
}
/// <summary>
/// Executes the reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>A System.Data.SqlClient.SqlDataReader object.</returns>
public new SqlDataReader ExecuteReader()
{
return Intercept(() => InnerSqlCommand.ExecuteReader());
}
/// <summary>
/// Executes the reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="behavior">The behavior.</param>
/// <returns>A System.Data.SqlClient.SqlDataReader object.</returns>
public new SqlDataReader ExecuteReader(CommandBehavior behavior)
{
return Intercept(() => InnerSqlCommand.ExecuteReader(behavior));
}
/// <summary>
/// Executes the reader asynchronous. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public new Task<SqlDataReader> ExecuteReaderAsync()
{
return InterceptAsync(() => InnerSqlCommand.ExecuteReaderAsync());
}
/// <summary>
/// Executes the reader asynchronous. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public new Task<SqlDataReader> ExecuteReaderAsync(CancellationToken cancellationToken)
{
return InterceptAsync(() => InnerSqlCommand.ExecuteReaderAsync(cancellationToken));
}
/// <summary>
/// Executes the reader asynchronous. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="behavior">The behavior.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public new Task<SqlDataReader> ExecuteReaderAsync(CommandBehavior behavior)
{
return InterceptAsync(() => InnerSqlCommand.ExecuteReaderAsync(behavior));
}
/// <summary>
/// Executes the reader asynchronous. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="behavior">The behavior.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public new Task<SqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
{
return InterceptAsync(() => InnerSqlCommand.ExecuteReaderAsync(behavior, cancellationToken));
}
/// <summary>
/// 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 <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>
/// The first column of the first row in the result set.
/// </returns>
public override object ExecuteScalar()
{
return Intercept(() => InnerSqlCommand.ExecuteScalar());
}
/// <summary>
/// This is the asynchronous version of <see cref="M:System.Data.Common.DbCommand.ExecuteScalar" />. Providers should override with an appropriate implementation.
/// The cancellation token may optionally be ignored.The default implementation invokes the synchronous <see cref="M:System.Data.Common.DbCommand.ExecuteScalar" /> 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 <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>
/// A task representing the asynchronous operation.
/// </returns>
public override Task<object> ExecuteScalarAsync(CancellationToken cancellationToken)
{
return InterceptAsync(() => InnerSqlCommand.ExecuteScalarAsync(cancellationToken));
}
/// <summary>
/// Executes the XML reader. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>An System.Xml.XmlReader object.</returns>
public XmlReader ExecuteXmlReader()
{
return Intercept(() => InnerSqlCommand.ExecuteXmlReader());
}
/// <summary>
/// Executes the XML reader asynchronous. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>A task representing the asynchronous operation.</returns>
public Task<XmlReader> ExecuteXmlReaderAsync()
{
return InterceptAsync(() => InnerSqlCommand.ExecuteXmlReaderAsync());
}
/// <summary>
/// Executes the XML reader asynchronous. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>A task representing the asynchronous operation.</returns>
public Task<XmlReader> ExecuteXmlReaderAsync(CancellationToken cancellationToken)
{
return InterceptAsync(() => InnerSqlCommand.ExecuteXmlReaderAsync(cancellationToken));
}
/// <summary>
/// Creates a prepared (or compiled) version of the command on the data source. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
public override void Prepare()
{
InnerSqlCommand.Prepare();
}
/// <summary>
/// Resets the command timeout. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
public void ResetCommandTimeout()
{
InnerSqlCommand.ResetCommandTimeout();
}
/// <summary>
/// Creates a new instance of a <see cref="T:System.Data.Common.DbParameter" /> object. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <returns>
/// A <see cref="T:System.Data.Common.DbParameter" /> object.
/// </returns>
protected override DbParameter CreateDbParameter()
{
return InnerSqlCommand.CreateParameter();
}
/// <summary>
/// Executes the command text against the connection. Wrapper of the same function in <see cref="T:System.Data.SqlClient.SqlCommand"/>.
/// </summary>
/// <param name="behavior">An instance of <see cref="T:System.Data.CommandBehavior" />.</param>
/// <returns>
/// A task representing the operation.
/// </returns>
protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
{
return Intercept(() => InnerSqlCommand.ExecuteReader(behavior));
}
private TResult Intercept<TResult>(Func<TResult> method)
=> _interceptor.Intercept(method, this);
private async Task<TResult> InterceptAsync<TResult>(Func<Task<TResult>> method)
=> await _interceptor.InterceptAsync(method, this);
}
}