Skip to content

Commit

Permalink
Throw detailed exception message in buffered data reader
Browse files Browse the repository at this point in the history
Resolves #20364

Adds code for error handling when create buffered data reader.
Code needs to handle unexpected nulls and incorrect type of data encountered
  • Loading branch information
smitpatel committed Aug 26, 2020
1 parent c44ef44 commit 79e693c
Show file tree
Hide file tree
Showing 16 changed files with 662 additions and 266 deletions.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,9 @@
<data name="FunctionOverrideMismatch" xml:space="preserve">
<value>The property '{propertySpecification}' has specific configuration for the function '{function}', however it isn't mapped to a column on that function return. Remove the specific configuration or map an entity type that contains this property to '{function}'.</value>
</data>
<data name="GetXMethodOnNullData" xml:space="preserve">
<value>Data is Null. This method or property cannot be called on Null values.</value>
</data>
<data name="IncompatibleTableCommentMismatch" xml:space="preserve">
<value>Cannot use table '{table}' for entity type '{entityType}' since it is being used for entity type '{otherEntityType}' and the comment '{comment}' does not match the comment '{otherComment}'.</value>
</data>
Expand Down
467 changes: 384 additions & 83 deletions src/EFCore.Relational/Query/Internal/BufferedDataReader.cs

Large diffs are not rendered by default.

64 changes: 36 additions & 28 deletions src/EFCore.Relational/Query/Internal/FromSqlQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public class FromSqlQueryingEnumerable<T> : IEnumerable<T>, IAsyncEnumerable<T>,
private readonly Type _contextType;
private readonly IDiagnosticsLogger<DbLoggerCategory.Query> _queryLogger;
private readonly bool _standAloneStateManager;
private readonly bool _detailedErrorsEnabled;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -43,7 +44,8 @@ public FromSqlQueryingEnumerable(
[NotNull] IReadOnlyList<string> columnNames,
[NotNull] Func<QueryContext, DbDataReader, int[], T> shaper,
[NotNull] Type contextType,
bool standAloneStateManager)
bool standAloneStateManager,
bool detailedErrorsEnabled)
{
_relationalQueryContext = relationalQueryContext;
_relationalCommandCache = relationalCommandCache;
Expand All @@ -52,6 +54,7 @@ public FromSqlQueryingEnumerable(
_contextType = contextType;
_queryLogger = relationalQueryContext.QueryLogger;
_standAloneStateManager = standAloneStateManager;
_detailedErrorsEnabled = detailedErrorsEnabled;
}

/// <summary>
Expand All @@ -61,7 +64,11 @@ public FromSqlQueryingEnumerable(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
=> new AsyncEnumerator(this, cancellationToken);
{
_relationalQueryContext.CancellationToken = cancellationToken;

return new AsyncEnumerator(this);
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -96,7 +103,8 @@ public virtual DbCommand CreateDbCommand()
_relationalQueryContext.ParameterValues,
null,
null,
null),
null,
_detailedErrorsEnabled),
Guid.Empty,
(DbCommandMethod)(-1));

Expand Down Expand Up @@ -144,6 +152,7 @@ private sealed class Enumerator : IEnumerator<T>
private readonly Type _contextType;
private readonly IDiagnosticsLogger<DbLoggerCategory.Query> _queryLogger;
private readonly bool _standAloneStateManager;
private readonly bool _detailedErrorsEnabled;

private RelationalDataReader _dataReader;
private int[] _indexMap;
Expand All @@ -157,6 +166,7 @@ public Enumerator(FromSqlQueryingEnumerable<T> queryingEnumerable)
_contextType = queryingEnumerable._contextType;
_queryLogger = queryingEnumerable._queryLogger;
_standAloneStateManager = queryingEnumerable._standAloneStateManager;
_detailedErrorsEnabled = queryingEnumerable._detailedErrorsEnabled;
}

public T Current { get; private set; }
Expand Down Expand Up @@ -199,14 +209,14 @@ private bool InitializeReader(DbContext _, bool result)

var relationalCommand = _relationalCommandCache.GetRelationalCommand(_relationalQueryContext.ParameterValues);

_dataReader
= relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
_relationalQueryContext.ParameterValues,
_relationalCommandCache.ReaderColumns,
_relationalQueryContext.Context,
_relationalQueryContext.CommandLogger));
_dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
_relationalQueryContext.ParameterValues,
_relationalCommandCache.ReaderColumns,
_relationalQueryContext.Context,
_relationalQueryContext.CommandLogger,
_detailedErrorsEnabled));

_indexMap = BuildIndexMap(_columnNames, _dataReader.DbDataReader);

