public interface IDataReader : IDisposable, IDataRecord
{
int Depth { get; }
int RecordsAffected { get; }
bool IsClosed { get; }
void Close();
bool Read();
bool NextResult();
DataTable GetSchemaTable();
}
public abstract class DbDataReader : MarshalByRefObject, IDataReader, IEnumerable
{
protected DbDataReader()
: base()
{
}
abstract public bool HasRows { get; }
abstract public bool IsClosed { get; }
abstract public int RecordsAffected { get; }
virtual public void Close()
{
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
Close();
}
}
abstract public bool Read();
public Task<bool> ReadAsync()
{
return ReadAsync(CancellationToken.None);
}
virtual public Task<bool> ReadAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return ADP.CreatedTaskWithCancellation<bool>();
}
else
{
try
{
return Read() ? ADP.TrueTask : ADP.FalseTask;
}
catch (Exception e)
{
return ADP.CreatedTaskWithException<bool>(e);
}
}
}
abstract public bool NextResult();
public Task<bool> NextResultAsync()
{
return NextResultAsync(CancellationToken.None);
}
virtual public Task<bool> NextResultAsync(CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return ADP.CreatedTaskWithCancellation<bool>();
}
else
{
try
{
return NextResult() ? ADP.TrueTask : ADP.FalseTask;
}
catch (Exception e)
{
return ADP.CreatedTaskWithException<bool>(e);
}
}
}
abstract public bool IsDBNull(int ordinal);
public Task<bool> IsDBNullAsync(int ordinal)
{
return IsDBNullAsync(ordinal, CancellationToken.None);
}
virtual public Task<bool> IsDBNullAsync(int ordinal, CancellationToken cancellationToken)
{
if (cancellationToken.IsCancellationRequested)
{
return ADP.CreatedTaskWithCancellation<bool>();
}
else
{
try
{
return IsDBNull(ordinal) ? ADP.TrueTask : ADP.FalseTask;
}
catch (Exception e)
{
return ADP.CreatedTaskWithException<bool>(e);
}
}
}
}
public class SqlDataReader : DbDataReader, IDataReader
{
private SqlCommand _command;
private SqlConnection _connection;
private CommandBehavior _commandBehavior;
private Task _currentTask;
private Snapshot _snapshot;
private CancellationTokenSource _cancelAsyncOnCloseTokenSource;
private CancellationToken _cancelAsyncOnCloseToken;
internal SqlDataReader(SqlCommand command, CommandBehavior behavior)
{
SqlConnection.VerifyExecutePermission();
_command = command;
_commandBehavior = behavior;
if (_command != null)
{
_defaultTimeoutMilliseconds = (long)command.CommandTimeout * 1000L;
_connection = command.Connection;
if (_connection != null)
{
_statistics = _connection.Statistics;
_typeSystem = _connection.TypeSystem;
}
}
_sharedState._dataReady = false;
_metaDataConsumed = false;
_hasRows = false;
_browseModeInfoConsumed = false;
_currentStream = null;
_currentTextReader = null;
_cancelAsyncOnCloseTokenSource = new CancellationTokenSource();
_cancelAsyncOnCloseToken = _cancelAsyncOnCloseTokenSource.Token;
_columnDataCharsIndex = -1;
}
override public void Close()
{
SqlStatistics statistics = null;
IntPtr hscp;
Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.Close|API> %d#", ObjectID);
try
{
statistics = SqlStatistics.StartTimer(Statistics);
TdsParserStateObject stateObj = _stateObj;
_cancelAsyncOnCloseTokenSource.Cancel();
var currentTask = _currentTask;
if ((currentTask != null) && (!currentTask.IsCompleted))
{
try
{
((IAsyncResult)currentTask).AsyncWaitHandle.WaitOne();
var networkPacketTaskSource = stateObj._networkPacketTaskSource;
if (networkPacketTaskSource != null)
{
((IAsyncResult)networkPacketTaskSource.Task).AsyncWaitHandle.WaitOne();
}
}
catch (Exception)
{
_connection.InnerConnection.DoomThisConnection();
_isClosed = true;
if (stateObj != null)
{
lock (stateObj)
{
_stateObj = null;
_command = null;
_connection = null;
}
}
throw;
}
}
CloseActiveSequentialStreamAndTextReader();
if (stateObj != null)
{
lock (stateObj)
{
if (_stateObj != null)
{
if (_snapshot != null)
{
#if DEBUG
// The stack trace for replays will differ since they weren't captured during close
stateObj._permitReplayStackTraceToDiffer = true;
#endif
PrepareForAsyncContinuation();
}
SetTimeout(_defaultTimeoutMilliseconds);
stateObj._syncOverAsync = true;
if (!TryCloseInternal(true /*closeReader*/ ))
{
throw SQL.SynchronousCallMayNotPend();
}
}
}
}
}
finally
{
SqlStatistics.StopTimer(statistics);
Bid.ScopeLeave(ref hscp);
}
}
override public bool Read()
{
if (_currentTask != null)
{
throw SQL.PendingBeginXXXExists();
}
bool more;
bool result;
Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
result = TryReadInternal(true, out more);
if (!result) { throw SQL.SynchronousCallMayNotPend(); }
return more;
}
override public bool NextResult()
{
if (_currentTask != null)
{
throw SQL.PendingBeginXXXExists();
}
bool more;
bool result;
Debug.Assert(_stateObj == null || _stateObj._syncOverAsync, "Should not attempt pends in a synchronous call");
result = TryNextResult(out more);
if (!result) { throw SQL.SynchronousCallMayNotPend(); }
return more;
}
public override Task<bool> ReadAsync(CancellationToken cancellationToken)
{
IntPtr hscp;
Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.ReadAsync|API> %d#", ObjectID);
try
{
if (IsClosed)
{
return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("ReadAsync")));
}
if (cancellationToken.IsCancellationRequested)
{
return ADP.CreatedTaskWithCancellation<bool>();
}
if (_currentTask != null)
{
return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists()));
}
bool rowTokenRead = false;
bool more = false;
try
{
if ((!_haltRead) && ((!_sharedState._dataReady) || (WillHaveEnoughData(_metaData.Length - 1))))
{
#if DEBUG
try {
_stateObj._shouldHaveEnoughData = true;
#endif
if (_sharedState._dataReady)
{
CleanPartialReadReliable();
}
if (_stateObj.IsRowTokenReady())
{
bool result = TryReadInternal(true, out more);
Debug.Assert(result, "Should not have run out of data");
rowTokenRead = true;
if (more)
{
if (IsCommandBehavior(CommandBehavior.SequentialAccess))
{
return ADP.TrueTask;
}
else if (WillHaveEnoughData(_metaData.Length - 1))
{
result = TryReadColumn(_metaData.Length - 1, setTimeout: true);
Debug.Assert(result, "Should not have run out of data");
return ADP.TrueTask;
}
}
else
{
return ADP.FalseTask;
}
}
#if DEBUG
}
finally {
_stateObj._shouldHaveEnoughData = false;
}
#endif
}
}
catch (Exception ex)
{
if (!ADP.IsCatchableExceptionType(ex))
{
throw;
}
return ADP.CreatedTaskWithException<bool>(ex);
}
TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
if (original != null)
{
source.SetException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists()));
return source.Task;
}
if (_cancelAsyncOnCloseToken.IsCancellationRequested)
{
source.SetCanceled();
_currentTask = null;
return source.Task;
}
IDisposable registration = null;
if (cancellationToken.CanBeCanceled)
{
registration = cancellationToken.Register(_command.CancelIgnoreFailure);
}
PrepareAsyncInvocation(useSnapshot: true);
Func<Task, Task<bool>> moreFunc = null;
moreFunc = (t) =>
{
if (t != null)
{
Bid.Trace("<sc.SqlDataReader.ReadAsync> attempt retry %d#\n", ObjectID);
PrepareForAsyncContinuation();
}
if (rowTokenRead || TryReadInternal(true, out more))
{
if (!more || (_commandBehavior & CommandBehavior.SequentialAccess) == CommandBehavior.SequentialAccess)
{
return more ? ADP.TrueTask : ADP.FalseTask;
}
else
{
if (!rowTokenRead)
{
rowTokenRead = true;
_snapshot = null;
PrepareAsyncInvocation(useSnapshot: true);
}
if (TryReadColumn(_metaData.Length - 1, true))
{
// completed
return ADP.TrueTask;
}
}
}
return ContinueRetryable(moreFunc);
};
return InvokeRetryable(moreFunc, source, registration);
}
finally
{
Bid.ScopeLeave(ref hscp);
}
}
public override Task<bool> NextResultAsync(CancellationToken cancellationToken)
{
IntPtr hscp;
Bid.ScopeEnter(out hscp, "<sc.SqlDataReader.NextResultAsync|API> %d#", ObjectID);
try
{
TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
if (IsClosed)
{
source.SetException(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("NextResultAsync")));
return source.Task;
}
IDisposable registration = null;
if (cancellationToken.CanBeCanceled)
{
if (cancellationToken.IsCancellationRequested)
{
source.SetCanceled();
return source.Task;
}
registration = cancellationToken.Register(_command.CancelIgnoreFailure);
}
Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
if (original != null)
{
source.SetException(ADP.ExceptionWithStackTrace(SQL.PendingBeginXXXExists()));
return source.Task;
}
if (_cancelAsyncOnCloseToken.IsCancellationRequested)
{
source.SetCanceled();
_currentTask = null;
return source.Task;
}
PrepareAsyncInvocation(useSnapshot: true);
Func<Task, Task<bool>> moreFunc = null;
moreFunc = (t) =>
{
if (t != null)
{
Bid.Trace("<sc.SqlDataReader.NextResultAsync> attempt retry %d#\n", ObjectID);
PrepareForAsyncContinuation();
}
bool more;
if (TryNextResult(out more))
{
return more ? ADP.TrueTask : ADP.FalseTask;
}
return ContinueRetryable(moreFunc);
};
return InvokeRetryable(moreFunc, source, registration);
}
finally
{
Bid.ScopeLeave(ref hscp);
}
}
override public Task<bool> IsDBNullAsync(int i, CancellationToken cancellationToken)
{
try
{
CheckHeaderIsReady(columnIndex: i, methodName: "IsDBNullAsync");
}
catch (Exception ex)
{
if (!ADP.IsCatchableExceptionType(ex))
{
throw;
}
return ADP.CreatedTaskWithException<bool>(ex);
}
if ((_sharedState._nextColumnHeaderToRead > i) && (!cancellationToken.IsCancellationRequested) && (_currentTask == null))
{
var data = _data;
if (data != null)
{
return data[i].IsNull ? ADP.TrueTask : ADP.FalseTask;
}
else
{
return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("IsDBNullAsync")));
}
}
else
{
if (_currentTask != null)
{
return ADP.CreatedTaskWithException<bool>(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
}
if (cancellationToken.IsCancellationRequested)
{
return ADP.CreatedTaskWithCancellation<bool>();
}
try
{
if (WillHaveEnoughData(i, headerOnly: true))
{
#if DEBUG
try {
_stateObj._shouldHaveEnoughData = true;
#endif
ReadColumnHeader(i);
return _data[i].IsNull ? ADP.TrueTask : ADP.FalseTask;
#if DEBUG
}
finally {
_stateObj._shouldHaveEnoughData = false;
}
#endif
}
}
catch (Exception ex)
{
if (!ADP.IsCatchableExceptionType(ex))
{
throw;
}
return ADP.CreatedTaskWithException<bool>(ex);
}
TaskCompletionSource<bool> source = new TaskCompletionSource<bool>();
Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
if (original != null)
{
source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
return source.Task;
}
if (_cancelAsyncOnCloseToken.IsCancellationRequested)
{
source.SetCanceled();
_currentTask = null;
return source.Task;
}
IDisposable registration = null;
if (cancellationToken.CanBeCanceled)
{
registration = cancellationToken.Register(_command.CancelIgnoreFailure);
}
// Setup async
PrepareAsyncInvocation(useSnapshot: true);
// Setup the retryable function
Func<Task, Task<bool>> moreFunc = null;
moreFunc = (t) =>
{
if (t != null)
{
PrepareForAsyncContinuation();
}
if (TryReadColumnHeader(i))
{
return _data[i].IsNull ? ADP.TrueTask : ADP.FalseTask;
}
else
{
return ContinueRetryable(moreFunc);
}
};
// Go!
return InvokeRetryable(moreFunc, source, registration);
}
}
override public Task<T> GetFieldValueAsync<T>(int i, CancellationToken cancellationToken)
{
try
{
CheckDataIsReady(columnIndex: i, methodName: "GetFieldValueAsync");
if ((!IsCommandBehavior(CommandBehavior.SequentialAccess)) && (_sharedState._nextColumnDataToRead > i) && (!cancellationToken.IsCancellationRequested) && (_currentTask == null))
{
var data = _data;
var metaData = _metaData;
if ((data != null) && (metaData != null))
{
return Task.FromResult<T>(GetFieldValueFromSqlBufferInternal<T>(data[i], metaData[i]));
}
else
{
return ADP.CreatedTaskWithException<T>(ADP.ExceptionWithStackTrace(ADP.DataReaderClosed("GetFieldValueAsync")));
}
}
}
catch (Exception ex)
{
if (!ADP.IsCatchableExceptionType(ex))
{
throw;
}
return ADP.CreatedTaskWithException<T>(ex);
}
if (_currentTask != null)
{
return ADP.CreatedTaskWithException<T>(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
}
if (cancellationToken.IsCancellationRequested)
{
return ADP.CreatedTaskWithCancellation<T>();
}
try
{
if (WillHaveEnoughData(i))
{
#if DEBUG
try {
_stateObj._shouldHaveEnoughData = true;
#endif
return Task.FromResult(GetFieldValueInternal<T>(i));
#if DEBUG
}
finally {
_stateObj._shouldHaveEnoughData = false;
}
#endif
}
}
catch (Exception ex)
{
if (!ADP.IsCatchableExceptionType(ex))
{
throw;
}
return ADP.CreatedTaskWithException<T>(ex);
}
TaskCompletionSource<T> source = new TaskCompletionSource<T>();
Task original = Interlocked.CompareExchange(ref _currentTask, source.Task, null);
if (original != null)
{
source.SetException(ADP.ExceptionWithStackTrace(ADP.AsyncOperationPending()));
return source.Task;
}
if (_cancelAsyncOnCloseToken.IsCancellationRequested)
{
source.SetCanceled();
_currentTask = null;
return source.Task;
}
IDisposable registration = null;
if (cancellationToken.CanBeCanceled)
{
registration = cancellationToken.Register(_command.CancelIgnoreFailure);
}
PrepareAsyncInvocation(useSnapshot: true);
Func<Task, Task<T>> moreFunc = null;
moreFunc = (t) =>
{
if (t != null)
{
PrepareForAsyncContinuation();
}
if (TryReadColumn(i, setTimeout: false))
{
return Task.FromResult<T>(GetFieldValueFromSqlBufferInternal<T>(_data[i], _metaData[i]));
}
else
{
return ContinueRetryable(moreFunc);
}
};
// Go!
return InvokeRetryable(moreFunc, source, registration);
}
}