コード例 #1
0
        /// <summary>
        /// Visit an <see cref="InputReferenceExpression"/>, producing a new InputReferenceExpression
        /// based on the visited form of the <see cref="QueryableResourceExpression"/> that is referenced by
        /// the InputReferenceExpression argument, <paramref name="ire"/>.
        /// </summary>
        /// <param name="ire">InputReferenceExpression expression to visit</param>
        /// <returns>Visited InputReferenceExpression expression</returns>
        internal virtual Expression VisitInputReferenceExpression(InputReferenceExpression ire)
        {
            Debug.Assert(ire != null, "ire != null -- otherwise caller never should have visited here");
            ResourceExpression re = (ResourceExpression)this.Visit(ire.Target);

            return(re.CreateReference());
        }
コード例 #2
0
        /// <summary>
        /// Analyzes a lambda expression to check whether it can be satisfied with
        /// $select and client-side materialization.
        /// </summary>
        /// <param name="le">Lambda expression.</param>
        /// <param name="re">Resource expression in scope.</param>
        /// <param name="matchMembers">Whether member accesses are matched as top-level projections.</param>
        /// <param name="context">Context of expression to analyze.</param>
        /// <returns>true if the lambda is a client-side projection; false otherwise.</returns>
        internal static bool Analyze(LambdaExpression le, ResourceExpression re, bool matchMembers, DataServiceContext context)
        {
            Debug.Assert(le != null, "le != null");

            if (le.Body.NodeType == ExpressionType.Constant)
            {
                if (ClientTypeUtil.TypeOrElementTypeIsEntity(le.Body.Type))
                {
                    throw new NotSupportedException(Strings.ALinq_CannotCreateConstantEntity);
                }

                re.Projection = new ProjectionQueryOptionExpression(le.Body.Type, le, new List<string>());
                return true;
            }

            if (le.Body.NodeType == ExpressionType.MemberInit || le.Body.NodeType == ExpressionType.New)
            {
                AnalyzeResourceExpression(le, re, context);
                return true;
            }

            if (matchMembers)
            {
                // Members can be projected standalone or type-casted.
                Expression withoutConverts = SkipConverts(le.Body);
                if (withoutConverts.NodeType == ExpressionType.MemberAccess)
                {
                    AnalyzeResourceExpression(le, re, context);
                    return true;
                }
            }

            return false;
        }
コード例 #3
0
        /// <summary>
        /// Retargets this input reference to point to the resource set specified by <paramref name="newTarget"/>.
        /// </summary>
        /// <param name="newTarget">The <see cref="QueryableResourceExpression"/> that this input reference should use as its target</param>
        internal void OverrideTarget(QueryableResourceExpression newTarget)
        {
            Debug.Assert(newTarget != null, "Resource set cannot be null");
            Debug.Assert(newTarget.ResourceType.Equals(this.Type), "Cannot reference a resource set with a different resource type");

            this.target = newTarget;
        }
コード例 #4
0
        /// <summary>
        /// Analyzes a lambda expression to check whether it can be satisfied with
        /// $select and client-side materialization.
        /// </summary>
        /// <param name="le">Lambda expression.</param>
        /// <param name="re">Resource expression in scope.</param>
        /// <param name="matchMembers">Whether member accesses are matched as top-level projections.</param>
        /// <param name="context">Context of expression to analyze.</param>
        /// <returns>true if the lambda is a client-side projection; false otherwise.</returns>
        internal static bool Analyze(LambdaExpression le, ResourceExpression re, bool matchMembers, DataServiceContext context)
        {
            Debug.Assert(le != null, "le != null");

            if (le.Body.NodeType == ExpressionType.Constant)
            {
                if (ClientTypeUtil.TypeOrElementTypeIsEntity(le.Body.Type))
                {
                    throw new NotSupportedException(Strings.ALinq_CannotCreateConstantEntity);
                }

                re.Projection = new ProjectionQueryOptionExpression(le.Body.Type, le, new List <string>());
                return(true);
            }

            if (le.Body.NodeType == ExpressionType.MemberInit || le.Body.NodeType == ExpressionType.New)
            {
                AnalyzeResourceExpression(le, re, context);
                return(true);
            }

            if (matchMembers)
            {
                // Members can be projected standalone or type-casted.
                Expression withoutConverts = SkipConverts(le.Body);
                if (withoutConverts.NodeType == ExpressionType.MemberAccess)
                {
                    AnalyzeResourceExpression(le, re, context);
                    return(true);
                }
            }

            return(false);
        }