Expand Down Expand Up @@ -234,14 +244,12 @@ private sealed class AsyncEnumerator : IAsyncEnumerator<T>
private readonly Type _contextType;
private readonly IDiagnosticsLogger<DbLoggerCategory.Query> _queryLogger;
private readonly bool _standAloneStateManager;
private readonly CancellationToken _cancellationToken;
private readonly bool _detailedErrorsEnabled;

private RelationalDataReader _dataReader;
private int[] _indexMap;

public AsyncEnumerator(
FromSqlQueryingEnumerable<T> queryingEnumerable,
CancellationToken cancellationToken)
public AsyncEnumerator(FromSqlQueryingEnumerable<T> queryingEnumerable)
{
_relationalQueryContext = queryingEnumerable._relationalQueryContext;
_relationalCommandCache = queryingEnumerable._relationalCommandCache;
Expand All @@ -250,7 +258,7 @@ public AsyncEnumerator(
_contextType = queryingEnumerable._contextType;
_queryLogger = queryingEnumerable._queryLogger;
_standAloneStateManager = queryingEnumerable._standAloneStateManager;
_cancellationToken = cancellationToken;
_detailedErrorsEnabled = queryingEnumerable._detailedErrorsEnabled;
}

public T Current { get; private set; }
Expand All @@ -264,10 +272,10 @@ public async ValueTask<bool> MoveNextAsync()
if (_dataReader == null)
{
await _relationalQueryContext.ExecutionStrategyFactory.Create()
.ExecuteAsync(true, InitializeReaderAsync, null, _cancellationToken).ConfigureAwait(false);
.ExecuteAsync(true, InitializeReaderAsync, null, _relationalQueryContext.CancellationToken).ConfigureAwait(false);
}

var hasNext = await _dataReader.ReadAsync(_cancellationToken).ConfigureAwait(false);
var hasNext = await _dataReader.ReadAsync(_relationalQueryContext.CancellationToken).ConfigureAwait(false);

Current = hasNext
? _shaper(_relationalQueryContext, _dataReader.DbDataReader, _indexMap)
Expand All @@ -290,16 +298,16 @@ private async Task<bool> InitializeReaderAsync(DbContext _, bool result, Cancell

var relationalCommand = _relationalCommandCache.GetRelationalCommand(_relationalQueryContext.ParameterValues);

_dataReader
= await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
_relationalQueryContext.ParameterValues,
_relationalCommandCache.ReaderColumns,
_relationalQueryContext.Context,
_relationalQueryContext.CommandLogger),
cancellationToken)
.ConfigureAwait(false);
_dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
_relationalQueryContext.ParameterValues,
_relationalCommandCache.ReaderColumns,
_relationalQueryContext.Context,
_relationalQueryContext.CommandLogger,
_detailedErrorsEnabled),
cancellationToken)
.ConfigureAwait(false);

_indexMap = BuildIndexMap(_columnNames, _dataReader.DbDataReader);

Expand Down
64 changes: 36 additions & 28 deletions src/EFCore.Relational/Query/Internal/SingleQueryingEnumerable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class SingleQueryingEnumerable<T> : IEnumerable<T>, IAsyncEnumerable<T>,
private readonly Type _contextType;
private readonly IDiagnosticsLogger<DbLoggerCategory.Query> _queryLogger;
private readonly bool _standAloneStateManager;
private readonly bool _detailedErrorsEnabled;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -40,14 +41,16 @@ public SingleQueryingEnumerable(
[NotNull] RelationalCommandCache relationalCommandCache,
[NotNull] Func<QueryContext, DbDataReader, ResultContext, SingleQueryResultCoordinator, T> shaper,
[NotNull] Type contextType,
bool standAloneStateManager)
bool standAloneStateManager,
bool detailedErrorsEnabled)
{
_relationalQueryContext = relationalQueryContext;
_relationalCommandCache = relationalCommandCache;
_shaper = shaper;
_contextType = contextType;
_queryLogger = relationalQueryContext.QueryLogger;
_standAloneStateManager = standAloneStateManager;
_detailedErrorsEnabled = detailedErrorsEnabled;
}

