예제 #1
0
        public static List <MemberInfo> Find(LambdaExpression lambdaExpression)
        {
            var finder = new ParameterPathFinder(lambdaExpression.Parameters[0]);

            finder.Visit(lambdaExpression.Body);

            return(finder.result);
        }
        protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
        {
            if (!(methodCallExpression.Method.DeclaringType == typeof(Queryable) ||
                  methodCallExpression.Method.DeclaringType == typeof(Enumerable) ||
                  methodCallExpression.Method.DeclaringType == typeof(QueryableExtensions)))
            {
                return(base.VisitMethodCall(methodCallExpression));
            }

            var saveInsideSkipTake = this.insideSkipTake;

            if (methodCallExpression.Method.Name == "Skip" || methodCallExpression.Method.Name == "Take")
            {
                this.insideSkipTake = true;

                try
                {
                    var retval = base.VisitMethodCall(methodCallExpression);

                    if (!saveInsideSkipTake)
                    {
                        foreach (var includeSelector in this.includeSelectors)
                        {
                            retval = Expression.Call(MethodInfoFastRef.QueryableExtensionsIncludeMethod.MakeGenericMethod(retval.Type.GetGenericArguments()[0], includeSelector.Body.Type), retval, includeSelector);
                        }
                    }

                    return(retval);
                }
                finally
                {
                    this.insideSkipTake = saveInsideSkipTake;
                }
            }
            else if (methodCallExpression.Method.Name == "Select" && this.insideSkipTake)
            {
                var lambda = methodCallExpression.Arguments[1].StripQuotes();

                var saveSelector = this.selector;

                if (this.selector != null)
                {
                    var body = SqlExpressionReplacer.Replace(this.selector.Body, this.selector.Parameters[0], lambda.Body);

                    this.selector = Expression.Lambda(body, lambda.Parameters[0]);
                }
                else
                {
                    this.selector = lambda;
                }

                try
                {
                    return(base.VisitMethodCall(methodCallExpression));
                }
                finally
                {
                    this.selector = saveSelector;
                }
            }
            else if (methodCallExpression.Method.Name == "Include" && this.insideSkipTake)
            {
                var source = Visit(methodCallExpression.Arguments[0]);

                List <MemberInfo> path;

                if (this.selector == null)
                {
                    path = new List <MemberInfo>();
                }
                else
                {
                    path = ParameterPathFinder.Find(this.selector);
                }

                if (path == null)
                {
                    ParameterPathFinder.Find(this.selector);

                    return(source);
                }

                bool IsRelatedObjectsMemberExpression(Expression expression)
                {
                    return(expression is MemberExpression memberExpression &&
                           memberExpression.Member.GetMemberReturnType().IsGenericType &&
                           memberExpression.Member.GetMemberReturnType().GetGenericTypeDefinition() == typeof(RelatedDataAccessObjects <>));
                }

                var includeSelector = methodCallExpression.Arguments[1].StripQuotes();
                var includesRelatedDataAccessObjects = SqlExpressionFinder.FindExists(includeSelector.Body, IsRelatedObjectsMemberExpression);

                if (includesRelatedDataAccessObjects)
                {
                    // Create a new include selector adjusting for any additional member accesses necessary because of select projections

                    var oldParam = includeSelector.Parameters[0];
                    var oldBody  = includeSelector.Body;

                    var newParam = path.Count > 0 ? Expression.Parameter(path.First().DeclaringType) : oldParam;

                    var replacement = path.Aggregate((Expression)newParam, Expression.MakeMemberAccess, c => c);

                    var newBody = SqlExpressionReplacer.Replace(oldBody, oldParam, replacement);

                    this.includeSelectors.Add(Expression.Lambda(newBody, newParam));
                }

                return(source);
            }

            return(base.VisitMethodCall(methodCallExpression));
        }