示例#1
0
 /// <summary>
 /// Looks for a method call expression of the form
 ///     Select(entity => Convert(entity, DerivedType))
 /// If found, returns DerivedType.
 /// </summary>
 /// <param name="callExpression">Expression to check for pattern match.</param>
 /// <param name="convertType">If the match was found, this is the type used in the Convert, otherwise null.</param>
 /// <returns>True if the expression matches the desired pattern, otherwise false.</returns>
 private static bool TryMatchSelectWithConvert(MethodCallExpression callExpression, out Type convertType)
 {
     convertType = null;
     return(ReflectionUtil.IsSequenceMethod(callExpression.Method, SequenceMethod.Select) &&
            TryMatchConvertSingleArgument(callExpression.Arguments[1], out convertType));
 }
示例#2
0
        internal override Expression VisitMethodCall(MethodCallExpression m)
        {
            string         str;
            SequenceMethod method;

            if (TypeSystem.TryGetQueryOptionMethod(m.Method, out str))
            {
                this.builder.Append(str);
                this.builder.Append('(');
                if (str == "substringof")
                {
                    this.Visit(m.Arguments[0]);
                    this.builder.Append(',');
                    this.Visit(m.Object);
                }
                else
                {
                    if (m.Object != null)
                    {
                        this.Visit(m.Object);
                    }
                    if (m.Arguments.Count > 0)
                    {
                        if (m.Object != null)
                        {
                            this.builder.Append(',');
                        }
                        for (int i = 0; i < m.Arguments.Count; i++)
                        {
                            this.Visit(m.Arguments[i]);
                            if (i < (m.Arguments.Count - 1))
                            {
                                this.builder.Append(',');
                            }
                        }
                    }
                }
                this.builder.Append(')');
                return(m);
            }
            if (ReflectionUtil.TryIdentifySequenceMethod(m.Method, out method))
            {
                if (ReflectionUtil.IsAnyAllMethod(method))
                {
                    WebUtil.RaiseVersion(ref this.uriVersion, Util.DataServiceVersion3);
                    this.Visit(m.Arguments[0]);
                    this.builder.Append('/');
                    if (method == SequenceMethod.All)
                    {
                        this.builder.Append("all");
                    }
                    else
                    {
                        this.builder.Append("any");
                    }
                    this.builder.Append('(');
                    if (method != SequenceMethod.Any)
                    {
                        LambdaExpression expression = (LambdaExpression)m.Arguments[1];
                        string           name       = expression.Parameters[0].Name;
                        this.builder.Append(name);
                        this.builder.Append(':');
                        this.scopeCount++;
                        this.Visit(expression.Body);
                        this.scopeCount--;
                    }
                    this.builder.Append(')');
                    return(m);
                }
                if ((method == SequenceMethod.OfType) && (this.parent != null))
                {
                    MethodCallExpression parent = this.parent as MethodCallExpression;
                    if (((parent != null) && ReflectionUtil.TryIdentifySequenceMethod(parent.Method, out method)) && ReflectionUtil.IsAnyAllMethod(method))
                    {
                        Type type = parent.Method.GetGenericArguments().SingleOrDefault <Type>();
                        if (ClientTypeUtil.TypeOrElementTypeIsEntity(type))
                        {
                            this.Visit(m.Arguments[0]);
                            this.builder.Append('/');
                            this.builder.Append(System.Data.Services.Client.UriHelper.GetEntityTypeNameForUriAndValidateMaxProtocolVersion(type, this.context, ref this.uriVersion));
                            return(m);
                        }
                    }
                }
            }
            this.cantTranslateExpression = true;
            return(m);
        }
示例#3
0
        /// <summary>
        /// Identifies and normalizes a Select method call expression of the following form:
        ///     Select(x => Convert(x))
        /// If a match is found, it is translated into a Cast() call.
        ///
        /// This supports type casting in queries like the following:
        ///     from DerivedType entity in context.Entities
        ///     select entity
        /// Where DerivedType is derived from the type of context.Entities.
        /// The pattern also applies to SelectMany calls with the same structure as above.
        ///
        /// In C#, the type cast above is represented as a Cast call and the ResourceBinder knows how to handle that.
        /// In VB, the same query is translated into Select(x => Convert(x)) instead of Cast, and the ResourceBinder
        /// doesn't recognize that pattern. This normalization allows the two queries to be treated the same.
        /// </summary>
        /// <param name="callExpression">MethodCallExpression to potentially normalize.</param>
        /// <returns>
        /// If the query pattern was found, a Cast call is returned with the same source as the original Select and
        /// a cast type that is the same as the original Convert expression.
        /// If no normalization is required, the original MethodCallExpression is returned without changes.
        /// </returns>
        private static MethodCallExpression NormalizeSelectWithTypeCast(MethodCallExpression callExpression)
        {
            Type convertType;

            if (TryMatchSelectWithConvert(callExpression, out convertType))
            {
                // Find the Cast method on the same type where the Select method was declared
                MethodInfo castMethodInfo = callExpression.Method.DeclaringType.GetMethod("Cast", true /*isPublic*/, true /*isStatic*/);
                if (castMethodInfo != null && castMethodInfo.IsGenericMethodDefinition && ReflectionUtil.IsSequenceMethod(castMethodInfo, SequenceMethod.Cast))
                {
                    MethodInfo genericCastMethodInfo = castMethodInfo.MakeGenericMethod(convertType);
                    return(Expression.Call(genericCastMethodInfo, callExpression.Arguments[0]));
                }
            }

            // nothing has changed
            return(callExpression);
        }
示例#4
0
        /// <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, 'substringof', 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 == "substringof")
                {
                    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.Arguments[0]);
                    this.builder.Append(UriHelper.COMMA);
                    this.Visit(m.Object);
                }
                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
            {
                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.DataServiceVersion3);

                        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);
                            }
                        }
                    }
                }

                this.cantTranslateExpression = true;
            }

            return(m);
        }