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);
                }
            }
예제 #5
0
            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;
            }