public bool MoveNext() { try { using (_relationalQueryContext.ConcurrencyDetector.EnterCriticalSection()) { if (_dataReader == null) { var relationalCommand = _relationalCommandCache.GetRelationalCommand( _relationalQueryContext.ParameterValues); _dataReader = relationalCommand.ExecuteReader( new RelationalCommandParameterObject( _relationalQueryContext.Connection, _relationalQueryContext.ParameterValues, _relationalQueryContext.Context, _relationalQueryContext.CommandLogger)); // Non-Composed FromSql if (_columnNames != null) { var readerColumns = Enumerable.Range(0, _dataReader.DbDataReader.FieldCount) .ToDictionary(i => _dataReader.DbDataReader.GetName(i), i => i, StringComparer.OrdinalIgnoreCase); _indexMap = new int[_columnNames.Count]; for (var i = 0; i < _columnNames.Count; i++) { var columnName = _columnNames[i]; if (!readerColumns.TryGetValue(columnName, out var ordinal)) { throw new InvalidOperationException(RelationalStrings.FromSqlMissingColumn(columnName)); } _indexMap[i] = ordinal; } } else { _indexMap = null; } _resultCoordinator = new ResultCoordinator(); } var hasNext = _resultCoordinator.HasNext ?? _dataReader.Read(); Current = default; if (hasNext) { while (true) { _resultCoordinator.ResultReady = true; _resultCoordinator.HasNext = null; Current = _shaper( _relationalQueryContext, _dataReader.DbDataReader, _resultCoordinator.ResultContext, _indexMap, _resultCoordinator); if (_resultCoordinator.ResultReady) { // We generated a result so null out previously stored values _resultCoordinator.ResultContext.Values = null; break; } if (!_dataReader.Read()) { _resultCoordinator.HasNext = false; // Enumeration has ended, materialize last element _resultCoordinator.ResultReady = true; Current = _shaper( _relationalQueryContext, _dataReader.DbDataReader, _resultCoordinator.ResultContext, _indexMap, _resultCoordinator); break; } } } return(hasNext); } } catch (Exception exception) { _logger.QueryIterationFailed(_contextType, exception); throw; } }
public async ValueTask <bool> MoveNextAsync() { try { if (_dataReader == null) { var selectExpression = new ParameterValueBasedSelectExpressionOptimizer( _sqlExpressionFactory, _parameterNameGeneratorFactory) .Optimize(_selectExpression, _relationalQueryContext.ParameterValues); var relationalCommand = _querySqlGeneratorFactory.Create().GetCommand(selectExpression); _dataReader = await relationalCommand.ExecuteReaderAsync( new RelationalCommandParameterObject( _relationalQueryContext.Connection, _relationalQueryContext.ParameterValues, _relationalQueryContext.Context, _relationalQueryContext.CommandLogger), _cancellationToken); if (selectExpression.IsNonComposedFromSql()) { var projection = _selectExpression.Projection.ToList(); var readerColumns = Enumerable.Range(0, _dataReader.DbDataReader.FieldCount) .ToDictionary(i => _dataReader.DbDataReader.GetName(i), i => i, StringComparer.OrdinalIgnoreCase); _indexMap = new int[projection.Count]; for (var i = 0; i < projection.Count; i++) { if (projection[i].Expression is ColumnExpression columnExpression) { var columnName = columnExpression.Name; if (columnName != null) { if (!readerColumns.TryGetValue(columnName, out var ordinal)) { throw new InvalidOperationException(RelationalStrings.FromSqlMissingColumn(columnName)); } _indexMap[i] = ordinal; } } } } else { _indexMap = null; } _resultCoordinator = new ResultCoordinator(); } var hasNext = _resultCoordinator.HasNext ?? await _dataReader.ReadAsync(_cancellationToken); _resultCoordinator.HasNext = null; Current = hasNext ? _shaper(_relationalQueryContext, _dataReader.DbDataReader, _indexMap, _resultCoordinator) : default; return(hasNext); } catch (Exception exception) { _logger.QueryIterationFailed(_contextType, exception); throw; } }
private static void PopulateIncludeCollection <TIncludingEntity, TIncludedEntity>( int collectionId, QueryContext queryContext, DbDataReader dbDataReader, ResultCoordinator resultCoordinator, Func <QueryContext, DbDataReader, object[]> parentIdentifier, Func <QueryContext, DbDataReader, object[]> outerIdentifier, Func <QueryContext, DbDataReader, object[]> selfIdentifier, Func <QueryContext, DbDataReader, ResultContext, ResultCoordinator, TIncludedEntity> innerShaper, INavigation inverseNavigation, Action <TIncludingEntity, TIncludedEntity> fixup, bool trackingQuery) { var collectionMaterializationContext = resultCoordinator.Collections[collectionId]; if (collectionMaterializationContext.Parent is TIncludingEntity entity) { if (resultCoordinator.HasNext == false) { // Outer Enumerator has ended GenerateCurrentElementIfPending(); return; } if (!StructuralComparisons.StructuralEqualityComparer.Equals( outerIdentifier(queryContext, dbDataReader), collectionMaterializationContext.OuterIdentifier)) { // Outer changed so collection has ended. Materialize last element. GenerateCurrentElementIfPending(); // If parent also changed then this row is now pointing to element of next collection if (!StructuralComparisons.StructuralEqualityComparer.Equals( parentIdentifier(queryContext, dbDataReader), collectionMaterializationContext.ParentIdentifier)) { resultCoordinator.HasNext = true; } return; } var innerKey = selfIdentifier(queryContext, dbDataReader); if (innerKey.Any(e => e == null)) { // No correlated element return; } if (collectionMaterializationContext.SelfIdentifier != null) { if (StructuralComparisons.StructuralEqualityComparer.Equals( innerKey, collectionMaterializationContext.SelfIdentifier)) { // repeated row for current element // If it is pending materialization then it may have nested elements if (collectionMaterializationContext.ResultContext.Values != null) { ProcessCurrentElementRow(); } resultCoordinator.ResultReady = false; return; } // Row for new element which is not first element // So materialize the element GenerateCurrentElementIfPending(); resultCoordinator.HasNext = null; collectionMaterializationContext.UpdateSelfIdentifier(innerKey); } else { // First row for current element collectionMaterializationContext.UpdateSelfIdentifier(innerKey); } ProcessCurrentElementRow(); resultCoordinator.ResultReady = false; } void ProcessCurrentElementRow() { var previousResultReady = resultCoordinator.ResultReady; resultCoordinator.ResultReady = true; var relatedEntity = innerShaper( queryContext, dbDataReader, collectionMaterializationContext.ResultContext, resultCoordinator); if (resultCoordinator.ResultReady) { // related entity is materialized collectionMaterializationContext.ResultContext.Values = null; if (!trackingQuery) { fixup(entity, relatedEntity); if (inverseNavigation != null) { SetIsLoadedNoTracking(relatedEntity, inverseNavigation); } } } resultCoordinator.ResultReady &= previousResultReady; } void GenerateCurrentElementIfPending() { if (collectionMaterializationContext.ResultContext.Values != null) { resultCoordinator.HasNext = false; ProcessCurrentElementRow(); } collectionMaterializationContext.UpdateSelfIdentifier(null); } }
private static void PopulateCollection <TCollection, TElement, TRelatedEntity>( int collectionId, QueryContext queryContext, DbDataReader dbDataReader, ResultCoordinator resultCoordinator, Func <QueryContext, DbDataReader, object[]> parentIdentifier, Func <QueryContext, DbDataReader, object[]> outerIdentifier, Func <QueryContext, DbDataReader, object[]> selfIdentifier, Func <QueryContext, DbDataReader, ResultContext, ResultCoordinator, TRelatedEntity> innerShaper) where TRelatedEntity : TElement where TCollection : class, ICollection <TElement> { var collectionMaterializationContext = resultCoordinator.Collections[collectionId]; if (collectionMaterializationContext.Collection is null) { // nothing to materialize since no collection created return; } if (resultCoordinator.HasNext == false) { // Outer Enumerator has ended GenerateCurrentElementIfPending(); return; } if (!StructuralComparisons.StructuralEqualityComparer.Equals( outerIdentifier(queryContext, dbDataReader), collectionMaterializationContext.OuterIdentifier)) { // Outer changed so collection has ended. Materialize last element. GenerateCurrentElementIfPending(); // If parent also changed then this row is now pointing to element of next collection if (!StructuralComparisons.StructuralEqualityComparer.Equals( parentIdentifier(queryContext, dbDataReader), collectionMaterializationContext.ParentIdentifier)) { resultCoordinator.HasNext = true; } return; } var innerKey = selfIdentifier(queryContext, dbDataReader); if (innerKey.Any(e => e == null)) { // No correlated element return; } if (collectionMaterializationContext.SelfIdentifier != null) { if (StructuralComparisons.StructuralEqualityComparer.Equals( innerKey, collectionMaterializationContext.SelfIdentifier)) { // repeated row for current element // If it is pending materialization then it may have nested elements if (collectionMaterializationContext.ResultContext.Values != null) { ProcessCurrentElementRow(); } resultCoordinator.ResultReady = false; return; } // Row for new element which is not first element // So materialize the element GenerateCurrentElementIfPending(); resultCoordinator.HasNext = null; collectionMaterializationContext.UpdateSelfIdentifier(innerKey); } else { // First row for current element collectionMaterializationContext.UpdateSelfIdentifier(innerKey); } ProcessCurrentElementRow(); resultCoordinator.ResultReady = false; void ProcessCurrentElementRow() { var previousResultReady = resultCoordinator.ResultReady; resultCoordinator.ResultReady = true; var element = innerShaper( queryContext, dbDataReader, collectionMaterializationContext.ResultContext, resultCoordinator); if (resultCoordinator.ResultReady) { // related element is materialized collectionMaterializationContext.ResultContext.Values = null; ((TCollection)collectionMaterializationContext.Collection).Add(element); } resultCoordinator.ResultReady &= previousResultReady; } void GenerateCurrentElementIfPending() { if (collectionMaterializationContext.ResultContext.Values != null) { resultCoordinator.HasNext = false; ProcessCurrentElementRow(); } collectionMaterializationContext.UpdateSelfIdentifier(null); } }
private static void IncludeCollection <TEntity, TIncludedEntity>( QueryContext queryContext, DbDataReader dbDataReader, TEntity entity, Func <QueryContext, DbDataReader, object[]> outerKeySelector, Func <QueryContext, DbDataReader, object[]> innerKeySelector, Func <QueryContext, DbDataReader, ResultCoordinator, TIncludedEntity> innerShaper, INavigation navigation, INavigation inverseNavigation, Action <TEntity, TIncludedEntity> fixup, bool trackingQuery, ResultCoordinator resultCoordinator) { if (entity is null) { return; } if (trackingQuery) { queryContext.StateManager.TryGetEntry(entity).SetIsLoaded(navigation); } else { SetIsLoadedNoTracking(entity, navigation); } var innerKey = innerKeySelector(queryContext, dbDataReader); var outerKey = outerKeySelector(queryContext, dbDataReader); var relatedEntity = innerShaper(queryContext, dbDataReader, resultCoordinator); if (ReferenceEquals(relatedEntity, null)) { navigation.GetCollectionAccessor().GetOrCreate(entity); return; } if (!trackingQuery) { fixup(entity, relatedEntity); if (inverseNavigation != null && !inverseNavigation.IsCollection()) { SetIsLoadedNoTracking(relatedEntity, inverseNavigation); } } var hasNext = resultCoordinator.HasNext ?? dbDataReader.Read(); while (hasNext) { resultCoordinator.HasNext = null; var currentOuterKey = outerKeySelector(queryContext, dbDataReader); if (!StructuralComparisons.StructuralEqualityComparer.Equals(outerKey, currentOuterKey)) { resultCoordinator.HasNext = true; break; } var currentInnerKey = innerKeySelector(queryContext, dbDataReader); if (StructuralComparisons.StructuralEqualityComparer.Equals(innerKey, currentInnerKey)) { continue; } relatedEntity = innerShaper(queryContext, dbDataReader, resultCoordinator); if (!trackingQuery) { fixup(entity, relatedEntity); if (inverseNavigation != null && !inverseNavigation.IsCollection()) { SetIsLoadedNoTracking(relatedEntity, inverseNavigation); } } hasNext = resultCoordinator.HasNext ?? dbDataReader.Read(); } resultCoordinator.HasNext = hasNext; }