ResourceBinder.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / ndp / fx / src / DataWeb / Client / System / Data / Services / Client / ALinq / ResourceBinder.cs / 4 / ResourceBinder.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Expression visitor that binds expression tree to resources
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services.Client
{
    #region Namespaces. 

    using System; 
    using System.Collections.Generic; 
    using System.Data.Services.Common;
    using System.Diagnostics; 
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
 
    #endregion Namespaces.
 
    ///  
    /// This class provides a Bind method that analyzes an input  and returns a bound tree.
    ///  
    internal class ResourceBinder : DataServiceExpressionVisitor
    {
        /// 
        /// Sets of  that require a key predicate that hasn't been satisfied yet. 
        /// 
        private readonly HashSet setsRequiringKeyPredicate = new HashSet(EqualityComparer.Default); 
 
        /// Analyzes and binds the specified expression.
        /// Expression to bind. 
        /// The expression with bound nodes.
        internal static Expression Bind(Expression e)
        {
            ResourceBinder rb = new ResourceBinder(); 
            Expression boundExpression = rb.Visit(e);
 
            // Post Process bound tree 
            // check to see if path segment ends in multi-valued property with no key predicate
            ResourceSetExpression rse = boundExpression as ResourceSetExpression; 
            if (rse != null)
            {
                rse.IsLeafResource = true;
            } 

            // If there are missing key predicates, only throw if translation was successful and the missing 
            // key predicate is the only error. Otherwise, allow the expression that could not be translated 
            // to propagate to the UriWriter which will throw to indicate that it is not supported.
            if ((boundExpression is ResourceExpression) && 
                rb.setsRequiringKeyPredicate.Count > 0)
            {
                throw new NotSupportedException(Strings.ALinq_CantNavigateWithoutKeyPredicate);
            } 

            return boundExpression; 
        } 

        private Expression AnalyzePredicate(MethodCallExpression mce) 
        {
            // Validate that the input is a resource set and retrieve the Lambda that defines the predicate
            ResourceSetExpression input;
            LambdaExpression le; 
            if (!TryGetResourceSetMethodArguments(mce, out input, out le))
            { 
                // might have failed because of singleton, so throw better error if so. 
                ValidationRules.RequireNonSingleton(mce.Arguments[0]);
                return mce; 
            }

            //
            // Valid predicate patterns are as follows: 
            // 1. A URI-compatible filter applied to the input resource set
            // 2. A key-predicate filter applied to the input resource set 
            // - Additionally, key-predicate filters may be applied to any resource path component 
            //   that does not already have a key-predicate filter, regardless of any filter applied
            //   to the current input resource set, if transparent scopes are present. 
            // - It is not valid to apply a key-predicate or a filter to a resource set
            //   for which key-predicate is already present.
            // - It is valid to apply a filter to a resource set for which a filter already exists;
            //   such filters are AND'd together. 
            //
            // [Key-predicate that targets a path component AND]* [Key-predicate over input | URI-compatible filter over input]+ 
 
            List conjuncts = new List();
            AddConjuncts(le.Body, conjuncts); 

            Dictionary> predicatesByTarget = new Dictionary>();
            List referencedInputs = new List();
            foreach (Expression e in conjuncts) 
            {
                Expression reboundPredicate = InputBinder.Bind(e, input, le.Parameters[0], referencedInputs); 
                if (referencedInputs.Count > 1) 
                {
                    // UNSUPPORTED: A single clause cannot refer to more than one resource set 
                    return mce;
                }

                ResourceSetExpression boundTarget = (referencedInputs.Count == 0 ? input : referencedInputs[0] as ResourceSetExpression); 
                if (boundTarget == null)
                { 
                    // UNSUPPORTED: Each clause must refer to a path component that is a resource set, not a singleton navigation property 
                    return mce;
                } 

                List targetPredicates = null;
                if (!predicatesByTarget.TryGetValue(boundTarget, out targetPredicates))
                { 
                    targetPredicates = new List();
                    predicatesByTarget[boundTarget] = targetPredicates; 
                } 

                targetPredicates.Add(reboundPredicate); 
                referencedInputs.Clear();
            }

            conjuncts = null; 
            List inputPredicates;
            if (predicatesByTarget.TryGetValue(input, out inputPredicates)) 
            { 
                predicatesByTarget.Remove(input);
            } 
            else
            {
                inputPredicates = null;
            } 

            foreach (KeyValuePair> predicates in predicatesByTarget) 
            { 
                ResourceSetExpression target = predicates.Key;
                List clauses = predicates.Value; 

                Dictionary keyValues;
                if (!ExtractKeyPredicate(target, clauses, out keyValues) ||
                    clauses.Count > 0) 
                {
                    // UNSUPPORTED: Only key predicates may be applied to earlier path components 
                    return mce; 
                }
 
                // Earlier path components must be navigation sources, and navigation sources cannot have query options.
                Debug.Assert(!target.HasQueryOptions, "Navigation source had query options?");

                SetKeyPredicate(target, keyValues); 
            }
 
            if (inputPredicates != null) 
            {
                Dictionary inputKeyValues; 
                if (ExtractKeyPredicate(input, inputPredicates, out inputKeyValues))
                {
                    if (input.HasSequenceQueryOptions)
                    { 
                        // SQLBUDT 616297: A key predicate cannot be applied if query options other than 'Expand' are present,
                        // so merge the key predicate into the filter query option instead. 
                        inputPredicates.Add(BuildKeyPredicateFilter(input.CreateReference(), inputKeyValues)); 
                    }
                    else 
                    {
                        SetKeyPredicate(input, inputKeyValues);
                    }
                } 

                if (inputPredicates.Count > 0) 
                { 
                    if (input.KeyPredicate != null)
                    { 
                        // SQLBUDT 616297: a filter query option cannot be specified if a key predicate is present,
                        // so remove the existing key predicate and add it to inputPredicates as a filter. Note that
                        // this can only be done on the current resource set, where the filter query option is being
                        // applied, since earlier sets in the resource path are navigations that require a singleton, 
                        // enforced by the presence of a key predicate.
                        inputPredicates.Add(BuildKeyPredicateFilter(input.CreateReference(), input.KeyPredicate)); 
                        input.KeyPredicate = null; 
                    }
 
                    int start;
                    Expression newFilter;
                    if (input.Filter != null)
                    { 
                        start = 0;
                        newFilter = input.Filter.Predicate; 
                    } 
                    else
                    { 
                        start = 1;
                        newFilter = inputPredicates[0];
                    }
 
                    for (int idx = start; idx < inputPredicates.Count; idx++)
                    { 
                        newFilter = Expression.And(newFilter, inputPredicates[idx]); 
                    }
 
                    AddSequenceQueryOption(input, new FilterQueryOptionExpression(mce.Method.ReturnType, newFilter));
                }
            }
 
            return input; // No need to adjust this.currentResource - filters are merged in all cases
        } 
 
        private void SetKeyPredicate(ResourceSetExpression rse, Dictionary keyValues)
        { 
            Debug.Assert(rse != null, "rse != null");
            Debug.Assert(keyValues != null, "keyValues != null");

            if (rse.KeyPredicate == null) 
            {
                rse.KeyPredicate = new Dictionary(); 
            } 

            foreach(var g in keyValues) 
            {
                if (rse.KeyPredicate.Keys.Contains(g.Key))
                {
                    //UNSUPPORTED:  =  AND  =  are multiple key predicates and 
                    //cannot be represented as a resource path.
                    throw Error.NotSupported(Strings.ALinq_CanOnlyApplyOneKeyPredicate); 
                } 

                rse.KeyPredicate.Add(g.Key, g.Value); 
            }

            this.setsRequiringKeyPredicate.Remove(rse);
        } 

        private static bool ExtractKeyPredicate(ResourceSetExpression target, List predicates, out Dictionary keyValues) 
        { 
            keyValues = null;
            List nonKeyPredicates = null; 

            foreach (Expression predicate in predicates)
            {
                PropertyInfo property; 
                ConstantExpression constantValue;
                if (PatternRules.MatchKeyComparison(predicate, out property, out constantValue)) 
                { 
                    if (keyValues == null)
                    { 
                        keyValues = new Dictionary();
                    }
                    else if (keyValues.ContainsKey(property))
                    { 
                        // UNSUPPORTED:  =  AND  =  are multiple key predicates and
                        // cannot be represented as a resource path. 
                        throw Error.NotSupported(Strings.ALinq_CanOnlyApplyOneKeyPredicate); 
                    }
 
                    keyValues.Add(property, constantValue);
                }
                else
                { 
                    if (nonKeyPredicates == null)
                    { 
                        nonKeyPredicates = new List(); 
                    }
                    nonKeyPredicates.Add(predicate); 
                }
            }

            Debug.Assert(keyValues != null || nonKeyPredicates != null, "No key predicates or non-key predicates found?"); 

            if (keyValues != null && 
                !PatternRules.GetKeyProperties(target.CreateReference().Type).SequenceEqual(keyValues.Keys)) 
            {
                keyValues = null; 
                return false;
            }

            // If keyValues is non-null then at least one expression from predicates was a key comparison 
            // and should no longer be present in the predicates list since it is now in the key values dictionary.
            if (keyValues != null) 
            { 
                // Remove all predicates.
                predicates.Clear(); 

                // If any non-key predicates were found, add them back
                if (nonKeyPredicates != null)
                { 
                    predicates.AddRange(nonKeyPredicates);
                } 
            } 

            return (keyValues != null); 
        }

        private static Expression BuildKeyPredicateFilter(InputReferenceExpression input, Dictionary keyValuesDictionary)
        { 
            Debug.Assert(keyValuesDictionary.Count > 0, "At least one key property is required in a key predicate");
 
            Expression retExpr = null; 
            foreach (KeyValuePair keyValue in keyValuesDictionary)
            { 
                Expression clause = Expression.Equal(Expression.Property(input, keyValue.Key), keyValue.Value);
                if (retExpr == null)
                {
                    retExpr = clause; 
                }
                else 
                { 
                    retExpr = Expression.And(retExpr, clause);
                } 
            }

            return retExpr;
        } 

        private static void AddConjuncts(Expression e, List conjuncts) 
        { 
            if (PatternRules.MatchAnd(e))
            { 
                BinaryExpression be = (BinaryExpression)e;
                AddConjuncts(be.Left, conjuncts);
                AddConjuncts(be.Right, conjuncts);
            } 
            else
            { 
                conjuncts.Add(e); 
            }
        } 

        internal Expression AnalyzeProjection(MethodCallExpression mce)
        {
            Expression input = mce.Arguments[0]; 
            LambdaExpression le;
            if(!PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out le)) 
            { 
                return mce;
            } 
            else if (PatternRules.MatchIdentitySelector(le))
            {
                return input;
            } 
            else if (PatternRules.MatchTransparentIdentitySelector(input, le))
            { 
                return RemoveTransparentScope(mce.Method.ReturnType, (ResourceSetExpression)input); 
            }
 
            ResourceExpression navSource;
            Expression boundProjection;
            if (IsValidNavigationSource(input, out navSource) &&
                TryBindToInput(navSource, le, out boundProjection) && 
                PatternRules.MatchPropertyProjectionSingleton(navSource, boundProjection))
            { 
                return CreateNavigationPropertySingletonExpression(mce.Method.ReturnType, navSource, boundProjection); 
            }
 
            return mce;
        }

        private bool IsValidNavigationSource(Expression input, out ResourceExpression sourceExpression) 
        {
            ValidationRules.RequireCanNavigate(input); 
 
            sourceExpression = input as ResourceExpression;
            if (sourceExpression != null && !sourceExpression.IsSingleton) 
            {
                ResourceSetExpression rse = (ResourceSetExpression)sourceExpression;
                this.setsRequiringKeyPredicate.Add(rse);
            } 

            return (sourceExpression != null); 
        } 

        internal Expression AnalyzeSelectMany(MethodCallExpression mce) 
        {
            if (mce.Arguments.Count != 2 && mce.Arguments.Count != 3)
            {
                return mce; 
            }
 
            ResourceExpression input; 
            if (!IsValidNavigationSource(mce.Arguments[0], out input))
            { 
                return mce;
            }

            LambdaExpression le; 
            if(!PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out le))
            { 
                return mce; 
            }
 
            List referencedInputs = new List();
            Expression navPropRef = InputBinder.Bind(le.Body, input, le.Parameters[0], referencedInputs);
            Type resourceType = TypeSystem.GetElementType(navPropRef.Type);
 
            // could be a Cast sequence operator here that can be optimized out.
            SequenceMethod sm; 
            MethodCallExpression mce2 = navPropRef as MethodCallExpression; 
            while (mce2 != null && ReflectionUtil.TryIdentifySequenceMethod(mce2.Method, out sm) && sm == SequenceMethod.Cast)
            { 
                navPropRef = mce2.Arguments[0];
                mce2 = navPropRef as MethodCallExpression;
            }
 
            ResourceSetExpression rse = null;
            if (PatternRules.MatchPropertyProjectionSet(input, navPropRef)) 
            { 
                rse = CreateResourceSetExpression(mce.Method.ReturnType, input, navPropRef, resourceType);
            } 
            else
            {
                return mce;
            } 

            if (mce.Arguments.Count == 3) 
            { 
                LambdaExpression selector = StripQuotes(mce.Arguments[2]) as LambdaExpression;
 
                // now examine selector
                // Check for transparent scope result - projects the input and the selector
                ResourceSetExpression.TransparentAccessors transparentScope;
                if (PatternRules.MatchTransparentScopeSelector(selector, out transparentScope)) 
                {
                    rse.TransparentScope = transparentScope; 
                } 
                else if (!PatternRules.MatchIdentityProjectionResultSelector(selector))
                { 
                    return mce;
                }
            }
 
            return rse;
        } 
 
        internal static Expression ApplyOrdering(MethodCallExpression mce, ResourceSetExpression input, Expression selector, bool descending, bool thenBy)
        { 
            List selectors;
            if (!thenBy)
            {
                selectors = new List(); 
                AddSequenceQueryOption(input, new OrderByQueryOptionExpression(mce.Type, selectors));
            } 
            else 
            {
                Debug.Assert(input.OrderBy != null); 
                selectors = input.OrderBy.Selectors;
            }

            selectors.Add(new OrderByQueryOptionExpression.Selector(selector, descending)); 

            return input; 
        } 

