// ReSharper disable once InconsistentNaming
 private static IAsyncEnumerable <TElement> _InjectParameters <TElement>(
     QueryContext queryContext,
     IAsyncEnumerable <TElement> source,
     string[] parameterNames,
     object[] parameterValues)
 => new ParameterInjector <TElement>(queryContext, source, parameterNames, parameterValues);
            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);
                }
            }
示例#3
0
 private static IRelatedEntitiesLoader _CreateCollectionRelatedEntitiesLoader(
     QueryContext queryContext,
     ShaperCommandContext shaperCommandContext,
     int queryIndex,
     Func <ValueBuffer, object> materializer)
 => new CollectionRelatedEntitiesLoader(queryContext, shaperCommandContext, queryIndex, materializer);
            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);
                }
            }