/// <summary>
        /// MethodCallExpression visit method
        /// </summary>
        /// <param name="m">The MethodCallExpression expression to visit</param>
        /// <returns>The visited MethodCallExpression expression </returns>
        internal override Expression VisitMethodCall(MethodCallExpression m)
        {
            string methodName;

            if (TypeSystem.TryGetQueryOptionMethod(m.Method, out methodName))
            {
                this.builder.Append(methodName);
                this.builder.Append(UriHelper.LEFTPAREN);

                // There is a single function, 'contains', which reorders its argument with
                // respect to the CLR method. Thus handling it as a special case rather than
                // using a more general argument reordering mechanism.
                if (methodName == "contains")
                {
                    Debug.Assert(m.Method.Name == "Contains", "m.Method.Name == 'Contains'");
                    Debug.Assert(m.Object != null, "m.Object != null");
                    Debug.Assert(m.Arguments.Count == 1, "m.Arguments.Count == 1");
                    this.Visit(m.Object);
                    this.builder.Append(UriHelper.COMMA);
                    this.Visit(m.Arguments[0]);
                }
                else
                {
                    if (m.Object != null)
                    {
                        this.Visit(m.Object);
                    }

                    if (m.Arguments.Count > 0)
                    {
                        if (m.Object != null)
                        {
                            this.builder.Append(UriHelper.COMMA);
                        }

                        for (int ii = 0; ii < m.Arguments.Count; ii++)
                        {
                            this.Visit(m.Arguments[ii]);
                            if (ii < m.Arguments.Count - 1)
                            {
                                this.builder.Append(UriHelper.COMMA);
                            }
                        }
                    }
                }

                this.builder.Append(UriHelper.RIGHTPAREN);
            }
            else if (m.Method.Name == "HasFlag")
            {
                Debug.Assert(m.Method.Name == "HasFlag", "m.Method.Name == 'HasFlag'");
                Debug.Assert(m.Object != null, "m.Object != null");
                Debug.Assert(m.Arguments.Count == 1, "m.Arguments.Count == 1");
                this.Visit(m.Object);
                this.builder.Append(UriHelper.SPACE);
                this.builder.Append(UriHelper.HAS);
                this.builder.Append(UriHelper.SPACE);
                this.Visit(m.Arguments[0]);
            }
            else
            {
                SequenceMethod sequenceMethod;
                if (ReflectionUtil.TryIdentifySequenceMethod(m.Method, out sequenceMethod))
                {
                    if (ReflectionUtil.IsAnyAllMethod(sequenceMethod))
                    {
                        // Raise the uriVersion each time we write any or all methods to the uri.
                        WebUtil.RaiseVersion(ref this.uriVersion, Util.ODataVersion4);

                        this.Visit(m.Arguments[0]);
                        this.builder.Append(UriHelper.FORWARDSLASH);
                        if (sequenceMethod == SequenceMethod.All)
                        {
                            this.builder.Append(XmlConstants.AllMethodName);
                        }
                        else
                        {
                            this.builder.Append(XmlConstants.AnyMethodName);
                        }

                        this.builder.Append(UriHelper.LEFTPAREN);
                        if (sequenceMethod != SequenceMethod.Any)
                        {
                            // SequenceMethod.Any represents Enumerable.Any(), which has only source argument
                            // AnyPredicate and All has a second parameter which is the predicate lambda.
                            Debug.Assert(m.Arguments.Count == 2, "m.Arguments.Count() == 2");
                            LambdaExpression le            = (LambdaExpression)m.Arguments[1];
                            string           rangeVariable = le.Parameters[0].Name;
                            this.builder.Append(rangeVariable);
                            this.builder.Append(UriHelper.COLON);
                            this.scopeCount++;
                            this.Visit(le.Body);
                            this.scopeCount--;
                        }

                        this.builder.Append(UriHelper.RIGHTPAREN);
                        return(m);
                    }
                    else if (sequenceMethod == SequenceMethod.OfType && this.parent != null)
                    {
                        // check to see if this is an OfType filter for Any or All.
                        // e.g. ctx.CreateQuery<Movie>("Movies").Where(m=>m.Actors.OfType<MegaStar>().Any())
                        //      which translates to /Movies()?$filter=Actors/MegaStar/any()
                        MethodCallExpression mce = this.parent as MethodCallExpression;
                        if (mce != null && ReflectionUtil.TryIdentifySequenceMethod(mce.Method, out sequenceMethod) && ReflectionUtil.IsAnyAllMethod(sequenceMethod))
                        {
                            Type filteredType = mce.Method.GetGenericArguments().SingleOrDefault();
                            if (ClientTypeUtil.TypeOrElementTypeIsEntity(filteredType))
                            {
                                this.Visit(m.Arguments[0]);
                                this.builder.Append(UriHelper.FORWARDSLASH);

                                UriHelper.AppendTypeSegment(this.builder, filteredType, this.context, this.inPath, ref this.uriVersion);

                                return(m);
                            }
                        }
                    }
                    else if (sequenceMethod == SequenceMethod.Count && this.parent != null)
                    {
                        if (m.Arguments.Any() && m.Arguments[0] != null)
                        {
                            this.Visit(m.Arguments[0]);
                        }

                        this.builder.Append(UriHelper.FORWARDSLASH).Append(UriHelper.DOLLARSIGN).Append(UriHelper.COUNT);
                        return(m);
                    }
                }
                else
                {
                    if (m.Object != null)
                    {
                        this.Visit(m.Object);
                    }

                    if (m.Method.Name != "GetValue" && m.Method.Name != "GetValueAsync")
                    {
                        this.builder.Append(UriHelper.FORWARDSLASH);

                        // writing functions in query options
                        writingFunctionsInQuery = true;
                        string declaringType = this.context.ResolveNameFromTypeInternal(m.Method.DeclaringType);
                        if (string.IsNullOrEmpty(declaringType))
                        {
                            throw new NotSupportedException(Strings.ALinq_CantTranslateExpression(m.ToString()));
                        }

                        int    index            = declaringType.LastIndexOf('.');
                        string fullNamespace    = declaringType.Remove(index + 1);
                        string serverMethodName = ClientTypeUtil.GetServerDefinedName(m.Method);
                        this.builder.Append(fullNamespace + serverMethodName);
                        this.builder.Append(UriHelper.LEFTPAREN);
                        string[] argumentNames = m.Method.GetParameters().Select(p => p.Name).ToArray();
                        for (int i = 0; i < m.Arguments.Count; ++i)
                        {
                            this.builder.Append(argumentNames[i]);
                            this.builder.Append(UriHelper.EQUALSSIGN);
                            this.scopeCount++;
                            this.Visit(m.Arguments[i]);
                            this.scopeCount--;
                            this.builder.Append(UriHelper.COMMA);
                        }

                        if (m.Arguments.Any())
                        {
                            this.builder.Remove(this.builder.Length - 1, 1);
                        }

                        this.builder.Append(UriHelper.RIGHTPAREN);
                        writingFunctionsInQuery = false;
                    }

                    return(m);
                }

                this.cantTranslateExpression = true;
            }

            return(m);
        }
        /// <summary>
        /// Serializes an UnaryExpression to a string
        /// </summary>
        /// <param name="u">Expression to serialize</param>
        /// <returns>UnaryExpression</returns>
        internal override Expression VisitUnary(UnaryExpression u)
        {
            switch (u.NodeType)
            {
            case ExpressionType.Not:
                this.builder.Append(UriHelper.NOT);
                this.builder.Append(UriHelper.SPACE);
                this.VisitOperand(u.Operand);
                break;

            case ExpressionType.Negate:
            case ExpressionType.NegateChecked:
                this.builder.Append(UriHelper.SPACE);
                this.builder.Append(UriHelper.NEGATE);
                this.VisitOperand(u.Operand);
                break;

            case ExpressionType.Convert:
            case ExpressionType.ConvertChecked:
                if (u.Type != typeof(object))
                {
                    if (IsEnumTypeExpression(u))
                    {
                        this.Visit(u.Operand);
                    }
                    else
                    {
                        this.builder.Append(UriHelper.CAST);
                        this.builder.Append(UriHelper.LEFTPAREN);
                        if (!this.IsImplicitInputReference(u.Operand))
                        {
                            this.Visit(u.Operand);
                            this.builder.Append(UriHelper.COMMA);
                        }

                        this.builder.Append(UriHelper.QUOTE);
                        this.builder.Append(UriHelper.GetTypeNameForUri(u.Type, this.context));
                        this.builder.Append(UriHelper.QUOTE);
                        this.builder.Append(UriHelper.RIGHTPAREN);
                    }
                }
                else
                {
                    if (!this.IsImplicitInputReference(u.Operand))
                    {
                        this.Visit(u.Operand);
                    }
                }

                break;

            case ExpressionType.TypeAs:
                if (u.Operand.NodeType == ExpressionType.TypeAs)
                {
                    throw new NotSupportedException(Strings.ALinq_CannotUseTypeFiltersMultipleTimes);
                }

                this.Visit(u.Operand);

                if (!this.IsImplicitInputReference(u.Operand))
                {
                    // InputReferenceExpressions aren't emitted, so no leading slash is required
                    this.builder.Append(UriHelper.FORWARDSLASH);
                }

                UriHelper.AppendTypeSegment(this.builder, u.Type, this.context, this.inPath, ref this.uriVersion);

                break;

            case ExpressionType.UnaryPlus:
                // no-op always ignore.
                break;

            default:
                this.cantTranslateExpression = true;
                break;
            }

            return(u);
        }