#if !ASTORIA_LIGHT      // Silverlight doesn't support synchronous operators. 

        /// Ensures that there's a limit on the cardinality of a query.
        ///  for the method to limit First/Single(OrDefault).
        /// Maximum cardinality to allow. 
        /// 
        /// An expression that limits  to no more than  elements. 
        ///  
        /// This method is used by .First(OrDefault) and .Single(OrDefault) to limit cardinality.
        private static Expression LimitCardinality(MethodCallExpression mce, int maxCardinality) 
        {
            Debug.Assert(mce != null, "mce != null");
            Debug.Assert(maxCardinality > 0, "Cardinality must be at least 1");
 
            if (mce.Arguments.Count != 1)
            { 
                // Don't support methods with predicates. 
                return mce;
            } 

            ResourceSetExpression rse = mce.Arguments[0] as ResourceSetExpression;
            if (rse != null)
            { 
                if (!rse.HasKeyPredicate && // no-op, already returns a singleton
                    (ResourceExpressionType)rse.NodeType != ResourceExpressionType.ResourceNavigationProperty) 
                { 
                    if (rse.Take == null || (int)rse.Take.TakeAmount.Value > maxCardinality)
                    { 
                        AddSequenceQueryOption(rse, new TakeQueryOptionExpression(mce.Type, Expression.Constant(maxCardinality)));
                    }
                }
                return mce.Arguments[0]; 
            }
            else if (mce.Arguments[0] is NavigationPropertySingletonExpression) 
            { 
                // no-op, already returns a singleton
                return mce.Arguments[0]; 
            }

            return mce;
        } 

