protected override IEnumerable <Entity> ExecuteInternal(IDictionary <string, DataSource> dataSources, IQueryExecutionOptions options, IDictionary <string, Type> parameterTypes, IDictionary <string, object> parameterValues) { var leftSchema = LeftSource.GetSchema(dataSources, parameterTypes); var innerParameterTypes = GetInnerParameterTypes(leftSchema, parameterTypes); if (OuterReferences != null) { if (parameterTypes == null) { innerParameterTypes = new Dictionary <string, Type>(); } else { innerParameterTypes = new Dictionary <string, Type>(parameterTypes); } foreach (var kvp in OuterReferences) { innerParameterTypes[kvp.Value] = leftSchema.Schema[kvp.Key]; } } var rightSchema = RightSource.GetSchema(dataSources, innerParameterTypes); var mergedSchema = GetSchema(dataSources, parameterTypes, true); var joinCondition = JoinCondition?.Compile(mergedSchema, parameterTypes); foreach (var left in LeftSource.Execute(dataSources, options, parameterTypes, parameterValues)) { var innerParameters = parameterValues; if (OuterReferences != null) { if (parameterValues == null) { innerParameters = new Dictionary <string, object>(); } else { innerParameters = new Dictionary <string, object>(parameterValues); } foreach (var kvp in OuterReferences) { left.Attributes.TryGetValue(kvp.Key, out var outerValue); innerParameters[kvp.Value] = outerValue; } } var hasRight = false; foreach (var right in RightSource.Execute(dataSources, options, innerParameterTypes, innerParameters)) { var merged = Merge(left, leftSchema, right, rightSchema); if (joinCondition == null || joinCondition(merged, parameterValues, options)) { yield return(merged); } hasRight = true; if (SemiJoin) { break; } } if (!hasRight && JoinType == QualifiedJoinType.LeftOuter) { yield return(Merge(left, leftSchema, null, rightSchema)); } } }