/// <summary>
Expand All @@ -57,7 +60,11 @@ public SingleQueryingEnumerable(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public virtual IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
=> new AsyncEnumerator(this, cancellationToken);
{
_relationalQueryContext.CancellationToken = cancellationToken;

return new AsyncEnumerator(this);
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down Expand Up @@ -113,6 +120,7 @@ private sealed class Enumerator : IEnumerator<T>
private readonly Type _contextType;
private readonly IDiagnosticsLogger<DbLoggerCategory.Query> _queryLogger;
private readonly bool _standAloneStateManager;
private readonly bool _detailedErrorsEnabled;

private RelationalDataReader _dataReader;
private SingleQueryResultCoordinator _resultCoordinator;
Expand All @@ -125,6 +133,7 @@ public Enumerator(SingleQueryingEnumerable<T> queryingEnumerable)
_contextType = queryingEnumerable._contextType;
_queryLogger = queryingEnumerable._queryLogger;
_standAloneStateManager = queryingEnumerable._standAloneStateManager;
_detailedErrorsEnabled = queryingEnumerable._detailedErrorsEnabled;
}

public T Current { get; private set; }
Expand Down Expand Up @@ -194,14 +203,14 @@ private bool InitializeReader(DbContext _, bool result)

var relationalCommand = _relationalCommandCache.GetRelationalCommand(_relationalQueryContext.ParameterValues);

_dataReader
= relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
_relationalQueryContext.ParameterValues,
_relationalCommandCache.ReaderColumns,
_relationalQueryContext.Context,
_relationalQueryContext.CommandLogger));
_dataReader = relationalCommand.ExecuteReader(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
_relationalQueryContext.ParameterValues,
_relationalCommandCache.ReaderColumns,
_relationalQueryContext.Context,
_relationalQueryContext.CommandLogger,
_detailedErrorsEnabled));

_resultCoordinator = new SingleQueryResultCoordinator();

Expand All @@ -228,22 +237,20 @@ private sealed class AsyncEnumerator : IAsyncEnumerator<T>
private readonly Type _contextType;
private readonly IDiagnosticsLogger<DbLoggerCategory.Query> _queryLogger;
private readonly bool _standAloneStateManager;
private readonly CancellationToken _cancellationToken;
private readonly bool _detailedErrorsEnabled;

private RelationalDataReader _dataReader;
private SingleQueryResultCoordinator _resultCoordinator;

public AsyncEnumerator(
SingleQueryingEnumerable<T> queryingEnumerable,
CancellationToken cancellationToken)
public AsyncEnumerator(SingleQueryingEnumerable<T> queryingEnumerable)
{
_relationalQueryContext = queryingEnumerable._relationalQueryContext;
_relationalCommandCache = queryingEnumerable._relationalCommandCache;
_shaper = queryingEnumerable._shaper;
_contextType = queryingEnumerable._contextType;
_queryLogger = queryingEnumerable._queryLogger;
_standAloneStateManager = queryingEnumerable._standAloneStateManager;
_cancellationToken = cancellationToken;
_detailedErrorsEnabled = queryingEnumerable._detailedErrorsEnabled;
}

public T Current { get; private set; }
Expand All @@ -257,11 +264,12 @@ public async ValueTask<bool> MoveNextAsync()
if (_dataReader == null)
{
await _relationalQueryContext.ExecutionStrategyFactory.Create()
.ExecuteAsync(true, InitializeReaderAsync, null, _cancellationToken)
.ExecuteAsync(true, InitializeReaderAsync, null, _relationalQueryContext.CancellationToken)
.ConfigureAwait(false);
}

var hasNext = _resultCoordinator.HasNext ?? await _dataReader.ReadAsync(_cancellationToken).ConfigureAwait(false);
var hasNext = _resultCoordinator.HasNext
?? await _dataReader.ReadAsync(_relationalQueryContext.CancellationToken).ConfigureAwait(false);
Current = default;

if (hasNext)
Expand All @@ -280,7 +288,7 @@ await _relationalQueryContext.ExecutionStrategyFactory.Create()
break;
}

if (!await _dataReader.ReadAsync(_cancellationToken).ConfigureAwait(false))
if (!await _dataReader.ReadAsync(_relationalQueryContext.CancellationToken).ConfigureAwait(false))
{
_resultCoordinator.HasNext = false;
// Enumeration has ended, materialize last element
Expand Down Expand Up @@ -311,16 +319,16 @@ private async Task<bool> InitializeReaderAsync(DbContext _, bool result, Cancell

var relationalCommand = _relationalCommandCache.GetRelationalCommand(_relationalQueryContext.ParameterValues);

_dataReader
= await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
_relationalQueryContext.ParameterValues,
_relationalCommandCache.ReaderColumns,
_relationalQueryContext.Context,
_relationalQueryContext.CommandLogger),
cancellationToken)
.ConfigureAwait(false);
_dataReader = await relationalCommand.ExecuteReaderAsync(
new RelationalCommandParameterObject(
_relationalQueryContext.Connection,
_relationalQueryContext.ParameterValues,
_relationalCommandCache.ReaderColumns,
_relationalQueryContext.Context,
_relationalQueryContext.CommandLogger,
_detailedErrorsEnabled),
cancellationToken)
.ConfigureAwait(false);

_resultCoordinator = new SingleQueryResultCoordinator();

Expand Down
Loading

0 comments on commit 79e693c

Please sign in to comment.