#endif 
 
        private static Expression AnalyzeCast(MethodCallExpression mce)
        { 
            ResourceExpression rse = mce.Arguments[0] as ResourceExpression;
            if (rse != null)
            {
                return rse.Cast(mce.Method.ReturnType); 
            }
 
            return mce; 
        }
 
        private static Expression StripConvert(Expression e)
        {
            UnaryExpression ue = e as UnaryExpression;
 
            if (ue != null &&
                ue.NodeType == ExpressionType.Convert && 
                ue.Type.IsGenericType && 
                ue.Type.GetGenericTypeDefinition() == typeof(DataServiceQuery<>))
            { 
                e = ue.Operand;
                ResourceExpression re = e as ResourceExpression;
                if (re != null)
                { 
                    e = re.Cast(ue.Type);
                } 
            } 

            return e; 
        }

        private static Expression AnalyzeExpand(MethodCallExpression mce)
        { 
            Expression obj = StripConvert(mce.Object);
            ResourceExpression re = obj as ResourceExpression; 
            if (re == null) 
            {
                return mce; 
            }

            ValidationRules.RequireCanExpand(re);
            ConstantExpression ce = mce.Arguments[0] as ConstantExpression; 
            re.ExpandPaths.Add(ce);
 
            return re; 
        }
 
        private static Expression AnalyzeAddCustomQueryOption(MethodCallExpression mce)
        {
            Expression obj = StripConvert(mce.Object);
            ResourceExpression re = obj as ResourceExpression; 
            if (re == null)
            { 
                return mce; 
            }
 
            ValidationRules.RequireCanAddCustomQueryOption(re);
            ValidationRules.RequireLegalCustomQueryOption(mce.Arguments[0], re);

            ConstantExpression name = mce.Arguments[0] as ConstantExpression; 
            ConstantExpression value = mce.Arguments[1] as ConstantExpression;
 
            re.CustomQueryOptions.Add(name, value); 

            return re; 
        }

        private static ResourceSetExpression CreateResourceSetExpression(Type type, ResourceExpression source, Expression memberExpression, Type resourceType)
        { 
            ResourceSetExpression newResource = new ResourceSetExpression(type, source, memberExpression, resourceType, source.ExpandPaths.ToList(), source.CustomQueryOptions.ToDictionary(kvp => kvp.Key, kvp => kvp.Value));
            source.ExpandPaths.Clear(); 
            source.CustomQueryOptions.Clear(); 
            return newResource;
        } 

        private static NavigationPropertySingletonExpression CreateNavigationPropertySingletonExpression(Type type, ResourceExpression source, Expression memberExpression)
        {
            NavigationPropertySingletonExpression newResource = new NavigationPropertySingletonExpression(type, source, memberExpression, source.ExpandPaths.ToList(), source.CustomQueryOptions.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)); 
            source.ExpandPaths.Clear();
            source.CustomQueryOptions.Clear(); 
            return newResource; 
        }
 
        /// 
        /// Produces a new  that is a clone of  in all respects,
        /// other than its result type - which will be  - and it's transparent scope,
        /// which will be null. This is a shallow clone operation - sequence query options, key predicate, etc are 
        /// not cloned, but are reassigned to the new instance. The  resource expression should be
        /// discarded after being used with this method. 
        ///  
        /// The result type -  - that the new resource set expression should have.
        /// The resource set expression from which the transparent scope is being removed 
        /// A new resource set expression without an enclosing transparent scope and with the specified result type.
        private static ResourceSetExpression RemoveTransparentScope(Type expectedResultType, ResourceSetExpression input)
        {
            // Create a new resource set expression based on the input 
            ResourceSetExpression newResource = new ResourceSetExpression(expectedResultType, input.Source, input.MemberExpression, input.ResourceType, input.ExpandPaths, input.CustomQueryOptions);
 
            // Reassign state items that are not constructor arguments - query options + key predicate 
            newResource.KeyPredicate = input.KeyPredicate;
            foreach (QueryOptionExpression queryOption in input.SequenceQueryOptions) 
            {
                newResource.AddSequenceQueryOption(queryOption);
            }
 
            // Instruct the new resource set expression to use input's Input Reference instead of creating its own.
            // This will also update the Input Reference to specify the new resource set expression as it's target, 
            // so that any usage of it in query option expressions is consistent with the new resource set expression. 
            newResource.OverrideInputReference(input);
 
            return newResource;
        }

        private static Expression StripQuotes(Expression e) 
        {
            return e.NodeType == ExpressionType.Quote ? ((UnaryExpression)e).Operand : e; 
        } 

        internal override Expression VisitResourceSetExpression(ResourceSetExpression rse) 
        {
            if ((ResourceExpressionType)rse.NodeType == ResourceExpressionType.RootResourceSet)
            {
                Debug.Assert(!rse.HasQueryOptions); 
                // since we could be adding query options to the root, create a new one which can be mutable.
                return new ResourceSetExpression(rse.Type, rse.Source, rse.MemberExpression, rse.ResourceType, null, null); 
            } 

            return rse; 
        }

        private static bool TryGetResourceSetMethodArguments(MethodCallExpression mce, out ResourceSetExpression input, out LambdaExpression lambda)
        { 
            input = null;
            lambda = null; 
 
            input = mce.Arguments[0] as ResourceSetExpression;
            if (input != null && 
                PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out lambda))
            {
                return true;
            } 

            return false; 
        } 

        private static bool TryBindToInput(ResourceExpression input, LambdaExpression le, out Expression bound) 
        {
            List referencedInputs = new List();
            bound = InputBinder.Bind(le.Body, input, le.Parameters[0], referencedInputs);
            if (referencedInputs.Count > 1 || (referencedInputs.Count == 1 && referencedInputs[0] != input)) 
            {
                bound = null; 
            } 

            return (bound != null); 
        }

        private static Expression AnalyzeResourceSetMethod(MethodCallExpression mce, Func sequenceMethodAnalyzer)
        { 
            ResourceSetExpression input;
            LambdaExpression le; 
            if (!TryGetResourceSetMethodArguments(mce, out input, out le)) 
            {
                // UNSUPPORTED: Expected LambdaExpression as second argument to sequence method 
                return mce;
            }

            Expression lambdaBody; 
            if (!TryBindToInput(input, le, out lambdaBody))
            { 
                // UNSUPPORTED: Lambda should reference the input, and only the input 
                return mce;
            } 

            return sequenceMethodAnalyzer(mce, input, lambdaBody);
        }
 
        private static Expression AnalyzeResourceSetConstantMethod(MethodCallExpression mce, Func constantMethodAnalyzer)
        { 
            ResourceExpression input = (ResourceExpression)mce.Arguments[0]; 
            ConstantExpression constantArg = mce.Arguments[1] as ConstantExpression;
            if (null == constantArg) 
            {
                // UNSUPPORTED: A ConstantExpression is expected
                return mce;
            } 

            return constantMethodAnalyzer(mce, input, constantArg); 
        } 

        private static void AddSequenceQueryOption(ResourceExpression target, QueryOptionExpression qoe) 
        {
            ValidationRules.RequireNonSingleton(target);
            ResourceSetExpression rse = (ResourceSetExpression)target;
 
            // Validation that can add option
            switch (qoe.NodeType) 
            { 
                case (ExpressionType)ResourceExpressionType.FilterQueryOption:
                    if (rse.Skip != null) 
                    {
                        throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("filter", "skip"));
                    }
                    else if (rse.Take != null) 
                    {
                        throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("filter", "top")); 
                    } 
                    break;
 
                case (ExpressionType)ResourceExpressionType.OrderByQueryOption:
                    if (rse.Skip != null)
                    {
                        throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("orderby", "skip")); 
                    }
                    else if (rse.Take != null) 
                    { 
                        throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("orderby", "top"));
                    } 
                    break;
                case (ExpressionType)ResourceExpressionType.SkipQueryOption:
                    if (rse.Take != null)
                    { 
                        throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("skip", "top"));
                    } 
                    break; 
                default:
                    break; 
            }

            rse.AddSequenceQueryOption(qoe);
        } 

        internal override Expression VisitBinary(BinaryExpression b) 
        { 
            Expression e = base.VisitBinary(b);
            if (PatternRules.MatchStringAddition(e)) 
            {
                BinaryExpression be = e as BinaryExpression;
                MethodInfo mi = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string)});
                return Expression.Call(mi, new Expression[] {be.Left, be.Right}); 
            }
 
            return e; 
        }
 
        internal override Expression VisitMemberAccess(MemberExpression m)
        {
            Expression e = base.VisitMemberAccess(m);
            MemberExpression me = e as MemberExpression; 
            PropertyInfo pi;
            MethodInfo mi; 
            if (me != null && 
                PatternRules.MatchNonPrivateReadableProperty(me, out pi) &&
                TypeSystem.TryGetPropertyAsMethod(pi, out mi)) 
            {
                return Expression.Call(me.Expression, mi);
            }
 
            return e;
        } 
 
        internal override Expression VisitMethodCall(MethodCallExpression mce)
        { 
            Expression e = base.VisitMethodCall(mce);
            mce = e as MethodCallExpression;

            if (mce != null) 
            {
                SequenceMethod sequenceMethod; 
                if (ReflectionUtil.TryIdentifySequenceMethod(mce.Method, out sequenceMethod)) 
                {
                    switch (sequenceMethod) 
                    {
                        case SequenceMethod.Where:
                            return AnalyzePredicate(mce);
                        case SequenceMethod.Select: 
                            return AnalyzeProjection(mce);
                        case SequenceMethod.SelectMany: 
                        case SequenceMethod.SelectManyResultSelector: 
                            return AnalyzeSelectMany(mce);
                        case SequenceMethod.Take: 
                            return AnalyzeResourceSetConstantMethod(mce, (callExp, resource, takeCount) => { AddSequenceQueryOption(resource, new TakeQueryOptionExpression(callExp.Type, takeCount)); return resource; });
                        case SequenceMethod.Skip:
                            return AnalyzeResourceSetConstantMethod(mce, (callExp, resource, skipCount) => { AddSequenceQueryOption(resource, new SkipQueryOptionExpression(callExp.Type, skipCount)); return resource; });
                        case SequenceMethod.OrderBy: 
                            return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, /*descending=*/false, /*thenBy=*/false));
                        case SequenceMethod.ThenBy: 
                            return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, /*descending=*/false, /*thenBy=*/true)); 
                        case SequenceMethod.OrderByDescending:
                            return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, /*descending=*/true, /*thenBy=*/false)); 
                        case SequenceMethod.ThenByDescending:
                             return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, /*descending=*/true, /*thenBy=*/true));
#if !ASTORIA_LIGHT      // Silverlight doesn't support synchronous operators.
                        case SequenceMethod.First: 
                        case SequenceMethod.FirstOrDefault:
                            return LimitCardinality(mce, 1); 
                        case SequenceMethod.Single: 
                        case SequenceMethod.SingleOrDefault:
                            return LimitCardinality(mce, 2); 