コード例 #5
0
        /// <summary>Builds the Uri for the expression passed in.</summary>
        /// <param name="e">The expression to translate into a Uri</param>
        /// <returns>Query components</returns>
        internal QueryComponents Translate(Expression e)
        {
            Uri     uri;
            Version version;
            bool    addTrailingParens = false;
            Dictionary <Expression, Expression> normalizerRewrites = null;

            // short cut analysis if just a resource set or singleton resource.
            // note - to be backwards compatible with V1, will only append trailing () for queries
            // that include more then just a resource set.
            if (!(e is QueryableResourceExpression))
            {
                normalizerRewrites = new Dictionary <Expression, Expression>(ReferenceEqualityComparer <Expression> .Instance);
                e = Evaluator.PartialEval(e);
                e = ExpressionNormalizer.Normalize(e, normalizerRewrites);
                e = ResourceBinder.Bind(e, this.Context);
                addTrailingParens = true;
            }

            UriWriter.Translate(this.Context, addTrailingParens, e, out uri, out version);
            ResourceExpression re            = e as ResourceExpression;
            Type             lastSegmentType = re.Projection == null ? re.ResourceType : re.Projection.Selector.Parameters[0].Type;
            LambdaExpression selector        = re.Projection == null ? null : re.Projection.Selector;

            return(new QueryComponents(uri, version, lastSegmentType, selector, normalizerRewrites));
        }
コード例 #6
0
        /// <summary>
        /// Retargets this input reference to point to the resource set specified by <paramref name="newTarget"/>.
        /// </summary>
        /// <param name="newTarget">The <see cref="QueryableResourceExpression"/> that this input reference should use as its target</param>
        internal void OverrideTarget(QueryableResourceExpression newTarget)
        {
            Debug.Assert(newTarget != null, "Resource set cannot be null");
            Debug.Assert(newTarget.ResourceType.Equals(this.Type), "Cannot reference a resource set with a different resource type");

            this.target = newTarget;
        }
コード例 #7
0
ファイル: UriWriter.cs プロジェクト: noflashbang/odata.net
        /// <summary>
        /// Visit Query options for Resource
        /// </summary>
        /// <param name="re">Resource Expression with query options</param>
        internal void VisitQueryOptions(ResourceExpression re)
        {
            if (re.HasQueryOptions)
            {
                this.uriBuilder.Append(UriHelper.QUESTIONMARK);

                QueryableResourceExpression rse = re as QueryableResourceExpression;
                if (rse != null)
                {
                    IEnumerator options = rse.SequenceQueryOptions.GetEnumerator();
                    while (options.MoveNext())
                    {
                        Expression e = ((Expression)options.Current);
                        ResourceExpressionType et = (ResourceExpressionType)e.NodeType;
                        switch (et)
                        {
                            case ResourceExpressionType.SkipQueryOption:
                                this.VisitQueryOptionExpression((SkipQueryOptionExpression)e);
                                break;
                            case ResourceExpressionType.TakeQueryOption:
                                this.VisitQueryOptionExpression((TakeQueryOptionExpression)e);
                                break;
                            case ResourceExpressionType.OrderByQueryOption:
                                this.VisitQueryOptionExpression((OrderByQueryOptionExpression)e);
                                break;
                            case ResourceExpressionType.FilterQueryOption:
                                this.VisitQueryOptionExpression((FilterQueryOptionExpression)e);
                                break;
                            default:
                                Debug.Assert(false, "Unexpected expression type " + (int)et);
                                break;
                        }
                    }
                }

                if (re.ExpandPaths.Count > 0)
                {
                    this.VisitExpandOptions(re.ExpandPaths);
                }

                if (re.Projection != null && re.Projection.Paths.Count > 0)
                {
                    this.VisitProjectionPaths(re.Projection.Paths);
                }

                if (re.CountOption == CountOption.CountQuery)
                {
                    this.VisitCountQueryOptions();
                }

                if (re.CustomQueryOptions.Count > 0)
                {
                    this.VisitCustomQueryOptions(re.CustomQueryOptions);
                    }

                this.AppendCachedQueryOptionsToUriBuilder();
                }
            }
