SqlCommand

public interface IDbCommand : IDisposable
{
    IDbConnection Connection { get; set; }

    IDbTransaction Transaction { get; set; }
    string CommandText { get; set; }
    int CommandTimeout { get; set; }
    CommandType CommandType { get; set; }
    IDataParameterCollection Parameters { get; }


    void Cancel();

    IDbDataParameter CreateParameter();


    int ExecuteNonQuery();

    IDataReader ExecuteReader();

    IDataReader ExecuteReader(CommandBehavior behavior);

    object ExecuteScalar();
}

 

public abstract class DbCommand : Component, IDbCommand
{
    protected DbCommand()
        : base()
    {
    }

abstract protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior); abstract public int ExecuteNonQuery(); public DbDataReader ExecuteReader() { return (DbDataReader)ExecuteDbDataReader(CommandBehavior.Default); } public DbDataReader ExecuteReader(CommandBehavior behavior) { return (DbDataReader)ExecuteDbDataReader(behavior); } IDataReader IDbCommand.ExecuteReader() { return (DbDataReader)ExecuteDbDataReader(CommandBehavior.Default); } IDataReader IDbCommand.ExecuteReader(CommandBehavior behavior) { return (DbDataReader)ExecuteDbDataReader(behavior); } public Task<int> ExecuteNonQueryAsync() { return ExecuteNonQueryAsync(CancellationToken.None); } public virtual Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return ADP.CreatedTaskWithCancellation<int>(); } else { CancellationTokenRegistration registration = new CancellationTokenRegistration(); if (cancellationToken.CanBeCanceled) { registration = cancellationToken.Register(CancelIgnoreFailure); } try { return Task.FromResult<int>(ExecuteNonQuery()); } catch (Exception e) { registration.Dispose(); return ADP.CreatedTaskWithException<int>(e); } } } public Task<DbDataReader> ExecuteReaderAsync() { return ExecuteReaderAsync(CommandBehavior.Default, CancellationToken.None); } public Task<DbDataReader> ExecuteReaderAsync(CancellationToken cancellationToken) { return ExecuteReaderAsync(CommandBehavior.Default, cancellationToken); } public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior) { return ExecuteReaderAsync(behavior, CancellationToken.None); } public Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { return ExecuteDbDataReaderAsync(behavior, cancellationToken); } protected virtual Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return ADP.CreatedTaskWithCancellation<DbDataReader>(); } else { CancellationTokenRegistration registration = new CancellationTokenRegistration(); if (cancellationToken.CanBeCanceled) { registration = cancellationToken.Register(CancelIgnoreFailure); } try { return Task.FromResult<DbDataReader>(ExecuteReader(behavior)); } catch (Exception e) { registration.Dispose(); return ADP.CreatedTaskWithException<DbDataReader>(e); } } } }

 

public sealed class SqlCommand : DbCommand, ICloneable
{
    private SqlParameterCollection _parameters;
    private SqlConnection _activeConnection;

    TaskCompletionSource<object> _reconnectionCompletionSource = null;

    private SqlNotificationRequest _notification;
    private bool _notificationAutoEnlist = true;

    private SqlTransaction _transaction;


    public SqlCommand()
        : base()
    {
        GC.SuppressFinalize(this);
    }

    public SqlCommand(string cmdText)
        : this()
    {
        CommandText = cmdText;
    }

    public SqlCommand(string cmdText, SqlConnection connection)
        : this()
    {
        CommandText = cmdText;
        Connection = connection;
    }

    public SqlCommand(string cmdText, SqlConnection connection, SqlTransaction transaction)
        : this()
    {
        CommandText = cmdText;
        Connection = connection;
        Transaction = transaction;
    }


    override protected void Dispose(bool disposing)
    {
        if (disposing)
        {
            _cachedMetaData = null;
        }

        base.Dispose(disposing);
    }