#endif
                        case SequenceMethod.Cast:
                            return AnalyzeCast(mce);
                        default: 
                            throw Error.MethodNotSupported(mce);
                    } 
                } 
                else if (mce.Method.DeclaringType.IsGenericType &&
                    mce.Method.DeclaringType.GetGenericTypeDefinition() == typeof(DataServiceQuery<>)) 
                {
                    Type t = typeof(DataServiceQuery<>).MakeGenericType(mce.Method.DeclaringType.GetGenericArguments()[0]);

                    if (mce.Method == t.GetMethod("Expand", new Type[] { typeof(string) })) 
                    {
                        return AnalyzeExpand(mce); 
                    } 
                    else if (mce.Method == t.GetMethod("AddQueryOption", new Type[] { typeof(string), typeof(object) }))
                    { 
                        return AnalyzeAddCustomQueryOption(mce);
                    }
                    else
                    { 
                        throw Error.MethodNotSupported(mce);
                    } 
                } 
                return mce;
            } 

            return e;
        }
 
        private static class PatternRules
        { 
            internal static bool MatchPropertyAccess(Expression e, out Expression instance, out List propertyPath) 
            {
                instance = null; 
                propertyPath = null;

                MemberExpression me = e as MemberExpression;
                while (me != null) 
                {
                    PropertyInfo pi; 
                    if (MatchNonPrivateReadableProperty(me, out pi)) 
                    {
                        if (propertyPath == null) 
                        {
                            propertyPath = new List();
                        }
                        propertyPath.Insert(0, pi.Name); 

                        e = me.Expression; 
                        me = e as MemberExpression; 
                    }
                    else 
                    {
                        me = null;
                    }
                } 

                if (propertyPath != null) 
                { 
                    instance = e;
                    return true; 
                }

                return false;
            } 

            // is constant 
            internal static bool MatchConstant(Expression e, out ConstantExpression constExpr) 
            {
                constExpr = e as ConstantExpression; 
                return (constExpr != null);
            }

            internal static bool MatchAnd(Expression e) 
            {
                BinaryExpression be = e as BinaryExpression; 
                return (be != null && (be.NodeType == ExpressionType.And || be.NodeType == ExpressionType.AndAlso)); 
            }
 
            internal static bool MatchNonPrivateReadableProperty(Expression e, out PropertyInfo propInfo)
            {
                MemberExpression me = e as MemberExpression;
                if (me == null) 
                {
                    propInfo = null; 
                    return false; 
                }
                return MatchNonPrivateReadableProperty(me, out propInfo); 
            }

            internal static bool MatchNonPrivateReadableProperty(MemberExpression me, out PropertyInfo propInfo)
            { 
                Debug.Assert(me != null);
 
                propInfo = null; 

                if (me.Member.MemberType == MemberTypes.Property) 
                {
                    PropertyInfo pi = (PropertyInfo)me.Member;
                    if (pi.CanRead && !TypeSystem.IsPrivate(pi))
                    { 
                        propInfo = pi;
                        return true; 
                    } 
                }
                return false; 
            }

            /// 
            /// Checks whether the specified  is a member access to a key. 
            /// 
            /// Expression to check. 
            /// If this is a key access, the property for the key. 
            /// true if  is a member access to a key; false otherwise.
            internal static bool MatchKeyProperty(Expression expression, out PropertyInfo property) 
            {
                property = null;

                // make sure member is property, it is public and has a Getter. 
                PropertyInfo pi;
                if (!PatternRules.MatchNonPrivateReadableProperty(expression, out pi)) 
                { 
                    return false;
                } 

                if (GetKeyProperties(pi.ReflectedType).Contains(pi))
                {
                    property = pi; 
                    return true;
                } 
 
                return false;
            } 

            /// Gets the key properties from the specified .
            /// Type to get properties from.
            /// A list of properties that are key for the type; possibly an empty list. 
            internal static List GetKeyProperties(Type type)
            { 
                Debug.Assert(type != null, "type != null"); 
                ClientType clientType = ClientType.Create(type, false /* expectModelType */);
                var result = new List(); 
                foreach (var property in clientType.Properties)
                {
                    if (property.KeyProperty)
                    { 
                        result.Add(property.DeclaringType.GetProperty(property.PropertyName));
                    } 
                } 

                return result; 
            }

            internal static bool MatchKeyComparison(Expression e, out PropertyInfo keyProperty, out ConstantExpression keyValue)
            { 
                if (PatternRules.MatchBinaryEquality(e))
                { 
                    BinaryExpression be = (BinaryExpression)e; 
                    if ((PatternRules.MatchKeyProperty(be.Left, out keyProperty) && PatternRules.MatchConstant(be.Right, out keyValue)) ||
                        (PatternRules.MatchKeyProperty(be.Right, out keyProperty) && PatternRules.MatchConstant(be.Left, out keyValue))) 
                    {
                        // if property is compared to null, expression is not key predicate comparison
                        return (keyValue.Value != null);
                    } 
                }
 
                keyProperty = null; 
                keyValue = null;
                return false; 
            }

            internal static bool MatchResourceSet(Expression e)
            { 
                return (e is ResourceSetExpression);
            } 
 
            internal static bool MatchResource(Expression e)
            { 
                return (e is ResourceExpression);
            }

            internal static bool MatchSingleArgumentLambda(Expression e, out LambdaExpression lambda) 
            {
                lambda = null; 
 
                LambdaExpression le = StripQuotes(e) as LambdaExpression;
                if (le != null && le.Parameters.Count == 1) 
                {
                    lambda = le;
                }
 
                return (lambda != null);
            } 
 
            internal static bool MatchIdentitySelector(Expression e)
            { 
                LambdaExpression le = (LambdaExpression)e;
                return (le.Body == le.Parameters[0]);
            }
 
            internal static bool MatchTransparentIdentitySelector(Expression input, LambdaExpression selector)
            { 
                if (selector.Parameters.Count != 1) 
                {
                    return false; 
                }

                return MatchTransparentScopeAccess(input, selector.Body, selector.Parameters[0]);
            } 

            private static bool MatchTransparentScopeAccess(Expression input, Expression potentialRef, ParameterExpression expectedTarget) 
            { 
                ResourceSetExpression rse = input as ResourceSetExpression;
                if (rse == null || rse.TransparentScope == null) 
                {
                    return false;
                }
 
                Expression paramRef;
                List refPath; 
                return (MatchPropertyAccess(potentialRef, out paramRef, out refPath) && 
                        paramRef == expectedTarget &&
                        refPath != null && refPath.Count == 1 && 
                        string.Equals(refPath[0], rse.TransparentScope.Accessor, StringComparison.Ordinal));
            }

            internal static bool MatchIdentityProjectionResultSelector(Expression e) 
            {
                LambdaExpression le = (LambdaExpression)e; 
                return (le.Body == le.Parameters[1]); 
            }
 
            internal static bool MatchTransparentScopeSelector(Expression e, out ResourceSetExpression.TransparentAccessors transparentScope)
            {
                transparentScope = null;
 
                LambdaExpression le = (LambdaExpression)e;
                if (le.Body.NodeType == ExpressionType.New) 
                { 
                    NewExpression ne = (NewExpression)le.Body;
                    if (ne.Constructor.GetParameters().Length == 2 && 
                        ne.Arguments[0] == le.Parameters[0] &&
                        ne.Arguments[1] == le.Parameters[1])
                    {
                        PropertyInfo[] publicProps = le.Body.Type.GetProperties(BindingFlags.Instance | BindingFlags.Public); 
                        if (publicProps.Length == 2)
                        { 
                            if (le.Body.Type.BaseType.Equals(typeof(object)) && 
                                   publicProps[0].Name.Equals(le.Parameters[0].Name, StringComparison.Ordinal) &&
                                   publicProps[1].Name.Equals(le.Parameters[1].Name, StringComparison.Ordinal)) 
                            {
                                transparentScope =
                                    new ResourceSetExpression.TransparentAccessors(
                                        publicProps[1].Name, 
                                        publicProps[0].Name
                                    ); 
                                return true; 
                            }
                        } 
                    }
                }

                return false; 
            }
 
            internal static bool MatchPropertyProjectionSet(ResourceExpression input, Expression potentialPropertyRef) 
            {
                return MatchNavigationPropertyProjection(input, potentialPropertyRef, true); 
            }

            internal static bool MatchPropertyProjectionSingleton(ResourceExpression input, Expression potentialPropertyRef)
            { 
                return MatchNavigationPropertyProjection(input, potentialPropertyRef, false);
            } 
 
            private static bool MatchNavigationPropertyProjection(ResourceExpression input, Expression potentialPropertyRef, bool requireSet)
            { 
                Expression foundInstance;
                List propertyNames;
                return (PatternRules.MatchNonSingletonProperty(potentialPropertyRef) == requireSet &&
                        MatchPropertyAccess(potentialPropertyRef, out foundInstance, out propertyNames) && 
                        foundInstance == input.CreateReference());
            } 
 
            internal static bool MatchNonSingletonProperty(Expression e)
            { 
                // byte[] and char[] ares ok - other IEnums are not.
                return (TypeSystem.FindIEnumerable(e.Type) != null) &&
                    e.Type != typeof(char[]) &&
                    e.Type != typeof(byte[]); 
            }
 
            internal static bool MatchBinaryExpression(Expression e) 
            {
                return (e is BinaryExpression); 
            }

            internal static bool MatchBinaryEquality(Expression e)
            { 
                return (PatternRules.MatchBinaryExpression(e) && ((BinaryExpression)e).NodeType == ExpressionType.Equal);
            } 
 
            internal static bool MatchStringAddition(Expression e)
            { 
                if (e.NodeType == ExpressionType.Add)
                {
                    BinaryExpression be = e as BinaryExpression;
                    return be != null && 
                        be.Left.Type == typeof(string) &&
                        be.Right.Type == typeof(string); 
                } 
                return false;
            } 
        }

        private static class ValidationRules
        { 
            internal static void RequireCanNavigate(Expression e)
            { 
                if (PatternRules.MatchResourceSet(e) 
                        && ((ResourceSetExpression)e).HasSequenceQueryOptions)
                { 
                    throw new NotSupportedException(Strings.ALinq_QueryOptionsOnlyAllowedOnLeafNodes);
                }
            }
 
            internal static void RequireCanExpand(Expression e)
            { 
                if (!PatternRules.MatchResource(e)) 
                {
                    throw new NotSupportedException(Strings.ALinq_CantExpand); 
                }
            }

            internal static void RequireCanAddCustomQueryOption(Expression e) 
            {
                if (!PatternRules.MatchResource(e)) 
                { 
                    throw new NotSupportedException(Strings.ALinq_CantAddQueryOption);
                } 
            }

            internal static void RequireNonSingleton(Expression e)
            { 
                ResourceExpression re = e as ResourceExpression;
                if (re != null && re.IsSingleton) 
                { 
                    throw new NotSupportedException(Strings.ALinq_QueryOptionsOnlyAllowedOnSingletons);
                } 
            }

            internal static void RequireLegalCustomQueryOption(Expression e, ResourceExpression target)
            { 
                string name = ((string)(e as ConstantExpression).Value).Trim();
 
                if (name[0] == UriHelper.DOLLARSIGN) 
                {
                    if (target.CustomQueryOptions.Any(c => (string)c.Key.Value == name)) 
                    {
                        // don't allow dups in Astoria $ namespace.
                        throw new NotSupportedException(Strings.ALinq_CantAddDuplicateQueryOption(name));
                    } 

                    ResourceSetExpression rse = target as ResourceSetExpression; 
                    if (rse != null) 
                    {
                        switch (name.Substring(1)) 
                        {
                            case UriHelper.OPTIONFILTER:
                                if (rse.Filter != null)
                                { 
                                    throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
                                } 
                                break; 
                            case UriHelper.OPTIONORDERBY:
                                if (rse.OrderBy != null) 
                                    throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
                                break;
                            case UriHelper.OPTIONEXPAND:
                                if (rse.ExpandPaths.Count > 0) 
                                    throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
                                break; 
                            case UriHelper.OPTIONSKIP: 
                                if (rse.Skip != null)
                                    throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name)); 
                                break;
                            case UriHelper.OPTIONTOP:
                                if (rse.Take != null)
                                    throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name)); 
                                break;
                            default: 
                                throw new NotSupportedException(Strings.ALinq_CantAddQueryOptionStartingWithDollarSign(name)); 
                        }
                    } 
                }
            }
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//  
//      Expression visitor that binds expression tree to resources
//  
// 
// @owner  [....]
//--------------------------------------------------------------------- 