コード例 #8
0
        /// <summary>
        /// Analyzes the specified <paramref name="lambda"/> for selection and updates
        /// <paramref name="resource"/>.
        /// </summary>
        /// <param name="lambda">Lambda expression to analyze.</param>
        /// <param name="resource">Resource expression to update.</param>
        /// <param name="context">Context of expression to analyze.</param>
        private static void AnalyzeResourceExpression(LambdaExpression lambda, ResourceExpression resource, DataServiceContext context)
        {
            SelectExpandPathBuilder pb = new SelectExpandPathBuilder();

            ProjectionAnalyzer.Analyze(lambda, pb, context);
            resource.Projection  = new ProjectionQueryOptionExpression(lambda.Body.Type, lambda, pb.ProjectionPaths.ToList());
            resource.ExpandPaths = pb.ExpandPaths.Union(resource.ExpandPaths, StringComparer.Ordinal).ToList();
            resource.RaiseUriVersion(pb.UriVersion);
        }
コード例 #9
0
ファイル: InputBinder.cs プロジェクト: larsenjo/odata.net
        /// <summary>
        /// Replaces Lambda parameter references or transparent scope property accesses over those Lambda
        /// parameter references with <see cref="InputReferenceExpression"/>s to the appropriate corresponding
        /// <see cref="QueryableResourceExpression"/>s, based on the 'input' QueryableResourceExpression to which the
        /// Lambda is logically applied and any enclosing transparent scope applied to that input resource.
        /// </summary>
        /// <param name="e">The expression to rebind</param>
        /// <param name="currentInput">
        /// The 'current input' resource - either the root resource or the
        /// rightmost resource in the navigation chain.</param>
        /// <param name="inputParameter">The Lambda parameter that represents a reference to the 'input'</param>
        /// <param name="referencedInputs">A list that will be populated with the resources that were referenced by the rebound expression</param>
        /// <returns>
        /// The rebound version of <paramref name="e"/> where MemberExpression/ParameterExpressions that
        /// represent resource references have been replaced with appropriate InputReferenceExpressions.
        /// </returns>
        internal static Expression Bind(Expression e, ResourceExpression currentInput, ParameterExpression inputParameter, List<ResourceExpression> referencedInputs)
        {
            Debug.Assert(e != null, "Expression cannot be null");
            Debug.Assert(currentInput != null, "A current input resource is required");
            Debug.Assert(inputParameter != null, "The input lambda parameter is required");
            Debug.Assert(referencedInputs != null, "The referenced inputs list is required");

            InputBinder binder = new InputBinder(currentInput, inputParameter);
            Expression result = binder.Visit(e);
            referencedInputs.AddRange(binder.referencedInputs);
            return result;
        }
コード例 #10
0
ファイル: InputBinder.cs プロジェクト: zdzislaw/odata.net
        /// <summary>
        /// Replaces Lambda parameter references or transparent scope property accesses over those Lambda
        /// parameter references with <see cref="InputReferenceExpression"/>s to the appropriate corresponding
        /// <see cref="QueryableResourceExpression"/>s, based on the 'input' QueryableResourceExpression to which the
        /// Lambda is logically applied and any enclosing transparent scope applied to that input resource.
        /// </summary>
        /// <param name="e">The expression to rebind</param>
        /// <param name="currentInput">
        /// The 'current input' resource - either the root resource or the
        /// rightmost resource in the navigation chain.</param>
        /// <param name="inputParameter">The Lambda parameter that represents a reference to the 'input'</param>
        /// <param name="referencedInputs">A list that will be populated with the resources that were referenced by the rebound expression</param>
        /// <returns>
        /// The rebound version of <paramref name="e"/> where MemberExpression/ParameterExpressions that
        /// represent resource references have been replaced with appropriate InputReferenceExpressions.
        /// </returns>
        internal static Expression Bind(Expression e, ResourceExpression currentInput, ParameterExpression inputParameter, List <ResourceExpression> referencedInputs)
        {
            Debug.Assert(e != null, "Expression cannot be null");
            Debug.Assert(currentInput != null, "A current input resource is required");
            Debug.Assert(inputParameter != null, "The input lambda parameter is required");
            Debug.Assert(referencedInputs != null, "The referenced inputs list is required");

            InputBinder binder = new InputBinder(currentInput, inputParameter);
            Expression  result = binder.Visit(e);

            referencedInputs.AddRange(binder.referencedInputs);
            return(result);
        }
コード例 #11
0
        internal LambdaExpression Rebind(LambdaExpression lambda, ResourceExpression source)
        {
            this.successfulRebind = true;
            this.oldLambdaParameter = lambda.Parameters[0];
            this.projectionSource = source;

            Expression body = this.Visit(lambda.Body);
            if (this.successfulRebind)
            {
                Type delegateType = typeof(Func<,>).MakeGenericType(new Type[] { newLambdaParameter.Type, lambda.Body.Type });
                return Expression.Lambda(delegateType, body, new ParameterExpression[] { this.newLambdaParameter });
            }
            else
            {
                throw new NotSupportedException(Strings.ALinq_CanOnlyProjectTheLeaf);
            }
        }
コード例 #12
0
        internal static LambdaExpression TryToRewrite(LambdaExpression le, ResourceExpression source)
        {
            Type proposedParameterType = source.ResourceType;
            LambdaExpression result;
            if (!ResourceBinder.PatternRules.MatchSingleArgumentLambda(le, out le) ||  // can only rewrite single parameter Lambdas.
                ClientTypeUtil.TypeOrElementTypeIsEntity(le.Parameters[0].Type) || // only attempt to rewrite if lambda parameter is not an entity type
                !(le.Parameters[0].Type.GetProperties().Any(p => p.PropertyType == proposedParameterType))) // lambda parameter must have public property that is same as proposed type.
            {
                result = le;
            }
            else
            {
                ProjectionRewriter rewriter = new ProjectionRewriter(proposedParameterType);
                result = rewriter.Rebind(le, source);
            }

            return result;
        }
コード例 #13
0
        internal LambdaExpression Rebind(LambdaExpression lambda, ResourceExpression source)
        {
            this.successfulRebind   = true;
            this.oldLambdaParameter = lambda.Parameters[0];
            this.projectionSource   = source;

            Expression body = this.Visit(lambda.Body);

            if (this.successfulRebind)
            {
                Type delegateType = typeof(Func <,>).MakeGenericType(new Type[] { newLambdaParameter.Type, lambda.Body.Type });
                return(Expression.Lambda(delegateType, body, new ParameterExpression[] { this.newLambdaParameter }));
            }
            else
            {
                throw new NotSupportedException(Strings.ALinq_CanOnlyProjectTheLeaf);
            }
        }
コード例 #14
0
        internal static LambdaExpression TryToRewrite(LambdaExpression le, ResourceExpression source)
        {
            Type             proposedParameterType = source.ResourceType;
            LambdaExpression result;

            if (!ResourceBinder.PatternRules.MatchSingleArgumentLambda(le, out le) ||                       // can only rewrite single parameter Lambdas.
                ClientTypeUtil.TypeOrElementTypeIsEntity(le.Parameters[0].Type) ||                          // only attempt to rewrite if lambda parameter is not an entity type
                !(le.Parameters[0].Type.GetProperties().Any(p => p.PropertyType == proposedParameterType))) // lambda parameter must have public property that is same as proposed type.
            {
                result = le;
            }
            else
            {
                ProjectionRewriter rewriter = new ProjectionRewriter(proposedParameterType);
                result = rewriter.Rebind(le, source);
            }

            return(result);
        }
コード例 #15
0
ファイル: InputBinder.cs プロジェクト: larsenjo/odata.net
 /// <summary>
 /// Constructs a new InputBinder based on the specified input resources, which is represented by the specified ParameterExpression.
 /// </summary>
 /// <param name="resource">The current input resource from which valid references must start</param>
 /// <param name="setReferenceParam">The parameter that must be referenced in order to refer to the specified input resources</param>
 private InputBinder(ResourceExpression resource, ParameterExpression setReferenceParam)
 {
     this.input = resource;
     this.inputResource = resource as QueryableResourceExpression;
     this.inputParameter = setReferenceParam;
 }