    override public int ExecuteNonQuery()
    {
        SqlConnection.ExecutePermission.Demand();

        _pendingCancel = false;

        SqlStatistics statistics = null;
        IntPtr hscp;
        Bid.ScopeEnter(out hscp, "<sc.SqlCommand.ExecuteNonQuery|API> %d#", ObjectID);
        Bid.CorrelationTrace("<sc.SqlCommand.ExecuteNonQuery|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
        bool success = false;
        int? sqlExceptionNumber = null;
        try
        {
            statistics = SqlStatistics.StartTimer(Statistics);
            WriteBeginExecuteEvent();
            bool usedCache;
            InternalExecuteNonQuery(null, ADP.ExecuteNonQuery, false, CommandTimeout, out usedCache);
            success = true;
            return _rowsAffected;
        }
        catch (SqlException ex)
        {
            sqlExceptionNumber = ex.Number;
            throw;
        }
        finally
        {
            SqlStatistics.StopTimer(statistics);
            Bid.ScopeLeave(ref hscp);
            WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
        }
    }


    [System.Security.Permissions.HostProtectionAttribute(ExternalThreading = true)]
    public IAsyncResult BeginExecuteNonQuery()
    {
        return BeginExecuteNonQuery(null, null);
    }


    [System.Security.Permissions.HostProtectionAttribute(ExternalThreading = true)]
    public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback, object stateObject)
    {
        Bid.CorrelationTrace("<sc.SqlCommand.BeginExecuteNonQuery|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
        SqlConnection.ExecutePermission.Demand();

        return BeginExecuteNonQueryInternal(0, callback, stateObject, 0, inRetry: false);
    }

    private IAsyncResult BeginExecuteNonQueryAsync(AsyncCallback callback, object stateObject)
    {
        return BeginExecuteNonQueryInternal(0, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite: true);
    }

    private IAsyncResult BeginExecuteNonQueryInternal(CommandBehavior behavior, AsyncCallback callback, object stateObject, int timeout, bool inRetry, bool asyncWrite = false)
    {
        TaskCompletionSource<object> globalCompletion = new TaskCompletionSource<object>(stateObject);
        TaskCompletionSource<object> localCompletion = new TaskCompletionSource<object>(stateObject);

        if (!inRetry)
        {
            _pendingCancel = false;

            ValidateAsyncCommand(); 
        }

        SqlStatistics statistics = null;
        try
        {
            if (!inRetry)
            {
                statistics = SqlStatistics.StartTimer(Statistics);
                WriteBeginExecuteEvent();
            }

            bool usedCache;
            try
            { 
                Task execNQ = InternalExecuteNonQuery(localCompletion, ADP.BeginExecuteNonQuery, false, timeout, out usedCache, asyncWrite, inRetry: inRetry);
                if (execNQ != null)
                {
                    AsyncHelper.ContinueTask(execNQ, localCompletion, () => BeginExecuteNonQueryInternalReadStage(localCompletion));
                }
                else
                {
                    BeginExecuteNonQueryInternalReadStage(localCompletion);
                }
            }
            catch (Exception e)
            {
                if (!ADP.IsCatchableOrSecurityExceptionType(e))
                {
                    throw;
                }

                ReliablePutStateObject();
                throw;
            }

            if (!TriggerInternalEndAndRetryIfNecessary(behavior, stateObject, timeout, ADP.EndExecuteNonQuery, usedCache, inRetry, asyncWrite, globalCompletion, localCompletion, InternalEndExecuteNonQuery, BeginExecuteNonQueryInternal))
            {
                globalCompletion = localCompletion;
            }

            if (callback != null)
            {
                globalCompletion.Task.ContinueWith((t) => callback(t), TaskScheduler.Default);
            }

            return globalCompletion.Task;
        }
        finally
        {
            SqlStatistics.StopTimer(statistics);
        }
    }


    public int EndExecuteNonQuery(IAsyncResult asyncResult)
    {
        try
        {
            return EndExecuteNonQueryInternal(asyncResult);
        }
        finally
        {
            Bid.CorrelationTrace("<sc.SqlCommand.EndExecuteNonQuery|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
        }
    }

    private int EndExecuteNonQueryAsync(IAsyncResult asyncResult)
    {
        Bid.CorrelationTrace("<sc.SqlCommand.EndExecuteNonQueryAsync|Info|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
        Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null);

        Exception asyncException = ((Task)asyncResult).Exception;
        if (asyncException != null)
        {
            ReliablePutStateObject();
            throw asyncException.InnerException;
        }
        else
        {
            ThrowIfReconnectionHasBeenCanceled();
            if (!_internalEndExecuteInitiated)
            {
                lock (_stateObj)
                {
                    return EndExecuteNonQueryInternal(asyncResult);
                }
            }
            else
            {
                return EndExecuteNonQueryInternal(asyncResult);
            }
        }
    }



    [System.Security.Permissions.HostProtectionAttribute(ExternalThreading = true)]
    public IAsyncResult BeginExecuteReader()
    {
        return BeginExecuteReader(null, null, CommandBehavior.Default);
    }

    [System.Security.Permissions.HostProtectionAttribute(ExternalThreading = true)]
    public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject)
    {
        return BeginExecuteReader(callback, stateObject, CommandBehavior.Default);
    }

    override protected DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
    {
        Bid.CorrelationTrace("<sc.SqlCommand.ExecuteDbDataReader|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
        return ExecuteReader(behavior, ADP.ExecuteReader);
    }

    new public SqlDataReader ExecuteReader()
    {
        SqlStatistics statistics = null;
        IntPtr hscp;
        Bid.ScopeEnter(out hscp, "<sc.SqlCommand.ExecuteReader|API> %d#", ObjectID);
        Bid.CorrelationTrace("<sc.SqlCommand.ExecuteReader|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
        try
        {
            statistics = SqlStatistics.StartTimer(Statistics);
            return ExecuteReader(CommandBehavior.Default, ADP.ExecuteReader);
        }
        finally
        {
            SqlStatistics.StopTimer(statistics);
            Bid.ScopeLeave(ref hscp);
        }
    }

    new public SqlDataReader ExecuteReader(CommandBehavior behavior)
    {
        IntPtr hscp;
        Bid.ScopeEnter(out hscp, "<sc.SqlCommand.ExecuteReader|API> %d#, behavior=%d{ds.CommandBehavior}", ObjectID, (int)behavior);
        Bid.CorrelationTrace("<sc.SqlCommand.ExecuteReader|API|Correlation> ObjectID%d#, behavior=%d{ds.CommandBehavior}, ActivityID %ls\n", ObjectID, (int)behavior);
        try
        {
            return ExecuteReader(behavior, ADP.ExecuteReader);
        }
        finally
        {
            Bid.ScopeLeave(ref hscp);
        }
    }


    [System.Security.Permissions.HostProtectionAttribute(ExternalThreading = true)]
    public IAsyncResult BeginExecuteReader(CommandBehavior behavior)
    {
        return BeginExecuteReader(null, null, behavior);
    }

    [System.Security.Permissions.HostProtectionAttribute(ExternalThreading = true)]
    public IAsyncResult BeginExecuteReader(AsyncCallback callback, object stateObject, CommandBehavior behavior)
    {
        Bid.CorrelationTrace("<sc.SqlCommand.BeginExecuteReader|API|Correlation> ObjectID%d#, behavior=%d{ds.CommandBehavior}, ActivityID %ls\n", ObjectID, (int)behavior);
        SqlConnection.ExecutePermission.Demand();
        return BeginExecuteReaderInternal(behavior, callback, stateObject, 0, inRetry: false);
    }

    internal SqlDataReader ExecuteReader(CommandBehavior behavior, string method)
    {
        SqlConnection.ExecutePermission.Demand(); 

        _pendingCancel = false;

        SqlStatistics statistics = null;

        TdsParser bestEffortCleanupTarget = null;
        RuntimeHelpers.PrepareConstrainedRegions();
        bool success = false;
        int? sqlExceptionNumber = null;
        try
        {
            WriteBeginExecuteEvent();
#if DEBUG
                TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
 
                RuntimeHelpers.PrepareConstrainedRegions();
                try {
                    tdsReliabilitySection.Start();
#else
            {
#endif //DEBUG
                bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
                statistics = SqlStatistics.StartTimer(Statistics);
                SqlDataReader result = RunExecuteReader(behavior, RunBehavior.ReturnImmediately, true, method);
                success = true;
                return result;
            }
#if DEBUG
                finally {
                    tdsReliabilitySection.Stop();
                }
#endif //DEBUG
        }
        catch (SqlException e)
        {
            sqlExceptionNumber = e.Number;
            throw;
        }
        catch (System.OutOfMemoryException e)
        {
            _activeConnection.Abort(e);
            throw;
        }
        catch (System.StackOverflowException e)
        {
            _activeConnection.Abort(e);
            throw;
        }
        catch (System.Threading.ThreadAbortException e)
        {
            _activeConnection.Abort(e);
            SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
            throw;
        }
        finally
        {
            SqlStatistics.StopTimer(statistics);
            WriteEndExecuteEvent(success, sqlExceptionNumber, synchronous: true);
        }
    }

    public SqlDataReader EndExecuteReader(IAsyncResult asyncResult)
    {
        try
        {
            return EndExecuteReaderInternal(asyncResult);
        }
        finally
        {
            Bid.CorrelationTrace("<sc.SqlCommand.EndExecuteReader|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
        }
    }

    private SqlDataReader EndExecuteReaderAsync(IAsyncResult asyncResult)
    {
        Bid.CorrelationTrace("<sc.SqlCommand.EndExecuteReaderAsync|Info|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
        Debug.Assert(!_internalEndExecuteInitiated || _stateObj == null);

        Exception asyncException = ((Task)asyncResult).Exception;
        if (asyncException != null)
        {
            ReliablePutStateObject();
            throw asyncException.InnerException;
        }
        else
        {
            ThrowIfReconnectionHasBeenCanceled();
            if (!_internalEndExecuteInitiated)
            {
                lock (_stateObj)
                {
                    return EndExecuteReaderInternal(asyncResult);
                }
            }
            else
            {
                return EndExecuteReaderInternal(asyncResult);
            }
        }
    }

    private IAsyncResult BeginExecuteReaderAsync(CommandBehavior behavior, AsyncCallback callback, object stateObject)
    {
        return BeginExecuteReaderInternal(behavior, callback, stateObject, CommandTimeout, inRetry: false, asyncWrite: true);
    }

    public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken)
    {

        Bid.CorrelationTrace("<sc.SqlCommand.ExecuteNonQueryAsync|API|Correlation> ObjectID%d#, ActivityID %ls\n", ObjectID);
        SqlConnection.ExecutePermission.Demand();

        TaskCompletionSource<int> source = new TaskCompletionSource<int>();

        CancellationTokenRegistration registration = new CancellationTokenRegistration();
        if (cancellationToken.CanBeCanceled)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                source.SetCanceled();
                return source.Task;
            }
            registration = cancellationToken.Register(CancelIgnoreFailure);
        }

        Task<int> returnedTask = source.Task;
        try
        {
            RegisterForConnectionCloseNotification(ref returnedTask);

            Task<int>.Factory.FromAsync(BeginExecuteNonQueryAsync, EndExecuteNonQueryAsync, null).ContinueWith((t) =>
            {
                registration.Dispose();
                if (t.IsFaulted)
                {
                    Exception e = t.Exception.InnerException;
                    source.SetException(e);
                }
                else
                {
                    if (t.IsCanceled)
                    {
                        source.SetCanceled();
                    }
                    else
                    {
                        source.SetResult(t.Result);
                    }
                }
            }, TaskScheduler.Default);
        }
        catch (Exception e)
        {
            source.SetException(e);
        }

        return returnedTask;
    }

    protected override Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
    {
        return ExecuteReaderAsync(behavior, cancellationToken).ContinueWith<DbDataReader>((result) =>
        {
            if (result.IsFaulted)
            {
                throw result.Exception.InnerException;
            }
            return result.Result;
        }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default);
    }

    new public Task<SqlDataReader> ExecuteReaderAsync()
    {
        return ExecuteReaderAsync(CommandBehavior.Default, CancellationToken.None);
    }

    new public Task<SqlDataReader> ExecuteReaderAsync(CommandBehavior behavior)
    {
        return ExecuteReaderAsync(behavior, CancellationToken.None);
    }

    new public Task<SqlDataReader> ExecuteReaderAsync(CancellationToken cancellationToken)
    {
        return ExecuteReaderAsync(CommandBehavior.Default, cancellationToken);
    }

    new public Task<SqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
    {
        Bid.CorrelationTrace("<sc.SqlCommand.ExecuteReaderAsync|API|Correlation> ObjectID%d#, behavior=%d{ds.CommandBehavior}, ActivityID %ls\n", ObjectID, (int)behavior);
        SqlConnection.ExecutePermission.Demand();

        TaskCompletionSource<SqlDataReader> source = new TaskCompletionSource<SqlDataReader>();

        CancellationTokenRegistration registration = new CancellationTokenRegistration();
        if (cancellationToken.CanBeCanceled)
        {
            if (cancellationToken.IsCancellationRequested)
            {
                source.SetCanceled();
                return source.Task;
            }
            registration = cancellationToken.Register(CancelIgnoreFailure);
        }

        Task<SqlDataReader> returnedTask = source.Task;
        try
        {
            RegisterForConnectionCloseNotification(ref returnedTask);

            Task<SqlDataReader>.Factory.FromAsync(BeginExecuteReaderAsync, EndExecuteReaderAsync, behavior, null).ContinueWith((t) =>
            {
                registration.Dispose();
                if (t.IsFaulted)
                {
                    Exception e = t.Exception.InnerException;
                    source.SetException(e);
                }
                else
                {
                    if (t.IsCanceled)
                    {
                        source.SetCanceled();
                    }
                    else
                    {
                        source.SetResult(t.Result);
                    }
                }
            }, TaskScheduler.Default);
        }
        catch (Exception e)
        {
            source.SetException(e);
        }

        return returnedTask;
    }

    internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, string method)
    {
        Task unused; // sync execution 
        bool usedCache;
        SqlDataReader reader = RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, completion: null, timeout: CommandTimeout, task: out unused, usedCache: out usedCache);
        Debug.Assert(unused == null, "returned task during synchronous execution");
        return reader;
    }

    internal SqlDataReader RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, string method, TaskCompletionSource<object> completion, int timeout, out Task task, out bool usedCache, bool asyncWrite = false, bool inRetry = false)
    {
        bool async = (null != completion);
        usedCache = false;

        task = null;

        _rowsAffected = -1;
        _rowsAffectedBySpDescribeParameterEncryption = -1;

        if (0 != (CommandBehavior.SingleRow & cmdBehavior))
        {
            cmdBehavior |= CommandBehavior.SingleResult;
        }

        if (!inRetry)
        {
            ValidateCommand(method, async);
        }

        CheckNotificationStateAndAutoEnlist();

        TdsParser bestEffortCleanupTarget = null;
        RuntimeHelpers.PrepareConstrainedRegions();
        try
        {
#if DEBUG
                TdsParser.ReliabilitySection tdsReliabilitySection = new TdsParser.ReliabilitySection();
 
                RuntimeHelpers.PrepareConstrainedRegions();
                try {
                    tdsReliabilitySection.Start();
#else
            {
#endif //DEBUG
                bestEffortCleanupTarget = SqlInternalConnection.GetBestEffortCleanupTarget(_activeConnection);
                SqlStatistics statistics = Statistics;
                if (null != statistics)
                {
                    if ((!this.IsDirty && this.IsPrepared && !_hiddenPrepare)
                        || (this.IsPrepared && _execType == EXECTYPE.PREPAREPENDING))
                    {
                        statistics.SafeIncrement(ref statistics._preparedExecs);
                    }
                    else
                    {
                        statistics.SafeIncrement(ref statistics._unpreparedExecs);
                    }
                }

                ResetEncryptionState();

                if (_activeConnection.IsContextConnection)
                {
                    return RunExecuteReaderSmi(cmdBehavior, runBehavior, returnStream);
                }
                else if (IsColumnEncryptionEnabled)
                {
                    Task returnTask = null;
                    PrepareForTransparentEncryption(cmdBehavior, returnStream, async, timeout, completion, out returnTask, asyncWrite && async, out usedCache, inRetry);
                    Debug.Assert(usedCache || (async == (returnTask != null)), @"if we didn't use the cache, returnTask should be null if and only if async is false.");

                    long firstAttemptStart = ADP.TimerCurrent();

                    try
                    {
                        return RunExecuteReaderTdsWithTransparentParameterEncryption(cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite && async, inRetry: inRetry, ds: null,
                            describeParameterEncryptionRequest: false, describeParameterEncryptionTask: returnTask);
                    }
                    catch (SqlException ex)
                    {
                        if (inRetry || async || !usedCache)
                        {
                            throw;
                        }

                        bool shouldRetry = false;

                        for (int i = 0; i < ex.Errors.Count; i++)
                        {
                            if (ex.Errors[i].Number == TdsEnums.TCE_CONVERSION_ERROR_CLIENT_RETRY)
                            {
                                shouldRetry = true;
                                break;
                            }
                        }

                        if (!shouldRetry)
                        {
                            throw;
                        }
                        else
                        {
                            SqlQueryMetadataCache.GetInstance().InvalidateCacheEntry(this);
                            return RunExecuteReader(cmdBehavior, runBehavior, returnStream, method, null, TdsParserStaticMethods.GetRemainingTimeout(timeout, firstAttemptStart), out task, out usedCache, async, inRetry: true);
                        }
                    }
                }
                else
                {
                    return RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite && async, inRetry: inRetry);
                }
            }
#if DEBUG
                finally {
                    tdsReliabilitySection.Stop();
                }
#endif //DEBUG
        }
        catch (System.OutOfMemoryException e)
        {
            _activeConnection.Abort(e);
            throw;
        }
        catch (System.StackOverflowException e)
        {
            _activeConnection.Abort(e);
            throw;
        }
        catch (System.Threading.ThreadAbortException e)
        {
            _activeConnection.Abort(e);
            SqlInternalConnection.BestEffortCleanup(bestEffortCleanupTarget);
            throw;
        }
    }