namespace System.Data.Services.Client
{
    #region Namespaces. 

    using System; 
    using System.Collections.Generic; 
    using System.Data.Services.Common;
    using System.Diagnostics; 
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
 
    #endregion Namespaces.
 
    ///  
    /// This class provides a Bind method that analyzes an input  and returns a bound tree.
    ///  
    internal class ResourceBinder : DataServiceExpressionVisitor
    {
        /// 
        /// Sets of  that require a key predicate that hasn't been satisfied yet. 
        /// 
        private readonly HashSet setsRequiringKeyPredicate = new HashSet(EqualityComparer.Default); 
 
        /// Analyzes and binds the specified expression.
        /// Expression to bind. 
        /// The expression with bound nodes.
        internal static Expression Bind(Expression e)
        {
            ResourceBinder rb = new ResourceBinder(); 
            Expression boundExpression = rb.Visit(e);
 
            // Post Process bound tree 
            // check to see if path segment ends in multi-valued property with no key predicate
            ResourceSetExpression rse = boundExpression as ResourceSetExpression; 
            if (rse != null)
            {
                rse.IsLeafResource = true;
            } 

            // If there are missing key predicates, only throw if translation was successful and the missing 
            // key predicate is the only error. Otherwise, allow the expression that could not be translated 
            // to propagate to the UriWriter which will throw to indicate that it is not supported.
            if ((boundExpression is ResourceExpression) && 
                rb.setsRequiringKeyPredicate.Count > 0)
            {
                throw new NotSupportedException(Strings.ALinq_CantNavigateWithoutKeyPredicate);
            } 

            return boundExpression; 
        } 

        private Expression AnalyzePredicate(MethodCallExpression mce) 
        {
            // Validate that the input is a resource set and retrieve the Lambda that defines the predicate
            ResourceSetExpression input;
            LambdaExpression le; 
            if (!TryGetResourceSetMethodArguments(mce, out input, out le))
            { 
                // might have failed because of singleton, so throw better error if so. 
                ValidationRules.RequireNonSingleton(mce.Arguments[0]);
                return mce; 
            }

            //
            // Valid predicate patterns are as follows: 
            // 1. A URI-compatible filter applied to the input resource set
            // 2. A key-predicate filter applied to the input resource set 
            // - Additionally, key-predicate filters may be applied to any resource path component 
            //   that does not already have a key-predicate filter, regardless of any filter applied
            //   to the current input resource set, if transparent scopes are present. 
            // - It is not valid to apply a key-predicate or a filter to a resource set
            //   for which key-predicate is already present.
            // - It is valid to apply a filter to a resource set for which a filter already exists;
            //   such filters are AND'd together. 
            //
            // [Key-predicate that targets a path component AND]* [Key-predicate over input | URI-compatible filter over input]+ 
 
            List conjuncts = new List();
            AddConjuncts(le.Body, conjuncts); 

            Dictionary> predicatesByTarget = new Dictionary>();
            List referencedInputs = new List();
            foreach (Expression e in conjuncts) 
            {
                Expression reboundPredicate = InputBinder.Bind(e, input, le.Parameters[0], referencedInputs); 
                if (referencedInputs.Count > 1) 
                {
                    // UNSUPPORTED: A single clause cannot refer to more than one resource set 
                    return mce;
                }

                ResourceSetExpression boundTarget = (referencedInputs.Count == 0 ? input : referencedInputs[0] as ResourceSetExpression); 
                if (boundTarget == null)
                { 
                    // UNSUPPORTED: Each clause must refer to a path component that is a resource set, not a singleton navigation property 
                    return mce;
                } 

                List targetPredicates = null;
                if (!predicatesByTarget.TryGetValue(boundTarget, out targetPredicates))
                { 
                    targetPredicates = new List();
                    predicatesByTarget[boundTarget] = targetPredicates; 
                } 

                targetPredicates.Add(reboundPredicate); 
                referencedInputs.Clear();
            }

            conjuncts = null; 
            List inputPredicates;
            if (predicatesByTarget.TryGetValue(input, out inputPredicates)) 
            { 
                predicatesByTarget.Remove(input);
            } 
            else
            {
                inputPredicates = null;
            } 

            foreach (KeyValuePair> predicates in predicatesByTarget) 
            { 
                ResourceSetExpression target = predicates.Key;
                List clauses = predicates.Value; 

                Dictionary keyValues;
                if (!ExtractKeyPredicate(target, clauses, out keyValues) ||
                    clauses.Count > 0) 
                {
                    // UNSUPPORTED: Only key predicates may be applied to earlier path components 
                    return mce; 
                }
 
                // Earlier path components must be navigation sources, and navigation sources cannot have query options.
                Debug.Assert(!target.HasQueryOptions, "Navigation source had query options?");

                SetKeyPredicate(target, keyValues); 
            }
 
            if (inputPredicates != null) 
            {
                Dictionary inputKeyValues; 
                if (ExtractKeyPredicate(input, inputPredicates, out inputKeyValues))
                {
                    if (input.HasSequenceQueryOptions)
                    { 
                        // SQLBUDT 616297: A key predicate cannot be applied if query options other than 'Expand' are present,
                        // so merge the key predicate into the filter query option instead. 
                        inputPredicates.Add(BuildKeyPredicateFilter(input.CreateReference(), inputKeyValues)); 
                    }
                    else 
                    {
                        SetKeyPredicate(input, inputKeyValues);
                    }
                } 

                if (inputPredicates.Count > 0) 
                { 
                    if (input.KeyPredicate != null)
                    { 
                        // SQLBUDT 616297: a filter query option cannot be specified if a key predicate is present,
                        // so remove the existing key predicate and add it to inputPredicates as a filter. Note that
                        // this can only be done on the current resource set, where the filter query option is being
                        // applied, since earlier sets in the resource path are navigations that require a singleton, 
                        // enforced by the presence of a key predicate.
                        inputPredicates.Add(BuildKeyPredicateFilter(input.CreateReference(), input.KeyPredicate)); 
                        input.KeyPredicate = null; 
                    }
 
                    int start;
                    Expression newFilter;
                    if (input.Filter != null)
                    { 
                        start = 0;
                        newFilter = input.Filter.Predicate; 
                    } 
                    else
                    { 
                        start = 1;
                        newFilter = inputPredicates[0];
                    }
 
                    for (int idx = start; idx < inputPredicates.Count; idx++)
                    { 
                        newFilter = Expression.And(newFilter, inputPredicates[idx]); 
                    }
 
                    AddSequenceQueryOption(input, new FilterQueryOptionExpression(mce.Method.ReturnType, newFilter));
                }
            }
 
            return input; // No need to adjust this.currentResource - filters are merged in all cases
        } 
 
        private void SetKeyPredicate(ResourceSetExpression rse, Dictionary keyValues)
        { 
            Debug.Assert(rse != null, "rse != null");
            Debug.Assert(keyValues != null, "keyValues != null");

            if (rse.KeyPredicate == null) 
            {
                rse.KeyPredicate = new Dictionary(); 
            } 

            foreach(var g in keyValues) 
            {
                if (rse.KeyPredicate.Keys.Contains(g.Key))
                {
                    //UNSUPPORTED:  =  AND  =  are multiple key predicates and 
                    //cannot be represented as a resource path.
                    throw Error.NotSupported(Strings.ALinq_CanOnlyApplyOneKeyPredicate); 
                } 

                rse.KeyPredicate.Add(g.Key, g.Value); 
            }

            this.setsRequiringKeyPredicate.Remove(rse);
        } 

        private static bool ExtractKeyPredicate(ResourceSetExpression target, List predicates, out Dictionary keyValues) 
        { 
            keyValues = null;
            List nonKeyPredicates = null; 

            foreach (Expression predicate in predicates)
            {
                PropertyInfo property; 
                ConstantExpression constantValue;
                if (PatternRules.MatchKeyComparison(predicate, out property, out constantValue)) 
                { 
                    if (keyValues == null)
                    { 
                        keyValues = new Dictionary();
                    }
                    else if (keyValues.ContainsKey(property))
                    { 
                        // UNSUPPORTED:  =  AND  =  are multiple key predicates and
                        // cannot be represented as a resource path. 
                        throw Error.NotSupported(Strings.ALinq_CanOnlyApplyOneKeyPredicate); 
                    }
 
                    keyValues.Add(property, constantValue);
                }
                else
                { 
                    if (nonKeyPredicates == null)
                    { 
                        nonKeyPredicates = new List(); 
                    }
                    nonKeyPredicates.Add(predicate); 
                }
            }

            Debug.Assert(keyValues != null || nonKeyPredicates != null, "No key predicates or non-key predicates found?"); 

            if (keyValues != null && 
                !PatternRules.GetKeyProperties(target.CreateReference().Type).SequenceEqual(keyValues.Keys)) 
            {
                keyValues = null; 
                return false;
            }

            // If keyValues is non-null then at least one expression from predicates was a key comparison 
            // and should no longer be present in the predicates list since it is now in the key values dictionary.
            if (keyValues != null) 
            { 
                // Remove all predicates.
                predicates.Clear(); 

                // If any non-key predicates were found, add them back
                if (nonKeyPredicates != null)
                { 
                    predicates.AddRange(nonKeyPredicates);
                } 
            } 

            return (keyValues != null); 
        }

        private static Expression BuildKeyPredicateFilter(InputReferenceExpression input, Dictionary keyValuesDictionary)
        { 
            Debug.Assert(keyValuesDictionary.Count > 0, "At least one key property is required in a key predicate");
 
            Expression retExpr = null; 
            foreach (KeyValuePair keyValue in keyValuesDictionary)
            { 
                Expression clause = Expression.Equal(Expression.Property(input, keyValue.Key), keyValue.Value);
                if (retExpr == null)
                {
                    retExpr = clause; 
                }
                else 
                { 
                    retExpr = Expression.And(retExpr, clause);
                } 
            }

            return retExpr;
        } 