コード例 #16
0
ファイル: InputBinder.cs プロジェクト: zdzislaw/odata.net
        /// <summary>
        /// Resolves member accesses that represent transparent scope property accesses to the corresponding resource,
        /// iff the input resource is enclosed in a transparent scope and the specified MemberExpression represents
        /// such a property access.
        /// </summary>
        /// <param name="m">MemberExpression expression to visit</param>
        /// <returns>
        /// An InputReferenceExpression if the member access represents a transparent scope property
        /// access that can be resolved to a resource in the path that produces the input resource;
        /// otherwise the same MemberExpression is returned.
        /// </returns>
        internal override Expression VisitMemberAccess(MemberExpression m)
        {
            // If the current input resource is not enclosed in a transparent scope, then this
            // MemberExpression cannot represent a valid transparent scope access based on the input parameter.
            if (this.inputResource == null ||
                !this.inputResource.HasTransparentScope)
            {
                return(base.VisitMemberAccess(m));
            }

            ParameterExpression  innerParamRef  = null;
            Stack <PropertyInfo> nestedAccesses = new Stack <PropertyInfo>();
            MemberExpression     memberRef      = m;

            while (memberRef != null &&
                   PlatformHelper.IsProperty(memberRef.Member) &&
                   memberRef.Expression != null)
            {
                nestedAccesses.Push((PropertyInfo)memberRef.Member);

                if (memberRef.Expression.NodeType == ExpressionType.Parameter)
                {
                    innerParamRef = (ParameterExpression)memberRef.Expression;
                }

                memberRef = memberRef.Expression as MemberExpression;
            }

            // Only continue if the inner non-MemberExpression is the input reference ParameterExpression and
            // at least one property reference is present - otherwise this cannot be a transparent scope access.
            if (innerParamRef != this.inputParameter || nestedAccesses.Count == 0)
            {
                return(m);
            }

            ResourceExpression          target         = this.input;
            QueryableResourceExpression targetResource = this.inputResource;
            bool transparentScopeTraversed             = false;

            // Process all the traversals through transparent scopes.
            while (nestedAccesses.Count > 0)
            {
                if (targetResource == null || !targetResource.HasTransparentScope)
                {
                    break;
                }

                // Peek the property; pop it once it's consumed
                // (it could be a non-transparent-identifier access).
                PropertyInfo currentProp = nestedAccesses.Peek();

                // If this is the accessor for the target, then the member
                // refers to the target itself.
                if (currentProp.Name.Equals(targetResource.TransparentScope.Accessor, StringComparison.Ordinal))
                {
                    target = targetResource;
                    nestedAccesses.Pop();
                    transparentScopeTraversed = true;
                    continue;
                }

                // This member could also be one of the in-scope sources of the target.
                Expression source;
                if (!targetResource.TransparentScope.SourceAccessors.TryGetValue(currentProp.Name, out source))
                {
                    break;
                }

                transparentScopeTraversed = true;
                nestedAccesses.Pop();
                Debug.Assert(source != null, "source != null -- otherwise ResourceBinder created an accessor to nowhere");
                InputReferenceExpression sourceReference = source as InputReferenceExpression;
                if (sourceReference == null)
                {
                    targetResource = source as QueryableResourceExpression;
                    if (targetResource == null || !targetResource.HasTransparentScope)
                    {
                        target = (ResourceExpression)source;
                    }
                }
                else
                {
                    targetResource = sourceReference.Target as QueryableResourceExpression;
                    target         = targetResource;
                }
            }

            // If no traversals were made, the original expression is OK.
            if (!transparentScopeTraversed)
            {
                return(m);
            }

            // Process traversals after the transparent scope.
            Expression result = this.CreateReference(target);

            while (nestedAccesses.Count > 0)
            {
                result = Expression.Property(result, nestedAccesses.Pop());
            }

            return(result);
        }
コード例 #17
0
ファイル: InputBinder.cs プロジェクト: zdzislaw/odata.net
 /// <summary>
 /// Returns an <see cref="InputReferenceExpression"/> that references the specified resource,
 /// and also adds the the resource to the hashset of resources that were referenced by the
 /// expression that is being rebound.
 /// </summary>
 /// <param name="resource">The resource for which a reference was found</param>
 /// <returns>An InputReferenceExpression that represents a reference to the specified resource</returns>
 private Expression CreateReference(ResourceExpression resource)
 {
     this.referencedInputs.Add(resource);
     return(resource.CreateReference());
 }
