/// <summary> /// Creates a group join include used to describe an Include operation that should /// be performed as part of a GroupJoin. /// </summary> /// <param name="navigationPath"> The included navigation path. </param> /// <param name="querySourceRequiresTracking"> true if this query source requires tracking. </param> /// <param name="existingGroupJoinInclude"> A possibly null existing group join include. </param> /// <param name="relatedEntitiesLoaders"> The related entities loaders. </param> /// <returns> /// A new group join include. /// </returns> public virtual object CreateGroupJoinInclude( IReadOnlyList <INavigation> navigationPath, bool querySourceRequiresTracking, object existingGroupJoinInclude, object relatedEntitiesLoaders) { var previousGroupJoinInclude = new GroupJoinInclude( navigationPath, (IReadOnlyList <Func <QueryContext, IRelatedEntitiesLoader> >)relatedEntitiesLoaders, querySourceRequiresTracking); var groupJoinInclude = existingGroupJoinInclude as GroupJoinInclude; if (groupJoinInclude != null) { groupJoinInclude.SetPrevious(previousGroupJoinInclude); return(null); } return(previousGroupJoinInclude); }
// ReSharper disable once InconsistentNaming private static IEnumerable <TResult> _GroupJoin <TOuter, TInner, TKey, TResult>( RelationalQueryContext queryContext, IEnumerable <ValueBuffer> source, IShaper <TOuter> outerShaper, IShaper <TInner> innerShaper, Func <TInner, TKey> innerKeySelector, Func <TOuter, IEnumerable <TInner>, TResult> resultSelector, GroupJoinInclude outerGroupJoinInclude, GroupJoinInclude innerGroupJoinInclude) { var outerGroupJoinIncludeContext = outerGroupJoinInclude?.CreateIncludeContext(queryContext); var innerGroupJoinIncludeContext = innerGroupJoinInclude?.CreateIncludeContext(queryContext); try { using (var sourceEnumerator = source.GetEnumerator()) { var comparer = EqualityComparer <TKey> .Default; var hasNext = sourceEnumerator.MoveNext(); var nextOuter = default(TOuter); while (hasNext) { var outer = Equals(nextOuter, default(TOuter)) ? outerShaper.Shape(queryContext, sourceEnumerator.Current) : nextOuter; nextOuter = default(TOuter); outerGroupJoinIncludeContext?.Include(outer); var inner = innerShaper.Shape(queryContext, sourceEnumerator.Current); var inners = new List <TInner>(); if (inner == null) { yield return(resultSelector(outer, inners)); hasNext = sourceEnumerator.MoveNext(); } else { var currentGroupKey = innerKeySelector(inner); innerGroupJoinIncludeContext?.Include(inner); inners.Add(inner); while (true) { hasNext = sourceEnumerator.MoveNext(); if (!hasNext) { break; } nextOuter = outerShaper.Shape(queryContext, sourceEnumerator.Current); if (!Equals(outer, nextOuter)) { break; } nextOuter = default(TOuter); inner = innerShaper.Shape(queryContext, sourceEnumerator.Current); if (inner == null) { break; } var innerKey = innerKeySelector(inner); if (!comparer.Equals(currentGroupKey, innerKey)) { break; } innerGroupJoinIncludeContext?.Include(inner); inners.Add(inner); } yield return(resultSelector(outer, inners)); } } } } finally { innerGroupJoinIncludeContext?.Dispose(); outerGroupJoinIncludeContext?.Dispose(); } }