        private static void AddConjuncts(Expression e, List conjuncts) 
        { 
            if (PatternRules.MatchAnd(e))
            { 
                BinaryExpression be = (BinaryExpression)e;
                AddConjuncts(be.Left, conjuncts);
                AddConjuncts(be.Right, conjuncts);
            } 
            else
            { 
                conjuncts.Add(e); 
            }
        } 

        internal Expression AnalyzeProjection(MethodCallExpression mce)
        {
            Expression input = mce.Arguments[0]; 
            LambdaExpression le;
            if(!PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out le)) 
            { 
                return mce;
            } 
            else if (PatternRules.MatchIdentitySelector(le))
            {
                return input;
            } 
            else if (PatternRules.MatchTransparentIdentitySelector(input, le))
            { 
                return RemoveTransparentScope(mce.Method.ReturnType, (ResourceSetExpression)input); 
            }
 
            ResourceExpression navSource;
            Expression boundProjection;
            if (IsValidNavigationSource(input, out navSource) &&
                TryBindToInput(navSource, le, out boundProjection) && 
                PatternRules.MatchPropertyProjectionSingleton(navSource, boundProjection))
            { 
                return CreateNavigationPropertySingletonExpression(mce.Method.ReturnType, navSource, boundProjection); 
            }
 
            return mce;
        }

        private bool IsValidNavigationSource(Expression input, out ResourceExpression sourceExpression) 
        {
            ValidationRules.RequireCanNavigate(input); 
 
            sourceExpression = input as ResourceExpression;
            if (sourceExpression != null && !sourceExpression.IsSingleton) 
            {
                ResourceSetExpression rse = (ResourceSetExpression)sourceExpression;
                this.setsRequiringKeyPredicate.Add(rse);
            } 

            return (sourceExpression != null); 
        } 

        internal Expression AnalyzeSelectMany(MethodCallExpression mce) 
        {
            if (mce.Arguments.Count != 2 && mce.Arguments.Count != 3)
            {
                return mce; 
            }
 
            ResourceExpression input; 
            if (!IsValidNavigationSource(mce.Arguments[0], out input))
            { 
                return mce;
            }

            LambdaExpression le; 
            if(!PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out le))
            { 
                return mce; 
            }
 
            List referencedInputs = new List();
            Expression navPropRef = InputBinder.Bind(le.Body, input, le.Parameters[0], referencedInputs);
            Type resourceType = TypeSystem.GetElementType(navPropRef.Type);
 
            // could be a Cast sequence operator here that can be optimized out.
            SequenceMethod sm; 
            MethodCallExpression mce2 = navPropRef as MethodCallExpression; 
            while (mce2 != null && ReflectionUtil.TryIdentifySequenceMethod(mce2.Method, out sm) && sm == SequenceMethod.Cast)
            { 
                navPropRef = mce2.Arguments[0];
                mce2 = navPropRef as MethodCallExpression;
            }
 
            ResourceSetExpression rse = null;
            if (PatternRules.MatchPropertyProjectionSet(input, navPropRef)) 
            { 
                rse = CreateResourceSetExpression(mce.Method.ReturnType, input, navPropRef, resourceType);
            } 
            else
            {
                return mce;
            } 

            if (mce.Arguments.Count == 3) 
            { 
                LambdaExpression selector = StripQuotes(mce.Arguments[2]) as LambdaExpression;
 
                // now examine selector
                // Check for transparent scope result - projects the input and the selector
                ResourceSetExpression.TransparentAccessors transparentScope;
                if (PatternRules.MatchTransparentScopeSelector(selector, out transparentScope)) 
                {
                    rse.TransparentScope = transparentScope; 
                } 
                else if (!PatternRules.MatchIdentityProjectionResultSelector(selector))
                { 
                    return mce;
                }
            }
 
            return rse;
        } 
 
        internal static Expression ApplyOrdering(MethodCallExpression mce, ResourceSetExpression input, Expression selector, bool descending, bool thenBy)
        { 
            List selectors;
            if (!thenBy)
            {
                selectors = new List(); 
                AddSequenceQueryOption(input, new OrderByQueryOptionExpression(mce.Type, selectors));
            } 
            else 
            {
                Debug.Assert(input.OrderBy != null); 
                selectors = input.OrderBy.Selectors;
            }

            selectors.Add(new OrderByQueryOptionExpression.Selector(selector, descending)); 

            return input; 
        } 

#if !ASTORIA_LIGHT      // Silverlight doesn't support synchronous operators. 

        /// Ensures that there's a limit on the cardinality of a query.
        ///  for the method to limit First/Single(OrDefault).
        /// Maximum cardinality to allow. 
        /// 
        /// An expression that limits  to no more than  elements. 
        ///  
        /// This method is used by .First(OrDefault) and .Single(OrDefault) to limit cardinality.
        private static Expression LimitCardinality(MethodCallExpression mce, int maxCardinality) 
        {
            Debug.Assert(mce != null, "mce != null");
            Debug.Assert(maxCardinality > 0, "Cardinality must be at least 1");
 
            if (mce.Arguments.Count != 1)
            { 
                // Don't support methods with predicates. 
                return mce;
            } 

            ResourceSetExpression rse = mce.Arguments[0] as ResourceSetExpression;
            if (rse != null)
            { 
                if (!rse.HasKeyPredicate && // no-op, already returns a singleton
                    (ResourceExpressionType)rse.NodeType != ResourceExpressionType.ResourceNavigationProperty) 
                { 
                    if (rse.Take == null || (int)rse.Take.TakeAmount.Value > maxCardinality)
                    { 
                        AddSequenceQueryOption(rse, new TakeQueryOptionExpression(mce.Type, Expression.Constant(maxCardinality)));
                    }
                }
                return mce.Arguments[0]; 
            }
            else if (mce.Arguments[0] is NavigationPropertySingletonExpression) 
            { 
                // no-op, already returns a singleton
                return mce.Arguments[0]; 
            }

            return mce;
        } 