コード例 #18
0
ファイル: UriWriter.cs プロジェクト: AlineGuan/odata.net
        /// <summary>
        /// Visit Query options for Resource
        /// </summary>
        /// <param name="re">Resource Expression with query options</param>
        internal void VisitQueryOptions(ResourceExpression re)
        {
            if (re.HasQueryOptions)
            {
                this.uriBuilder.Append(UriHelper.QUESTIONMARK);

                QueryableResourceExpression rse = re as QueryableResourceExpression;
                if (rse != null)
                {
                    IEnumerator options = rse.SequenceQueryOptions.GetEnumerator();
                    while (options.MoveNext())
                    {
                        Expression e = ((Expression)options.Current);
                        ResourceExpressionType et = (ResourceExpressionType)e.NodeType;
                        switch (et)
                        {
                            case ResourceExpressionType.SkipQueryOption:
                                this.VisitQueryOptionExpression((SkipQueryOptionExpression)e);
                                break;
                            case ResourceExpressionType.TakeQueryOption:
                                this.VisitQueryOptionExpression((TakeQueryOptionExpression)e);
                                break;
                            case ResourceExpressionType.OrderByQueryOption:
                                this.VisitQueryOptionExpression((OrderByQueryOptionExpression)e);
                                break;
                            case ResourceExpressionType.FilterQueryOption:
                                this.VisitQueryOptionExpression((FilterQueryOptionExpression)e);
                                break;
                            default:
                                Debug.Assert(false, "Unexpected expression type " + (int)et);
                                break;
                        }
                    }
                }

                if (re.ExpandPaths.Count > 0)
                {
                    this.VisitExpandOptions(re.ExpandPaths);
                }

                if (re.Projection != null && re.Projection.Paths.Count > 0)
                {
                    this.VisitProjectionPaths(re.Projection.Paths);
                }

                if (re.CountOption == CountOption.CountQuery)
                {
                    this.VisitCountQueryOptions();
                }

                if (re.CustomQueryOptions.Count > 0)
                {
                    this.VisitCustomQueryOptions(re.CustomQueryOptions);
                    }

                this.AppendCachedQueryOptionsToUriBuilder();
                }
            }
コード例 #19
0
ファイル: InputBinder.cs プロジェクト: zdzislaw/odata.net
 /// <summary>
 /// Constructs a new InputBinder based on the specified input resources, which is represented by the specified ParameterExpression.
 /// </summary>
 /// <param name="resource">The current input resource from which valid references must start</param>
 /// <param name="setReferenceParam">The parameter that must be referenced in order to refer to the specified input resources</param>
 private InputBinder(ResourceExpression resource, ParameterExpression setReferenceParam)
 {
     this.input          = resource;
     this.inputResource  = resource as QueryableResourceExpression;
     this.inputParameter = setReferenceParam;
 }
コード例 #20
0
 /// <summary>
 /// Constructs a new input reference expression that refers to the specified resource set
 /// </summary>
 /// <param name="target">The target resource set that the new expression will reference</param>
 internal InputReferenceExpression(ResourceExpression target)
 {
     Debug.Assert(target != null, "Target resource set cannot be null");
     this.target = target;
 }
コード例 #21
0
ファイル: InputBinder.cs プロジェクト: larsenjo/odata.net
 /// <summary>
 /// Returns an <see cref="InputReferenceExpression"/> that references the specified resource,
 /// and also adds the the resource to the hashset of resources that were referenced by the
 /// expression that is being rebound.
 /// </summary>
 /// <param name="resource">The resource for which a reference was found</param>
 /// <returns>An InputReferenceExpression that represents a reference to the specified resource</returns>
 private Expression CreateReference(ResourceExpression resource)
 {
     this.referencedInputs.Add(resource);
     return resource.CreateReference();
 }
コード例 #22
0
 /// <summary>
 /// Constructs a new input reference expression that refers to the specified resource set
 /// </summary>
 /// <param name="target">The target resource set that the new expression will reference</param>
 internal InputReferenceExpression(ResourceExpression target)
 {
     Debug.Assert(target != null, "Target resource set cannot be null");
     this.target = target;
 }
コード例 #23
0
 /// <summary>
 /// Analyzes the specified <paramref name="lambda"/> for selection and updates 
 /// <paramref name="resource"/>.
 /// </summary>
 /// <param name="lambda">Lambda expression to analyze.</param>
 /// <param name="resource">Resource expression to update.</param>
 /// <param name="context">Context of expression to analyze.</param>
 private static void AnalyzeResourceExpression(LambdaExpression lambda, ResourceExpression resource, DataServiceContext context)
 {
     SelectExpandPathBuilder pb = new SelectExpandPathBuilder();
     ProjectionAnalyzer.Analyze(lambda, pb, context);
     resource.Projection = new ProjectionQueryOptionExpression(lambda.Body.Type, lambda, pb.ProjectionPaths.ToList());
     resource.ExpandPaths = pb.ExpandPaths.Union(resource.ExpandPaths, StringComparer.Ordinal).ToList();
     resource.RaiseUriVersion(pb.UriVersion);
 }