    private SqlDataReader RunExecuteReaderTdsWithTransparentParameterEncryption(CommandBehavior cmdBehavior,
                                                                                RunBehavior runBehavior,
                                                                                bool returnStream,
                                                                                bool async,
                                                                                int timeout,
                                                                                out Task task,
                                                                                bool asyncWrite,
                                                                                bool inRetry,
                                                                                SqlDataReader ds = null,
                                                                                bool describeParameterEncryptionRequest = false,
                                                                                Task describeParameterEncryptionTask = null)
    {
        Debug.Assert(!asyncWrite || async, "AsyncWrite should be always accompanied by Async");

        if (ds == null && returnStream)
        {
            ds = new SqlDataReader(this, cmdBehavior);
        }

        if (describeParameterEncryptionTask != null)
        {
            long parameterEncryptionStart = ADP.TimerCurrent();
            TaskCompletionSource<object> completion = new TaskCompletionSource<object>();
            AsyncHelper.ContinueTask(describeParameterEncryptionTask, completion,
                () =>
                {
                    Task subTask = null;
                    RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, parameterEncryptionStart), out subTask, asyncWrite, inRetry, ds);
                    if (subTask == null)
                    {
                        completion.SetResult(null);
                    }
                    else
                    {
                        AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null));
                    }
                }, connectionToDoom: null,
                onFailure: ((exception) =>
                {
                    if (_cachedAsyncState != null)
                    {
                        _cachedAsyncState.ResetAsyncState();
                    }
                    if (exception != null)
                    {
                        throw exception;
                    }
                }),
                onCancellation: (() =>
                {
                    if (_cachedAsyncState != null)
                    {
                        _cachedAsyncState.ResetAsyncState();
                    }
                }),
                connectionToAbort: _activeConnection);
            task = completion.Task;
            return ds;
        }
        else
        {
            // Synchronous execution.
            return RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, timeout, out task, asyncWrite, inRetry, ds);
        }
    }

    private SqlDataReader RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, bool returnStream, bool async, int timeout, out Task task, bool asyncWrite, bool inRetry, SqlDataReader ds = null, bool describeParameterEncryptionRequest = false)
    {
        Debug.Assert(!asyncWrite || async, "AsyncWrite should be always accompanied by Async");

        if (ds == null && returnStream)
        {
            ds = new SqlDataReader(this, cmdBehavior);
        }

        Task reconnectTask = _activeConnection.ValidateAndReconnect(null, timeout);

        if (reconnectTask != null)
        {
            long reconnectionStart = ADP.TimerCurrent();
            if (async)
            {
                TaskCompletionSource<object> completion = new TaskCompletionSource<object>();
                _activeConnection.RegisterWaitingForReconnect(completion.Task);
                _reconnectionCompletionSource = completion;
                CancellationTokenSource timeoutCTS = new CancellationTokenSource();
                AsyncHelper.SetTimeoutException(completion, timeout, SQL.CR_ReconnectTimeout, timeoutCTS.Token);
                AsyncHelper.ContinueTask(reconnectTask, completion,
                    () =>
                    {
                        if (completion.Task.IsCompleted)
                        {
                            return;
                        }
                        Interlocked.CompareExchange(ref _reconnectionCompletionSource, null, completion);
                        timeoutCTS.Cancel();
                        Task subTask;
                        RunExecuteReaderTds(cmdBehavior, runBehavior, returnStream, async, TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart), out subTask, asyncWrite, inRetry, ds);
                        if (subTask == null)
                        {
                            completion.SetResult(null);
                        }
                        else
                        {
                            AsyncHelper.ContinueTask(subTask, completion, () => completion.SetResult(null));
                        }
                    }, connectionToAbort: _activeConnection);
                task = completion.Task;
                return ds;
            }
            else
            {
                AsyncHelper.WaitForCompletion(reconnectTask, timeout, () => { throw SQL.CR_ReconnectTimeout(); });
                timeout = TdsParserStaticMethods.GetRemainingTimeout(timeout, reconnectionStart);
            }
        }


        Debug.Assert(null != _activeConnection.Parser, "TdsParser class should not be null in Command.Execute!");

        bool inSchema = (0 != (cmdBehavior & CommandBehavior.SchemaOnly));

        // create a new RPC
        _SqlRPC rpc = null;

        task = null;

        string optionSettings = null;
        bool processFinallyBlock = true;
        bool decrementAsyncCountOnFailure = false;

        if (async && !inRetry)
        {
            _activeConnection.GetOpenTdsConnection().IncrementAsyncCount();
            decrementAsyncCountOnFailure = true;
        }

        try
        {

            if (asyncWrite)
            {
                _activeConnection.AddWeakReference(this, SqlReferenceCollection.CommandTag);
            }

            GetStateObject();
            Task writeTask = null;

            if (describeParameterEncryptionRequest)
            {
#if DEBUG
                    if (_sleepDuringRunExecuteReaderTdsForSpDescribeParameterEncryption) {
                        Thread.Sleep(10000);
                    }
#endif

                Debug.Assert(_sqlRPCParameterEncryptionReqArray != null, "RunExecuteReader rpc array not provided for describe parameter encryption request.");
                writeTask = _stateObj.Parser.TdsExecuteRPC(this, _sqlRPCParameterEncryptionReqArray, timeout, inSchema, this.Notification, _stateObj, CommandType.StoredProcedure == CommandType, sync: !asyncWrite);
            }
            else if (BatchRPCMode)
            {
                Debug.Assert(inSchema == false, "Batch RPC does not support schema only command beahvior");
                Debug.Assert(!IsPrepared, "Batch RPC should not be prepared!");
                Debug.Assert(!IsDirty, "Batch RPC should not be marked as dirty!");
                //Currently returnStream is always false, but we may want to return a Reader later.
                //if (returnStream) {
                //    Bid.Trace("<sc.SqlCommand.ExecuteReader|INFO> %d#, Command executed as batch RPC.\n", ObjectID);
                //}
                Debug.Assert(_SqlRPCBatchArray != null, "RunExecuteReader rpc array not provided");
                writeTask = _stateObj.Parser.TdsExecuteRPC(this, _SqlRPCBatchArray, timeout, inSchema, this.Notification, _stateObj, CommandType.StoredProcedure == CommandType, sync: !asyncWrite);
            }
            else if ((System.Data.CommandType.Text == this.CommandType) && (0 == GetParameterCount(_parameters)))
            {
                Debug.Assert(!IsUserPrepared, "CommandType.Text with no params should not be prepared!");
                if (returnStream)
                {
                    Bid.Trace("<sc.SqlCommand.ExecuteReader|INFO> %d#, Command executed as SQLBATCH.\n", ObjectID);
                }
                string text = GetCommandText(cmdBehavior) + GetResetOptionsString(cmdBehavior);
                writeTask = _stateObj.Parser.TdsExecuteSQLBatch(text, timeout, this.Notification, _stateObj, sync: !asyncWrite);
            }
            else if (System.Data.CommandType.Text == this.CommandType)
            {
                if (this.IsDirty)
                {
                    Debug.Assert(_cachedMetaData == null || !_dirty, "dirty query should not have cached metadata!"); 
                    if (_execType == EXECTYPE.PREPARED)
                    {
                        _hiddenPrepare = true;
                    }
                    Unprepare();
                    IsDirty = false;
                }

                if (_execType == EXECTYPE.PREPARED)
                {
                    Debug.Assert(this.IsPrepared && (_prepareHandle != -1), "invalid attempt to call sp_execute without a handle!");
                    rpc = BuildExecute(inSchema);
                }
                else if (_execType == EXECTYPE.PREPAREPENDING)
                {
                    Debug.Assert(_activeConnection.IsShiloh, "Invalid attempt to call sp_prepexec on non 7.x server");
                    rpc = BuildPrepExec(cmdBehavior);
                    // next time through, only do an exec
                    _execType = EXECTYPE.PREPARED;
                    _preparedConnectionCloseCount = _activeConnection.CloseCount;
                    _preparedConnectionReconnectCount = _activeConnection.ReconnectCount;
                    // mark ourselves as preparing the command
                    _inPrepare = true;
                }
                else
                {
                    Debug.Assert(_execType == EXECTYPE.UNPREPARED, "Invalid execType!");
                    BuildExecuteSql(cmdBehavior, null, _parameters, ref rpc);
                }

                // if shiloh, then set NOMETADATA_UNLESSCHANGED flag
                if (_activeConnection.IsShiloh)
                    rpc.options = TdsEnums.RPC_NOMETADATA;
                if (returnStream)
                {
                    Bid.Trace("<sc.SqlCommand.ExecuteReader|INFO> %d#, Command executed as RPC.\n", ObjectID);
                }

                Debug.Assert(_rpcArrayOf1[0] == rpc);
                writeTask = _stateObj.Parser.TdsExecuteRPC(this, _rpcArrayOf1, timeout, inSchema, this.Notification, _stateObj, CommandType.StoredProcedure == CommandType, sync: !asyncWrite);
            }
            else
            {
                Debug.Assert(this.CommandType == System.Data.CommandType.StoredProcedure, "unknown command type!");
                Debug.Assert(IsShiloh || !IsPrepared, "RPC should not be prepared!");
                Debug.Assert(IsShiloh || !IsDirty, "RPC should not be marked as dirty!");

                BuildRPC(inSchema, _parameters, ref rpc);

                optionSettings = GetSetOptionsString(cmdBehavior);
                if (returnStream)
                {
                    Bid.Trace("<sc.SqlCommand.ExecuteReader|INFO> %d#, Command executed as RPC.\n", ObjectID);
                }
                // turn set options ON
                if (null != optionSettings)
                {
                    Task executeTask = _stateObj.Parser.TdsExecuteSQLBatch(optionSettings, timeout, this.Notification, _stateObj, sync: true);
                    Debug.Assert(executeTask == null, "Shouldn't get a task when doing sync writes");
                    bool dataReady;
                    Debug.Assert(_stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
                    bool result = _stateObj.Parser.TryRun(RunBehavior.UntilDone, this, null, null, _stateObj, out dataReady);
                    if (!result) { throw SQL.SynchronousCallMayNotPend(); }
                    // and turn OFF when the ds exhausts the stream on Close()
                    optionSettings = GetResetOptionsString(cmdBehavior);
                }

                // turn debugging on
                _activeConnection.CheckSQLDebug();

                // execute sp
                Debug.Assert(_rpcArrayOf1[0] == rpc);
                writeTask = _stateObj.Parser.TdsExecuteRPC(this, _rpcArrayOf1, timeout, inSchema, this.Notification, _stateObj, CommandType.StoredProcedure == CommandType, sync: !asyncWrite);
            }

            Debug.Assert(writeTask == null || async, "Returned task in sync mode");

            if (async)
            {
                decrementAsyncCountOnFailure = false;
                if (writeTask != null)
                {
                    task = AsyncHelper.CreateContinuationTask(writeTask, () =>
                    {
                        _activeConnection.GetOpenTdsConnection(); // it will throw if connection is closed
                        cachedAsyncState.SetAsyncReaderState(ds, runBehavior, optionSettings);
                    },
                             onFailure: (exc) =>
                             {
                                 _activeConnection.GetOpenTdsConnection().DecrementAsyncCount();
                             });
                }
                else
                {
                    cachedAsyncState.SetAsyncReaderState(ds, runBehavior, optionSettings);
                }
            }
            else
            {
                // Always execute - even if no reader!
                FinishExecuteReader(ds, runBehavior, optionSettings, isInternal: false, forDescribeParameterEncryption: false);
            }
        }
        catch (Exception e)
        {
            processFinallyBlock = ADP.IsCatchableExceptionType(e);
            if (decrementAsyncCountOnFailure)
            {
                SqlInternalConnectionTds innerConnectionTds = (_activeConnection.InnerConnection as SqlInternalConnectionTds);
                if (null != innerConnectionTds)
                { // it may be closed 
                    innerConnectionTds.DecrementAsyncCount();
                }
            }
            throw;
        }
        finally
        {
            TdsParser.ReliabilitySection.Assert("unreliable call to RunExecuteReaderTds");  
            if (processFinallyBlock && !async)
            {
                PutStateObject();
            }
        }

        Debug.Assert(async || null == _stateObj, "non-null state object in RunExecuteReader");
        return ds;
    }
}

 

posted @ 2016-11-03 22:27  茗::流  阅读(420)  评论(0)    收藏  举报
如有雷同,纯属参考。如有侵犯你的版权,请联系我。