Exemplo n.º 1
0
        /// <summary>
        /// Correlates the elements of two queries based on matching keys.
        /// </summary>
        /// <typeparam name="TOuter">The type of ParseObjects of the first query.</typeparam>
        /// <typeparam name="TInner">The type of ParseObjects of the second query.</typeparam>
        /// <typeparam name="TKey">The type of the keys returned by the key selector
        /// functions.</typeparam>
        /// <typeparam name="TResult">The type of the result. This must match either
        /// TOuter or TInner</typeparam>
        /// <param name="outer">The first query to join.</param>
        /// <param name="inner">The query to join to the first query.</param>
        /// <param name="outerKeySelector">A function to extract a join key from the results of
        /// the first query.</param>
        /// <param name="innerKeySelector">A function to extract a join key from the results of
        /// the second query.</param>
        /// <param name="resultSelector">A function to select either the outer or inner query
        /// result to determine which query is the base query.</param>
        /// <returns>A new ParseQuery with a WhereMatchesQuery or WhereMatchesKeyInQuery
        /// clause based upon the query indicated in the <paramref name="resultSelector"/>.</returns>
        public static ParseQuery <TResult> Join <TOuter, TInner, TKey, TResult>(
            this ParseQuery <TOuter> outer,
            ParseQuery <TInner> inner,
            Expression <Func <TOuter, TKey> > outerKeySelector,
            Expression <Func <TInner, TKey> > innerKeySelector,
            Expression <Func <TOuter, TInner, TResult> > resultSelector)
            where TOuter : ParseObject
            where TInner : ParseObject
            where TResult : ParseObject
        {
            // resultSelector must select either the inner object or the outer object. If it's the inner
            // object, reverse the query.
            if (resultSelector.Body == resultSelector.Parameters[1])
            {
                // The inner object was selected.
                return(inner.Join <TInner, TOuter, TKey, TInner>(
                           outer,
                           innerKeySelector,
                           outerKeySelector,
                           (i, o) => i) as ParseQuery <TResult>);
            }
            if (resultSelector.Body != resultSelector.Parameters[0])
            {
                throw new InvalidOperationException("Joins must select either the outer or inner object.");
            }

            // Normalize both selectors
            Expression           outerNormalized = new ObjectNormalizer().Visit(outerKeySelector.Body);
            Expression           innerNormalized = new ObjectNormalizer().Visit(innerKeySelector.Body);
            MethodCallExpression outerAsGet      = outerNormalized as MethodCallExpression;
            MethodCallExpression innerAsGet      = innerNormalized as MethodCallExpression;

            if (IsParseObjectGet(outerAsGet) && outerAsGet.Object == outerKeySelector.Parameters[0])
            {
                var outerKey = GetValue(outerAsGet.Arguments[0]) as string;

                if (IsParseObjectGet(innerAsGet) && innerAsGet.Object == innerKeySelector.Parameters[0])
                {
                    // Both are key accesses, so treat this as a WhereMatchesKeyInQuery
                    var innerKey = GetValue(innerAsGet.Arguments[0]) as string;
                    return(outer.WhereMatchesKeyInQuery(outerKey, innerKey, inner) as ParseQuery <TResult>);
                }

                if (innerKeySelector.Body == innerKeySelector.Parameters[0])
                {
                    // The inner selector is on the result of the query itself, so treat this as a
                    // WhereMatchesQuery
                    return(outer.WhereMatchesQuery(outerKey, inner) as ParseQuery <TResult>);
                }
                throw new InvalidOperationException(
                          "The key for the joined object must be a ParseObject or a field access " +
                          "on the ParseObject.");
            }

            // TODO (hallucinogen): If we ever support "and" queries fully and/or support a "where this object
            // matches some key in some other query" (as opposed to requiring a key on this query), we
            // can add support for even more types of joins.

            throw new InvalidOperationException(
                      "The key for the selected object must be a field access on the ParseObject.");
        }