/// <summary>Visits a method call.</summary>
        /// <param name="m">The method call to visit.</param>
        /// <returns>The visited expression.</returns>
        internal override Expression VisitMethodCall(MethodCallExpression m)
        {
            // Select - we should mark it with ResourceType of the return type of the Func
            var selectMatch = ExpressionUtil.MatchSelectCall(m);

            if (selectMatch != null)
            {
                Expression result = base.VisitMethodCall(m);
                selectMatch = ExpressionUtil.MatchSelectCall(result);
                if (selectMatch != null)
                {
                    return(this.annotations.PropagateResourceType(selectMatch.LambdaBody, result));
                }
            }

            // SelectMany - we should mark it with ResourceType of the return type of the Func
            var selectManyMatch = ExpressionUtil.MatchSelectManyCall(m);

            if (selectManyMatch != null)
            {
                Expression result = base.VisitMethodCall(m);
                selectManyMatch = ExpressionUtil.MatchSelectManyCall(result);
                if (selectManyMatch != null)
                {
                    return(this.annotations.PropagateResourceType(selectManyMatch.LambdaBody, result));
                }
            }

            // Where - simply propagate the resource type of the source as it doesn't change the results (just filters them)
            var whereMatch = ExpressionUtil.MatchWhereCall(m);

            if (whereMatch != null)
            {
                Expression result = base.VisitMethodCall(m);
                whereMatch = ExpressionUtil.MatchWhereCall(result);
                if (whereMatch != null)
                {
                    return(this.annotations.PropagateResourceType(whereMatch.Source, result));
                }
            }

            // OrderBy/ThenBy - simply propagate the resource type of the source as it doesn't change the results (just sorts them)
            var orderByMatch = ExpressionUtil.MatchOrderByCall(m) ?? ExpressionUtil.MatchThenByCall(m);

            if (orderByMatch != null)
            {
                Expression result = base.VisitMethodCall(m);
                orderByMatch = ExpressionUtil.MatchOrderByCall(result) ?? ExpressionUtil.MatchThenByCall(result);
                if (orderByMatch != null)
                {
                    return(this.annotations.PropagateResourceType(orderByMatch.Source, result));
                }
            }

            return(base.VisitMethodCall(m));
        }
        /// <summary>
        /// ConditionalExpression visit method
        /// </summary>
        /// <param name="c">The ConditionalExpression expression to visit</param>
        /// <returns>
        /// The visited ConditionalExpression expression
        /// </returns>
        internal override Expression VisitConditional(ConditionalExpression c)
        {
            var nullEqualConditionalMatch = ExpressionUtil.MatchNullEqualConditional(c);

            if (nullEqualConditionalMatch != null &&
                nullEqualConditionalMatch.ComparisonOperand.Type == typeof(String))
            {
                if (((MemberExpression)nullEqualConditionalMatch.ComparisonOperand).Member.Name == ((MemberExpression)((MethodCallExpression)((UnaryExpression)nullEqualConditionalMatch.IfNotNull).Operand).Object).Member.Name)
                {
                    return(ExpressionUtil.RemoveConversionsAndTypeAs(nullEqualConditionalMatch.IfNotNull));
                }
            }

            return(base.VisitConditional(c));
        }
        /// <summary>
        /// Optimizes the filter contains expressions.
        /// </summary>
        /// <param name="expr">The expr.</param>
        /// <returns></returns>
        private static Expression OptimizeFilterContainsExpressions(Expression expr)
        {
            var nullEqualConditionalMatch = ExpressionUtil.MatchNullEqualConditional(expr);

            if (nullEqualConditionalMatch != null &&
                nullEqualConditionalMatch.ComparisonOperand.Type == typeof(String))
            {
                if (((MemberExpression)nullEqualConditionalMatch.ComparisonOperand).Member.Name == ((MemberExpression)((MethodCallExpression)((UnaryExpression)nullEqualConditionalMatch.IfNotNull).Operand).Object).Member.Name)
                {
                    return(ExpressionUtil.RemoveConversionsAndTypeAs(nullEqualConditionalMatch.IfNotNull));
                }
            }

            return(expr);
        }
        /// <summary>
        /// Visits the method call skip projections.
        /// </summary>
        /// <param name="m">The m.</param>
        /// <returns></returns>
        protected Expression VisitMethodCallSkipProjections(MethodCallExpression m)
        {
            var selectMatch = ExpressionUtil.MatchSelectCall(m);

            if (selectMatch != null)
            {
                Expression source = this.Visit(selectMatch.Source);
                return(this.Annotations.PropagateResourceType(
                           selectMatch.Lambda,
                           Expression.Call(selectMatch.MethodCall.Method, source, selectMatch.Lambda)));
            }
            else
            {
                return(base.VisitMethodCall(m));
            }
        }
        /// <summary>
        /// Visits the method call.
        /// </summary>
        /// <param name="m">The m.</param>
        /// <returns></returns>
        internal override Expression VisitMethodCall(MethodCallExpression m)
        {
            var whereMatch = ExpressionUtil.MatchWhereCall(m);

            if (whereMatch != null)
            {
                Expression       source = this.Visit(whereMatch.Source);
                LambdaExpression lambda = Expression.Lambda(
                    OptimizeFilterNullableBooleanToCondition(this.Visit(whereMatch.LambdaBody)),
                    whereMatch.Lambda.Parameters.ToArray());

                return(Expression.Call(
                           whereMatch.MethodCall.Method,
                           source,
                           lambda));
            }

            return(VisitMethodCallSkipProjections(m));
        }
 /// <summary>Determines if the <paramref name="expr"/> is a call to ThenBy.</summary>
 /// <param name="expr">Expression to inspect.</param>
 /// <returns>Instance of the <see cref="OrderByCallMatch"/> class if the <paramref name="expr"/> is a ThenBy call,
 /// or null otherwise.</returns>
 internal static OrderByCallMatch MatchThenByCall(Expression expr)
 {
     if (expr.NodeType == ExpressionType.Call && TypeSystem.IsMethodLinqThenBy(((MethodCallExpression)expr).Method))
     {
         MethodCallExpression call   = (MethodCallExpression)expr;
         LambdaExpression     lambda = (LambdaExpression)ExpressionUtil.RemoveQuotes(call.Arguments[1]);
         Expression           body   = ExpressionUtil.RemoveQuotes(lambda.Body);
         return(new OrderByCallMatch
         {
             MethodCall = call,
             Source = call.Arguments[0],
             Lambda = lambda,
             LambdaBody = body
         });
     }
     else
     {
         return(null);
     }
 }
        /// <summary>
        /// Optimizes the filter nullable boolean to condition.
        /// </summary>
        /// <param name="expr">The expr.</param>
        /// <returns></returns>
        private static Expression OptimizeFilterNullableBooleanToCondition(Expression expr)
        {
            if (expr.Type == typeof(bool))
            {
                var nullEqualConditionalMatch = ExpressionUtil.MatchNullEqualConditional(expr);
                if (nullEqualConditionalMatch != null &&
                    nullEqualConditionalMatch.ComparisonOperand.Type == typeof(bool?) &&
                    ExpressionUtil.IsFalseConstant(nullEqualConditionalMatch.IfNull) &&
                    ExpressionUtil.IsMemberAccess(nullEqualConditionalMatch.IfNotNull, "Value"))
                {
                    // (c == null) ? false : c.Value    (bool?)c
                    // =>
                    // c == (bool?)true
                    return(Expression.Equal(
                               nullEqualConditionalMatch.ComparisonOperand,
                               Expression.Constant(true, typeof(bool?))));
                }
            }

            return(expr);
        }
        /// <summary>Visits method call expression.</summary>
        /// <param name="m">The method call expression.</param>
        /// <returns>The visited expression.</returns>
        internal override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m.Method.Name == "Where")
            {
                Expression       source    = this.Visit(m.Arguments[0]);
                Expression       lambdaArg = this.Visit(m.Arguments[1]);
                LambdaExpression lambda    = (LambdaExpression)ExpressionUtil.RemoveQuotes(lambdaArg);
                Expression       body      = ExpressionUtil.RemoveQuotes(lambda.Body);
                Expression       newBody;
                newBody = this.SimplifyBoolComparison(body);
                if (newBody != null)
                {
                    return(Expression.Call(
                               m.Method,
                               source,
                               Expression.Lambda(newBody, lambda.Parameters.ToArray())));
                }
            }

            return(base.VisitMethodCall(m));
        }
        /// <summary>Visits a method call.</summary>
        /// <param name="m">The method call to visit.</param>
        /// <returns>The visited expression.</returns>
        internal override Expression VisitMethodCall(MethodCallExpression m)
        {
            if (m.Method == GetValueMethodInfo)
            {
                ResourceProperty property = (ResourceProperty)((ConstantExpression)m.Arguments[1]).Value;
                return(this.Annotations.AnnotateResourceProperty(
                           Expression.Property(this.Visit(m.Arguments[0]), property.Name),
                           property));
            }

            if (m.Method.IsGenericMethod && m.Method.GetGenericMethodDefinition() == GetSequenceValueMethodInfo)
            {
                ResourceProperty property = (ResourceProperty)((ConstantExpression)m.Arguments[1]).Value;
                return(this.Annotations.AnnotateResourceProperty(
                           Expression.Property(this.Visit(m.Arguments[0]), property.Name),
                           property));
            }

            if (m.Method == ConvertMethodInfo)
            {
                ResourceType type = (ResourceType)((ConstantExpression)m.Arguments[1]).Value;
                return(this.Annotations.AnnotateResourceType(
                           Expression.Convert(this.Visit(m.Arguments[0]), type.InstanceType),
                           type));
            }

            if (m.Method == TypeIsMethodInfo)
            {
                ResourceType type = (ResourceType)((ConstantExpression)m.Arguments[1]).Value;
                return(Expression.TypeIs(
                           this.Visit(m.Arguments[0]),
                           type.InstanceType));
            }

            var selectMatch = ExpressionUtil.MatchSelectCall(m);

            if (selectMatch != null)
            {
                // Annotate the source first
                Expression source = this.Visit(selectMatch.Source);

                // Annotate the lambda parameter with the source type
                this.Annotations.PropagateResourceType(
                    source,
                    selectMatch.Lambda.Parameters[0]);

                // Now annotate the lambda
                LambdaExpression lambda = (LambdaExpression)this.Visit(selectMatch.Lambda);
                Expression       body   = lambda.Body;
                return(this.Annotations.PropagateResourceType(
                           body,
                           Expression.Call(selectMatch.MethodCall.Method, source, lambda)));
            }

            var selectManyMatch = ExpressionUtil.MatchSelectManyCall(m);

            if (selectManyMatch != null)
            {
                // Annotate the lambda parameter with the source type
                this.Annotations.PropagateResourceType(
                    selectManyMatch.Source,
                    selectManyMatch.Lambda.Parameters[0]);
                return(base.VisitMethodCall(m));
            }

            var whereMatch = ExpressionUtil.MatchWhereCall(m);

            if (whereMatch != null)
            {
                // Annotate the lambda parameter with the source type
                this.Annotations.PropagateResourceType(
                    whereMatch.Source,
                    whereMatch.Lambda.Parameters[0]);
                return(base.VisitMethodCall(m));
            }

            var orderByMatch = ExpressionUtil.MatchOrderByCall(m) ?? ExpressionUtil.MatchThenByCall(m);

            if (orderByMatch != null)
            {
                // Annotate the lambda parameter with the source type
                this.Annotations.PropagateResourceType(
                    orderByMatch.Source,
                    orderByMatch.Lambda.Parameters[0]);
                return(base.VisitMethodCall(m));
            }

            return(base.VisitMethodCall(m));
        }