#endif 
 
        private static Expression AnalyzeCast(MethodCallExpression mce)
        { 
            ResourceExpression rse = mce.Arguments[0] as ResourceExpression;
            if (rse != null)
            {
                return rse.Cast(mce.Method.ReturnType); 
            }
 
            return mce; 
        }
 
        private static Expression StripConvert(Expression e)
        {
            UnaryExpression ue = e as UnaryExpression;
 
            if (ue != null &&
                ue.NodeType == ExpressionType.Convert && 
                ue.Type.IsGenericType && 
                ue.Type.GetGenericTypeDefinition() == typeof(DataServiceQuery<>))
            { 
                e = ue.Operand;
                ResourceExpression re = e as ResourceExpression;
                if (re != null)
                { 
                    e = re.Cast(ue.Type);
                } 
            } 

            return e; 
        }

        private static Expression AnalyzeExpand(MethodCallExpression mce)
        { 
            Expression obj = StripConvert(mce.Object);
            ResourceExpression re = obj as ResourceExpression; 
            if (re == null) 
            {
                return mce; 
            }

            ValidationRules.RequireCanExpand(re);
            ConstantExpression ce = mce.Arguments[0] as ConstantExpression; 
            re.ExpandPaths.Add(ce);
 
            return re; 
        }
 
        private static Expression AnalyzeAddCustomQueryOption(MethodCallExpression mce)
        {
            Expression obj = StripConvert(mce.Object);
            ResourceExpression re = obj as ResourceExpression; 
            if (re == null)
            { 
                return mce; 
            }
 
            ValidationRules.RequireCanAddCustomQueryOption(re);
            ValidationRules.RequireLegalCustomQueryOption(mce.Arguments[0], re);

            ConstantExpression name = mce.Arguments[0] as ConstantExpression; 
            ConstantExpression value = mce.Arguments[1] as ConstantExpression;
 
            re.CustomQueryOptions.Add(name, value); 

            return re; 
        }

        private static ResourceSetExpression CreateResourceSetExpression(Type type, ResourceExpression source, Expression memberExpression, Type resourceType)
        { 
            ResourceSetExpression newResource = new ResourceSetExpression(type, source, memberExpression, resourceType, source.ExpandPaths.ToList(), source.CustomQueryOptions.ToDictionary(kvp => kvp.Key, kvp => kvp.Value));
            source.ExpandPaths.Clear(); 
            source.CustomQueryOptions.Clear(); 
            return newResource;
        } 

        private static NavigationPropertySingletonExpression CreateNavigationPropertySingletonExpression(Type type, ResourceExpression source, Expression memberExpression)
        {
            NavigationPropertySingletonExpression newResource = new NavigationPropertySingletonExpression(type, source, memberExpression, source.ExpandPaths.ToList(), source.CustomQueryOptions.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)); 
            source.ExpandPaths.Clear();
            source.CustomQueryOptions.Clear(); 
            return newResource; 
        }
 
        /// 
        /// Produces a new  that is a clone of  in all respects,
        /// other than its result type - which will be  - and it's transparent scope,
        /// which will be null. This is a shallow clone operation - sequence query options, key predicate, etc are 
        /// not cloned, but are reassigned to the new instance. The  resource expression should be
        /// discarded after being used with this method. 
        ///  
        /// The result type -  - that the new resource set expression should have.
        /// The resource set expression from which the transparent scope is being removed 
        /// A new resource set expression without an enclosing transparent scope and with the specified result type.
        private static ResourceSetExpression RemoveTransparentScope(Type expectedResultType, ResourceSetExpression input)
        {
            // Create a new resource set expression based on the input 
            ResourceSetExpression newResource = new ResourceSetExpression(expectedResultType, input.Source, input.MemberExpression, input.ResourceType, input.ExpandPaths, input.CustomQueryOptions);
 
            // Reassign state items that are not constructor arguments - query options + key predicate 
            newResource.KeyPredicate = input.KeyPredicate;
            foreach (QueryOptionExpression queryOption in input.SequenceQueryOptions) 
            {
                newResource.AddSequenceQueryOption(queryOption);
            }
 
            // Instruct the new resource set expression to use input's Input Reference instead of creating its own.
            // This will also update the Input Reference to specify the new resource set expression as it's target, 
            // so that any usage of it in query option expressions is consistent with the new resource set expression. 
            newResource.OverrideInputReference(input);
 
            return newResource;
        }

        private static Expression StripQuotes(Expression e) 
        {
            return e.NodeType == ExpressionType.Quote ? ((UnaryExpression)e).Operand : e; 
        } 

        internal override Expression VisitResourceSetExpression(ResourceSetExpression rse) 
        {
            if ((ResourceExpressionType)rse.NodeType == ResourceExpressionType.RootResourceSet)
            {
                Debug.Assert(!rse.HasQueryOptions); 
                // since we could be adding query options to the root, create a new one which can be mutable.
                return new ResourceSetExpression(rse.Type, rse.Source, rse.MemberExpression, rse.ResourceType, null, null); 
            } 

            return rse; 
        }

        private static bool TryGetResourceSetMethodArguments(MethodCallExpression mce, out ResourceSetExpression input, out LambdaExpression lambda)
        { 
            input = null;
            lambda = null; 
 
            input = mce.Arguments[0] as ResourceSetExpression;
            if (input != null && 
                PatternRules.MatchSingleArgumentLambda(mce.Arguments[1], out lambda))
            {
                return true;
            } 

            return false; 
        } 

        private static bool TryBindToInput(ResourceExpression input, LambdaExpression le, out Expression bound) 
        {
            List referencedInputs = new List();
            bound = InputBinder.Bind(le.Body, input, le.Parameters[0], referencedInputs);
            if (referencedInputs.Count > 1 || (referencedInputs.Count == 1 && referencedInputs[0] != input)) 
            {
                bound = null; 
            } 

            return (bound != null); 
        }

        private static Expression AnalyzeResourceSetMethod(MethodCallExpression mce, Func sequenceMethodAnalyzer)
        { 
            ResourceSetExpression input;
            LambdaExpression le; 
            if (!TryGetResourceSetMethodArguments(mce, out input, out le)) 
            {
                // UNSUPPORTED: Expected LambdaExpression as second argument to sequence method 
                return mce;
            }

            Expression lambdaBody; 
            if (!TryBindToInput(input, le, out lambdaBody))
            { 
                // UNSUPPORTED: Lambda should reference the input, and only the input 
                return mce;
            } 

            return sequenceMethodAnalyzer(mce, input, lambdaBody);
        }
 
        private static Expression AnalyzeResourceSetConstantMethod(MethodCallExpression mce, Func constantMethodAnalyzer)
        { 
            ResourceExpression input = (ResourceExpression)mce.Arguments[0]; 
            ConstantExpression constantArg = mce.Arguments[1] as ConstantExpression;
            if (null == constantArg) 
            {
                // UNSUPPORTED: A ConstantExpression is expected
                return mce;
            } 

            return constantMethodAnalyzer(mce, input, constantArg); 
        } 

        private static void AddSequenceQueryOption(ResourceExpression target, QueryOptionExpression qoe) 
        {
            ValidationRules.RequireNonSingleton(target);
            ResourceSetExpression rse = (ResourceSetExpression)target;
 
            // Validation that can add option
            switch (qoe.NodeType) 
            { 
                case (ExpressionType)ResourceExpressionType.FilterQueryOption:
                    if (rse.Skip != null) 
                    {
                        throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("filter", "skip"));
                    }
                    else if (rse.Take != null) 
                    {
                        throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("filter", "top")); 
                    } 
                    break;
 
                case (ExpressionType)ResourceExpressionType.OrderByQueryOption:
                    if (rse.Skip != null)
                    {
                        throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("orderby", "skip")); 
                    }
                    else if (rse.Take != null) 
                    { 
                        throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("orderby", "top"));
                    } 
                    break;
                case (ExpressionType)ResourceExpressionType.SkipQueryOption:
                    if (rse.Take != null)
                    { 
                        throw new NotSupportedException(Strings.ALinq_QueryOptionOutOfOrder("skip", "top"));
                    } 
                    break; 
                default:
                    break; 
            }

            rse.AddSequenceQueryOption(qoe);
        } 

        internal override Expression VisitBinary(BinaryExpression b) 
        { 
            Expression e = base.VisitBinary(b);
            if (PatternRules.MatchStringAddition(e)) 
            {
                BinaryExpression be = e as BinaryExpression;
                MethodInfo mi = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string)});
                return Expression.Call(mi, new Expression[] {be.Left, be.Right}); 
            }
 
            return e; 
        }
 
        internal override Expression VisitMemberAccess(MemberExpression m)
        {
            Expression e = base.VisitMemberAccess(m);
            MemberExpression me = e as MemberExpression; 
            PropertyInfo pi;
            MethodInfo mi; 
            if (me != null && 
                PatternRules.MatchNonPrivateReadableProperty(me, out pi) &&
                TypeSystem.TryGetPropertyAsMethod(pi, out mi)) 
            {
                return Expression.Call(me.Expression, mi);
            }
 
            return e;
        } 
 
        internal override Expression VisitMethodCall(MethodCallExpression mce)
        { 
            Expression e = base.VisitMethodCall(mce);
            mce = e as MethodCallExpression;

            if (mce != null) 
            {
                SequenceMethod sequenceMethod; 
                if (ReflectionUtil.TryIdentifySequenceMethod(mce.Method, out sequenceMethod)) 
                {
                    switch (sequenceMethod) 
                    {
                        case SequenceMethod.Where:
                            return AnalyzePredicate(mce);
                        case SequenceMethod.Select: 
                            return AnalyzeProjection(mce);
                        case SequenceMethod.SelectMany: 
                        case SequenceMethod.SelectManyResultSelector: 
                            return AnalyzeSelectMany(mce);
                        case SequenceMethod.Take: 
                            return AnalyzeResourceSetConstantMethod(mce, (callExp, resource, takeCount) => { AddSequenceQueryOption(resource, new TakeQueryOptionExpression(callExp.Type, takeCount)); return resource; });
                        case SequenceMethod.Skip:
                            return AnalyzeResourceSetConstantMethod(mce, (callExp, resource, skipCount) => { AddSequenceQueryOption(resource, new SkipQueryOptionExpression(callExp.Type, skipCount)); return resource; });
                        case SequenceMethod.OrderBy: 
                            return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, /*descending=*/false, /*thenBy=*/false));
                        case SequenceMethod.ThenBy: 
                            return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, /*descending=*/false, /*thenBy=*/true)); 
                        case SequenceMethod.OrderByDescending:
                            return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, /*descending=*/true, /*thenBy=*/false)); 
                        case SequenceMethod.ThenByDescending:
                             return AnalyzeResourceSetMethod(mce, (callExp, resource, selector) => ApplyOrdering(callExp, resource, selector, /*descending=*/true, /*thenBy=*/true));
#if !ASTORIA_LIGHT      // Silverlight doesn't support synchronous operators.
                        case SequenceMethod.First: 
                        case SequenceMethod.FirstOrDefault:
                            return LimitCardinality(mce, 1); 
                        case SequenceMethod.Single: 
                        case SequenceMethod.SingleOrDefault:
                            return LimitCardinality(mce, 2); 