Exemple #3
0
        /// <summary>
        /// Enumerates through the list of URI operation parameters and creates a new Uri with the uri operation parameters written as query string of the new Uri.
        /// </summary>
        /// <param name="requestUri">The Uri used to construct the new Uri.</param>
        /// <param name="operationParameters">The non-empty list of uri parameters which will be converted to query string.</param>
        /// <returns>Uri containing the uri parameters as query string.</returns>
        internal Uri WriteUriOperationParametersToUri(Uri requestUri, List <UriOperationParameter> operationParameters)
        {
            Debug.Assert(operationParameters != null && operationParameters.Any(), "OperationParameters was null or empty");
            Debug.Assert(requestUri != null, "request_uri != null");

            UriBuilder    uriBuilder  = new UriBuilder(requestUri);
            StringBuilder pathBuilder = new StringBuilder();

            pathBuilder.Append(uriBuilder.Path);
            string lastSeg = uriBuilder.Path.Substring(uriBuilder.Path.LastIndexOf('/') + 1);

            StringBuilder queryBuilder = new StringBuilder();
            String        uriString    = UriUtil.UriToString(uriBuilder.Uri);

            if (!string.IsNullOrEmpty(uriBuilder.Query))
            {
                Debug.Assert(uriBuilder.Query[0] == UriHelper.QUESTIONMARK, "uriBuilder.Query[0] == UriHelper.QUESTIONMARK");

                // Don't append the '?', as later when we call setter on the Query, the '?' will be automatically added.
                queryBuilder.Append(uriBuilder.Query.Substring(1));
                queryBuilder.Append(UriHelper.AMPERSAND);
            }

            if (!lastSeg.Contains(Char.ToString(UriHelper.ATSIGN)))
            {
                pathBuilder.Append(UriHelper.LEFTPAREN);
            }
            else
            {
                if (pathBuilder.ToString().EndsWith(Char.ToString(UriHelper.RIGHTPAREN), StringComparison.OrdinalIgnoreCase))
                {
                    pathBuilder.Remove(pathBuilder.Length - 1, 1);
                    pathBuilder.Append(UriHelper.COMMA);
                }
            }

            foreach (UriOperationParameter op in operationParameters)
            {
                Debug.Assert(op != null, "op != null");
                Debug.Assert(!string.IsNullOrEmpty(op.Name), "!string.IsNullOrEmpty(op.ParameterName)");

                string paramName = op.Name.Trim();

                // if the parameter name is an alias, make sure that the URI contains it.
                if (paramName.StartsWith(Char.ToString(UriHelper.ATSIGN), StringComparison.OrdinalIgnoreCase) && !uriString.Contains(paramName))
                {
                    throw new DataServiceRequestException(Strings.Serializer_UriDoesNotContainParameterAlias(op.Name));
                }

                if (paramName.StartsWith(Char.ToString(UriHelper.ATSIGN), StringComparison.OrdinalIgnoreCase))
                {
                    // name=value&
                    queryBuilder.Append(paramName);
                    queryBuilder.Append(UriHelper.EQUALSSIGN);
                    queryBuilder.Append(this.ConvertToEscapedUriValue(paramName, op.Value));
                    queryBuilder.Append(UriHelper.AMPERSAND);
                }

                string value = this.ConvertToEscapedUriValue(paramName, op.Value);

                // non-primitive value, use alias.
                if (!UriHelper.IsPrimitiveValue(value))
                {
                    // name = @name
                    pathBuilder.Append(paramName);
                    pathBuilder.Append(UriHelper.EQUALSSIGN);
                    pathBuilder.Append(UriHelper.ENCODEDATSIGN);
                    pathBuilder.Append(paramName);
                    pathBuilder.Append(UriHelper.COMMA);

                    // @name = value&
                    queryBuilder.Append(UriHelper.ENCODEDATSIGN);
                    queryBuilder.Append(paramName);
                    queryBuilder.Append(UriHelper.EQUALSSIGN);
                    queryBuilder.Append(value);
                    queryBuilder.Append(UriHelper.AMPERSAND);
                }
                else
                {
                    // primitive value, do not use alias.
                    pathBuilder.Append(paramName);
                    pathBuilder.Append(UriHelper.EQUALSSIGN);
                    pathBuilder.Append(value);
                    pathBuilder.Append(UriHelper.COMMA);
                }
            }

            // remove the last extra comma.
            if (pathBuilder.ToString().EndsWith(Char.ToString(UriHelper.COMMA), StringComparison.OrdinalIgnoreCase))
            {
                Debug.Assert(pathBuilder.ToString().EndsWith(Char.ToString(UriHelper.COMMA), StringComparison.OrdinalIgnoreCase), "Uri was expected to end with an ampersand.");
                pathBuilder.Remove(pathBuilder.Length - 1, 1);
            }

            pathBuilder.Append(UriHelper.RIGHTPAREN);

            // remove the last extra ampersand.
            if (queryBuilder.ToString().EndsWith(Char.ToString(UriHelper.AMPERSAND), StringComparison.OrdinalIgnoreCase))
            {
                Debug.Assert(queryBuilder.ToString().EndsWith(Char.ToString(UriHelper.AMPERSAND), StringComparison.OrdinalIgnoreCase), "Uri was expected to end with an ampersand.");
                queryBuilder.Remove(queryBuilder.Length - 1, 1);
            }

            uriBuilder.Path  = pathBuilder.ToString();
            uriBuilder.Query = queryBuilder.ToString();

            return(uriBuilder.Uri);
        }