private static MethodCallExpression GetJoin(Expression outerSource, Expression innerSource, IEdmNavigationProperty navigationProperty)
        {
            Type innerType = OeExpressionHelper.GetCollectionItemType(innerSource.Type);

            var joinBuilder = new OeJoinBuilder(new OeQueryNodeVisitor(Expression.Parameter(typeof(Object))));

            (LambdaExpression outerKeySelector, LambdaExpression innerKeySelector) =
                joinBuilder.GetJoinKeySelector(outerSource.Type, innerType, Array.Empty <IEdmNavigationProperty>(), navigationProperty);

            var        replaceParameterVisitor = new ReplaceParameterVisitor(outerSource, outerKeySelector.Parameters[0]);
            Expression outerKeyExpression      = replaceParameterVisitor.Visit(outerKeySelector.Body);
            IReadOnlyList <MemberExpression> outerKeyProperties;
            IReadOnlyList <MemberExpression> innerKeyProperties;

            if (OeExpressionHelper.IsTupleType(outerKeySelector.ReturnType))
            {
                outerKeyProperties = OeExpressionHelper.GetPropertyExpressions(outerKeyExpression);
                innerKeyProperties = OeExpressionHelper.GetPropertyExpressions(innerKeySelector.Body);
            }
            else
            {
                outerKeyProperties = new MemberExpression[] { (MemberExpression)outerKeyExpression };
                innerKeyProperties = new MemberExpression[] { (MemberExpression)innerKeySelector.Body };
            }

            BinaryExpression?joinExpression = null;

            for (int i = 0; i < outerKeyProperties.Count; i++)
            {
                if (joinExpression == null)
                {
                    joinExpression = Expression.MakeBinary(ExpressionType.Equal, outerKeyProperties[i], innerKeyProperties[i]);
                }
                else
                {
                    BinaryExpression equal = Expression.MakeBinary(ExpressionType.Equal, outerKeyProperties[i], innerKeyProperties[i]);
                    joinExpression = Expression.MakeBinary(ExpressionType.AndAlso, joinExpression, equal);
                }
            }

            MethodInfo       firstMethodInfo = OeMethodInfoHelper.GetFirstMethodInfo(innerType);
            LambdaExpression joinLambda      = Expression.Lambda(joinExpression !, innerKeySelector.Parameters);

            return(Expression.Call(firstMethodInfo, innerSource, joinLambda));
        }