#endif
                        case SequenceMethod.Cast:
                            return AnalyzeCast(mce);
                        default: 
                            throw Error.MethodNotSupported(mce);
                    } 
                } 
                else if (mce.Method.DeclaringType.IsGenericType &&
                    mce.Method.DeclaringType.GetGenericTypeDefinition() == typeof(DataServiceQuery<>)) 
                {
                    Type t = typeof(DataServiceQuery<>).MakeGenericType(mce.Method.DeclaringType.GetGenericArguments()[0]);

                    if (mce.Method == t.GetMethod("Expand", new Type[] { typeof(string) })) 
                    {
                        return AnalyzeExpand(mce); 
                    } 
                    else if (mce.Method == t.GetMethod("AddQueryOption", new Type[] { typeof(string), typeof(object) }))
                    { 
                        return AnalyzeAddCustomQueryOption(mce);
                    }
                    else
                    { 
                        throw Error.MethodNotSupported(mce);
                    } 
                } 
                return mce;
            } 

            return e;
        }
 
        private static class PatternRules
        { 
            internal static bool MatchPropertyAccess(Expression e, out Expression instance, out List propertyPath) 
            {
                instance = null; 
                propertyPath = null;

                MemberExpression me = e as MemberExpression;
                while (me != null) 
                {
                    PropertyInfo pi; 
                    if (MatchNonPrivateReadableProperty(me, out pi)) 
                    {
                        if (propertyPath == null) 
                        {
                            propertyPath = new List();
                        }
                        propertyPath.Insert(0, pi.Name); 

                        e = me.Expression; 
                        me = e as MemberExpression; 
                    }
                    else 
                    {
                        me = null;
                    }
                } 

                if (propertyPath != null) 
                { 
                    instance = e;
                    return true; 
                }

                return false;
            } 

            // is constant 
            internal static bool MatchConstant(Expression e, out ConstantExpression constExpr) 
            {
                constExpr = e as ConstantExpression; 
                return (constExpr != null);
            }

            internal static bool MatchAnd(Expression e) 
            {
                BinaryExpression be = e as BinaryExpression; 
                return (be != null && (be.NodeType == ExpressionType.And || be.NodeType == ExpressionType.AndAlso)); 
            }
 
            internal static bool MatchNonPrivateReadableProperty(Expression e, out PropertyInfo propInfo)
            {
                MemberExpression me = e as MemberExpression;
                if (me == null) 
                {
                    propInfo = null; 
                    return false; 
                }
                return MatchNonPrivateReadableProperty(me, out propInfo); 
            }

            internal static bool MatchNonPrivateReadableProperty(MemberExpression me, out PropertyInfo propInfo)
            { 
                Debug.Assert(me != null);
 
                propInfo = null; 

                if (me.Member.MemberType == MemberTypes.Property) 
                {
                    PropertyInfo pi = (PropertyInfo)me.Member;
                    if (pi.CanRead && !TypeSystem.IsPrivate(pi))
                    { 
                        propInfo = pi;
                        return true; 
                    } 
                }
                return false; 
            }

            /// 
            /// Checks whether the specified  is a member access to a key. 
            /// 
            /// Expression to check. 
            /// If this is a key access, the property for the key. 
            /// true if  is a member access to a key; false otherwise.
            internal static bool MatchKeyProperty(Expression expression, out PropertyInfo property) 
            {
                property = null;

                // make sure member is property, it is public and has a Getter. 
                PropertyInfo pi;
                if (!PatternRules.MatchNonPrivateReadableProperty(expression, out pi)) 
                { 
                    return false;
                } 

                if (GetKeyProperties(pi.ReflectedType).Contains(pi))
                {
                    property = pi; 
                    return true;
                } 
 
                return false;
            } 

            /// Gets the key properties from the specified .
            /// Type to get properties from.
            /// A list of properties that are key for the type; possibly an empty list. 
            internal static List GetKeyProperties(Type type)
            { 
                Debug.Assert(type != null, "type != null"); 
                ClientType clientType = ClientType.Create(type, false /* expectModelType */);
                var result = new List(); 
                foreach (var property in clientType.Properties)
                {
                    if (property.KeyProperty)
                    { 
                        result.Add(property.DeclaringType.GetProperty(property.PropertyName));
                    } 
                } 

                return result; 
            }

            internal static bool MatchKeyComparison(Expression e, out PropertyInfo keyProperty, out ConstantExpression keyValue)
            { 
                if (PatternRules.MatchBinaryEquality(e))
                { 
                    BinaryExpression be = (BinaryExpression)e; 
                    if ((PatternRules.MatchKeyProperty(be.Left, out keyProperty) && PatternRules.MatchConstant(be.Right, out keyValue)) ||
                        (PatternRules.MatchKeyProperty(be.Right, out keyProperty) && PatternRules.MatchConstant(be.Left, out keyValue))) 
                    {
                        // if property is compared to null, expression is not key predicate comparison
                        return (keyValue.Value != null);
                    } 
                }
 
                keyProperty = null; 
                keyValue = null;
                return false; 
            }

            internal static bool MatchResourceSet(Expression e)
            { 
                return (e is ResourceSetExpression);
            } 
 
            internal static bool MatchResource(Expression e)
            { 
                return (e is ResourceExpression);
            }

            internal static bool MatchSingleArgumentLambda(Expression e, out LambdaExpression lambda) 
            {
                lambda = null; 
 
                LambdaExpression le = StripQuotes(e) as LambdaExpression;
                if (le != null && le.Parameters.Count == 1) 
                {
                    lambda = le;
                }
 
                return (lambda != null);
            } 
 
            internal static bool MatchIdentitySelector(Expression e)
            { 
                LambdaExpression le = (LambdaExpression)e;
                return (le.Body == le.Parameters[0]);
            }
 
            internal static bool MatchTransparentIdentitySelector(Expression input, LambdaExpression selector)
            { 
                if (selector.Parameters.Count != 1) 
                {
                    return false; 
                }

                return MatchTransparentScopeAccess(input, selector.Body, selector.Parameters[0]);
            } 

            private static bool MatchTransparentScopeAccess(Expression input, Expression potentialRef, ParameterExpression expectedTarget) 
            { 
                ResourceSetExpression rse = input as ResourceSetExpression;
                if (rse == null || rse.TransparentScope == null) 
                {
                    return false;
                }
 
                Expression paramRef;
                List refPath; 
                return (MatchPropertyAccess(potentialRef, out paramRef, out refPath) && 
                        paramRef == expectedTarget &&
                        refPath != null && refPath.Count == 1 && 
                        string.Equals(refPath[0], rse.TransparentScope.Accessor, StringComparison.Ordinal));
            }

            internal static bool MatchIdentityProjectionResultSelector(Expression e) 
            {
                LambdaExpression le = (LambdaExpression)e; 
                return (le.Body == le.Parameters[1]); 
            }
 
            internal static bool MatchTransparentScopeSelector(Expression e, out ResourceSetExpression.TransparentAccessors transparentScope)
            {
                transparentScope = null;
 
                LambdaExpression le = (LambdaExpression)e;
                if (le.Body.NodeType == ExpressionType.New) 
                { 
                    NewExpression ne = (NewExpression)le.Body;
                    if (ne.Constructor.GetParameters().Length == 2 && 
                        ne.Arguments[0] == le.Parameters[0] &&
                        ne.Arguments[1] == le.Parameters[1])
                    {
                        PropertyInfo[] publicProps = le.Body.Type.GetProperties(BindingFlags.Instance | BindingFlags.Public); 
                        if (publicProps.Length == 2)
                        { 
                            if (le.Body.Type.BaseType.Equals(typeof(object)) && 
                                   publicProps[0].Name.Equals(le.Parameters[0].Name, StringComparison.Ordinal) &&
                                   publicProps[1].Name.Equals(le.Parameters[1].Name, StringComparison.Ordinal)) 
                            {
                                transparentScope =
                                    new ResourceSetExpression.TransparentAccessors(
                                        publicProps[1].Name, 
                                        publicProps[0].Name
                                    ); 
                                return true; 
                            }
                        } 
                    }
                }

                return false; 
            }
 
            internal static bool MatchPropertyProjectionSet(ResourceExpression input, Expression potentialPropertyRef) 
            {
                return MatchNavigationPropertyProjection(input, potentialPropertyRef, true); 
            }

            internal static bool MatchPropertyProjectionSingleton(ResourceExpression input, Expression potentialPropertyRef)
            { 
                return MatchNavigationPropertyProjection(input, potentialPropertyRef, false);
            } 
 
            private static bool MatchNavigationPropertyProjection(ResourceExpression input, Expression potentialPropertyRef, bool requireSet)
            { 
                Expression foundInstance;
                List propertyNames;
                return (PatternRules.MatchNonSingletonProperty(potentialPropertyRef) == requireSet &&
                        MatchPropertyAccess(potentialPropertyRef, out foundInstance, out propertyNames) && 
                        foundInstance == input.CreateReference());
            } 
 
            internal static bool MatchNonSingletonProperty(Expression e)
            { 
                // byte[] and char[] ares ok - other IEnums are not.
                return (TypeSystem.FindIEnumerable(e.Type) != null) &&
                    e.Type != typeof(char[]) &&
                    e.Type != typeof(byte[]); 
            }
 
            internal static bool MatchBinaryExpression(Expression e) 
            {
                return (e is BinaryExpression); 
            }

            internal static bool MatchBinaryEquality(Expression e)
            { 
                return (PatternRules.MatchBinaryExpression(e) && ((BinaryExpression)e).NodeType == ExpressionType.Equal);
            } 
 
            internal static bool MatchStringAddition(Expression e)
            { 
                if (e.NodeType == ExpressionType.Add)
                {
                    BinaryExpression be = e as BinaryExpression;
                    return be != null && 
                        be.Left.Type == typeof(string) &&
                        be.Right.Type == typeof(string); 
                } 
                return false;
            } 
        }

        private static class ValidationRules
        { 
            internal static void RequireCanNavigate(Expression e)
            { 
                if (PatternRules.MatchResourceSet(e) 
                        && ((ResourceSetExpression)e).HasSequenceQueryOptions)
                { 
                    throw new NotSupportedException(Strings.ALinq_QueryOptionsOnlyAllowedOnLeafNodes);
                }
            }
 
            internal static void RequireCanExpand(Expression e)
            { 
                if (!PatternRules.MatchResource(e)) 
                {
                    throw new NotSupportedException(Strings.ALinq_CantExpand); 
                }
            }

            internal static void RequireCanAddCustomQueryOption(Expression e) 
            {
                if (!PatternRules.MatchResource(e)) 
                { 
                    throw new NotSupportedException(Strings.ALinq_CantAddQueryOption);
                } 
            }

            internal static void RequireNonSingleton(Expression e)
            { 
                ResourceExpression re = e as ResourceExpression;
                if (re != null && re.IsSingleton) 
                { 
                    throw new NotSupportedException(Strings.ALinq_QueryOptionsOnlyAllowedOnSingletons);
                } 
            }

            internal static void RequireLegalCustomQueryOption(Expression e, ResourceExpression target)
            { 
                string name = ((string)(e as ConstantExpression).Value).Trim();
 
                if (name[0] == UriHelper.DOLLARSIGN) 
                {
                    if (target.CustomQueryOptions.Any(c => (string)c.Key.Value == name)) 
                    {
                        // don't allow dups in Astoria $ namespace.
                        throw new NotSupportedException(Strings.ALinq_CantAddDuplicateQueryOption(name));
                    } 

                    ResourceSetExpression rse = target as ResourceSetExpression; 
                    if (rse != null) 
                    {
                        switch (name.Substring(1)) 
                        {
                            case UriHelper.OPTIONFILTER:
                                if (rse.Filter != null)
                                { 
                                    throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
                                } 
                                break; 
                            case UriHelper.OPTIONORDERBY:
                                if (rse.OrderBy != null) 
                                    throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
                                break;
                            case UriHelper.OPTIONEXPAND:
                                if (rse.ExpandPaths.Count > 0) 
                                    throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name));
                                break; 
                            case UriHelper.OPTIONSKIP: 
                                if (rse.Skip != null)
                                    throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name)); 
                                break;
                            case UriHelper.OPTIONTOP:
                                if (rse.Take != null)
                                    throw new NotSupportedException(Strings.ALinq_CantAddAstoriaQueryOption(name)); 
                                break;
                            default: 
                                throw new NotSupportedException(Strings.ALinq_CantAddQueryOptionStartingWithDollarSign(name)); 
                        }
                    } 
                }
            }
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK