Пример #1
0
        protected Expression RewriteBasicProjection(MethodCallExpression methodCallExpression, bool forSelector)
        {
            Expression[] originalSelectors;
            var          originalSource = methodCallExpression.Arguments[0];
            var          source         = this.Visit(originalSource);
            var          sourceType     = source.Type.GetGenericArguments()[0];
            var          originalPredicateOrSelector = methodCallExpression.Arguments[1];

            if (methodCallExpression.Arguments.Count == 2)
            {
                originalSelectors = new[] { originalPredicateOrSelector };
            }
            else
            {
                originalSelectors = new[] { originalPredicateOrSelector, methodCallExpression.Arguments[2] };
            }

            var sourceParameterExpression = (originalPredicateOrSelector.StripQuotes()).Parameters[0];
            var result = ReferencedRelatedObjectPropertyGatherer.Gather(this.model, originalSelectors, sourceParameterExpression, forSelector);
            var memberAccessExpressionsNeedingJoins = result.ReferencedRelatedObjectByPath;
            var currentRootExpressionsByPath        = result.RootExpressionsByPath;

            var predicateOrSelectors       = result.ReducedExpressions;
            var predicateOrSelectorLambdas = predicateOrSelectors.Select(c => c.StripQuotes()).ToArray();

            if (memberAccessExpressionsNeedingJoins.Count > 0)
            {
                var replacementExpressionForPropertyPath = new Dictionary <PropertyPath, Expression>(PropertyPathEqualityComparer.Default);

                var referencedObjectPaths = memberAccessExpressionsNeedingJoins
                                            .OrderBy(c => c.Key.Length)
                                            .Select(c => c.Value)
                                            .ToList();

                var types = referencedObjectPaths
                            .Select(c => c.FullAccessPropertyPath.Last.PropertyType)
                            .ToList();

                var finalTupleType = CreateFinalTupleType(sourceType, types);
                var replacementExpressionsByPropertyPathForSelector = new Dictionary <PropertyPath, Expression>(PropertyPathEqualityComparer.Default);
                var parameter = Expression.Parameter(finalTupleType);

                var i           = 1;
                var indexByPath = new Dictionary <PropertyPath, int>(PropertyPathEqualityComparer.Default);

                foreach (var value in referencedObjectPaths)
                {
                    indexByPath[value.FullAccessPropertyPath] = i++;
                }

                indexByPath[PropertyPath.Empty] = 0;

                foreach (var x in currentRootExpressionsByPath)
                {
                    indexByPath[x.Key] = 0;
                }

                foreach (var path in referencedObjectPaths.Select(c => c.FullAccessPropertyPath))
                {
                    var replacement = CreateExpressionForPath(referencedObjectPaths.Count, path, parameter, indexByPath);

                    replacementExpressionsByPropertyPathForSelector[path] = replacement;
                }

                replacementExpressionsByPropertyPathForSelector[PropertyPath.Empty] = CreateExpressionForPath(referencedObjectPaths.Count, PropertyPath.Empty, parameter, indexByPath);

                foreach (var value in replacementExpressionsByPropertyPathForSelector)
                {
                    replacementExpressionForPropertyPath[value.Key] = value.Value;
                }

                var propertyPathsByOriginalExpression = referencedObjectPaths
                                                        .SelectMany(d => d.TargetExpressions.Select(e => new { PropertyPath = d.FullAccessPropertyPath, Expression = e }))
                                                        .ToDictionary(c => c.Expression, c => c.PropertyPath);

                foreach (var lambda in predicateOrSelectorLambdas)
                {
                    propertyPathsByOriginalExpression[lambda.Parameters[0]] = PropertyPath.Empty;
                }

                var replacementExpressions = propertyPathsByOriginalExpression
                                             .ToDictionary(c => c.Key, c => replacementExpressionsByPropertyPathForSelector[c.Value]);

                var index       = 1;
                var currentLeft = source;

                foreach (var referencedObjectPath in referencedObjectPaths)
                {
                    var property = referencedObjectPath.FullAccessPropertyPath[referencedObjectPath.FullAccessPropertyPath.Length - 1];
                    var right    = Expression.Constant(this.model.GetDataAccessObjects(property.PropertyType), typeof(DataAccessObjects <>).MakeGenericType(property.PropertyType));

                    var join = MakeJoinCallExpression(index, currentLeft, right, referencedObjectPath.FullAccessPropertyPath, indexByPath, currentRootExpressionsByPath, sourceParameterExpression);

                    currentLeft = join;
                    index++;
                }

                Func <Expression, bool, Expression> replace = null;

                replace = (e, b) => SqlExpressionReplacer.Replace(e, c =>
                {
                    Expression value;

                    if (forSelector && b)
                    {
                        if (result.IncludedPropertyInfoByExpression.ContainsKey(c))
                        {
                            var x = replace(c, false);
                            var y = result.IncludedPropertyInfoByExpression[c];

                            var newList = y.Select(includedPropertyInfo => new IncludedPropertyInfo
                            {
                                RootExpression         = x,
                                FullAccessPropertyPath = includedPropertyInfo.FullAccessPropertyPath,
                                IncludedPropertyPath   = includedPropertyInfo.IncludedPropertyPath
                            }).ToList();

                            this.includedPropertyInfos[x] = newList;

                            return(x);
                        }
                    }

                    if (replacementExpressions.TryGetValue(c, out value))
                    {
                        return(value);
                    }

                    return(null);
                });

                var newPredicatorOrSelectorBodies = predicateOrSelectorLambdas.Select(c => replace(c.Body, true)).ToArray();
                var newPredicateOrSelectors       = newPredicatorOrSelectorBodies.Select(c => Expression.Lambda(c, parameter)).ToArray();

                MethodInfo           newMethod;
                MethodCallExpression newCall;
                var newParameterType = newPredicateOrSelectors[0].Parameters[0].Type;

                if (methodCallExpression.Method.Name.StartsWith("Select") ||
                    methodCallExpression.Method.Name.StartsWith("Where") ||
                    methodCallExpression.Method.Name.EqualsIgnoreCase("OrderBy"))
                {
                    if (methodCallExpression.Method.Name.StartsWith("Select"))
                    {
                        var projectionResultType = newPredicateOrSelectors[0].ReturnType;

                        newMethod = methodCallExpression.Method.GetGenericMethodDefinition().MakeGenericMethod(newParameterType, projectionResultType);

                        newCall = Expression.Call(null, newMethod, new[]
                        {
                            currentLeft,
                            newPredicateOrSelectors[0]
                        });
                    }
                    else if (methodCallExpression.Method.Name.StartsWith("Where"))
                    {
                        newMethod = methodCallExpression.Method.GetGenericMethodDefinition().MakeGenericMethod(newParameterType);

                        newCall = Expression.Call(null, newMethod, new[]
                        {
                            currentLeft,
                            newPredicateOrSelectors[0]
                        });
                    }
                    else if (methodCallExpression.Method.Name.StartsWith("OrderBy"))
                    {
                        var keyType = newPredicateOrSelectors[0].ReturnType;

                        newMethod = methodCallExpression.Method.GetGenericMethodDefinition().MakeGenericMethod(newParameterType, keyType);

                        newCall = Expression.Call(null, newMethod, new[]
                        {
                            currentLeft,
                            newPredicateOrSelectors[0]
                        });
                    }
                    else
                    {
                        throw new InvalidOperationException();
                    }

                    if (newCall.Method.ReturnType.GetGenericArguments()[0].IsGenericType &&
                        newCall.Method.ReturnType.GetGenericArguments()[0].GetGenericTypeDefinition() == typeof(LeftRightJoinInfo <,>))
                    {
                        var selectParameter = Expression.Parameter(newCall.Method.ReturnType.GetGenericArguments()[0]);
                        var selectBody      = CreateExpressionForPath(referencedObjectPaths.Count, PropertyPath.Empty, selectParameter, indexByPath);
                        var selectCall      = Expression.Lambda(selectBody, selectParameter);

                        var selectMethod = MethodInfoFastRef.QueryableSelectMethod.MakeGenericMethod
                                           (
                            selectParameter.Type,
                            selectCall.ReturnType
                                           );

                        newCall = Expression.Call(null, selectMethod, new Expression[] { newCall, selectCall });
                    }

                    this.replacementExpressionForPropertyPathsByJoin.Add(new Tuple <Expression, Dictionary <PropertyPath, Expression> >(newCall, replacementExpressionForPropertyPath));
                }
                else if (methodCallExpression.Method.Name == ("GroupBy"))
                {
                    var keyType     = newPredicateOrSelectors[0].ReturnType;
                    var elementType = methodCallExpression.Method.ReturnType.GetGenericArguments()[0].GetGenericArguments()[1];

                    newMethod = methodCallExpression.Method
                                .DeclaringType
                                .GetMethods().Single(c => c.IsGenericMethod &&
                                                     c.GetGenericArguments().Length == 3 &&
                                                     c.GetParameters().Length == 3 &&
                                                     c.GetParameters()[1].ParameterType.IsGenericType &&
                                                     c.GetParameters()[2].ParameterType.IsGenericType &&
                                                     c.GetParameters()[1].ParameterType.GetGenericTypeDefinition() == typeof(Expression <>) &&
                                                     c.GetParameters()[2].ParameterType.GetGenericTypeDefinition() == typeof(Expression <>) &&
                                                     c.GetParameters()[1].ParameterType.GetGenericArguments()[0].IsGenericType &&
                                                     c.GetParameters()[2].ParameterType.GetGenericArguments()[0].IsGenericType &&
                                                     c.GetParameters()[1].ParameterType.GetGenericArguments()[0].GetGenericTypeDefinition() == typeof(Func <,>) &&
                                                     c.GetParameters()[2].ParameterType.GetGenericArguments()[0].GetGenericTypeDefinition() == typeof(Func <,>))
                                .MakeGenericMethod(newParameterType, keyType, elementType);

                    var elementSelectorParameter = Expression.Parameter(newParameterType);
                    var elementSelectorBody      = CreateExpressionForPath(referencedObjectPaths.Count, PropertyPath.Empty, elementSelectorParameter, indexByPath);
                    var elementSelector          = Expression.Lambda(elementSelectorBody, elementSelectorParameter);

                    newCall = Expression.Call(null, newMethod, new []
                    {
                        currentLeft,
                        newPredicateOrSelectors[0],
                        elementSelector
                    });
                }
                else
                {
                    throw new InvalidOperationException("Method: " + methodCallExpression.Method);
                }

                this.replacementExpressionForPropertyPathsByJoin.Add(new Tuple <Expression, Dictionary <PropertyPath, Expression> >(newCall, replacementExpressionForPropertyPath));

                return(newCall);
            }
            else
            {
                if (source == originalSource &&
                    predicateOrSelectors.SequenceEqual(originalSelectors, ObjectReferenceIdentityEqualityComparer <Expression> .Default))
                {
                    return(methodCallExpression);
                }
                else
                {
                    return(Expression.Call
                           (
                               methodCallExpression.Object,
                               methodCallExpression.Method,
                               predicateOrSelectors.Prepend(source).ToArray()
                           ));
                }
            }
        }
        protected RewriteBasicProjectionResults RewriteBasicProjection(Expression originalSource, Tuple <LambdaExpression, ParameterExpression>[] originalSelectors, bool forProjection)
        {
            var source     = this.Visit(originalSource);
            var sourceType = source.Type.GetGenericArguments()[0];

            var result = ReferencedRelatedObjectPropertyGatherer.Gather(this.model, originalSelectors.Select(c => new Tuple <ParameterExpression, Expression>(c.Item2, c.Item1)).ToList(), forProjection);
            var memberAccessExpressionsNeedingJoins = result.ReferencedRelatedObjects;
            var currentRootExpressionsByPath        = result.RootExpressionsByPath;

            var predicateOrSelectors       = result.ReducedExpressions.Select(c => c.StripQuotes()).ToArray();
            var predicateOrSelectorLambdas = predicateOrSelectors.Select(c => c.StripQuotes()).ToArray();

            if (memberAccessExpressionsNeedingJoins.Count == 0)
            {
                return(new RewriteBasicProjectionResults
                {
                    NewSource = source,
                    NewSelectors = predicateOrSelectors.ToList(),
                    ReferencedObjectPaths = new List <ReferencedRelatedObject>()
                });
            }

            var replacementExpressionForPropertyPath = new Dictionary <PropertyPath, Expression>(PropertyPathEqualityComparer.Default);

            var referencedObjectPaths = memberAccessExpressionsNeedingJoins
                                        .Where(c => !c.IncludedPropertyPath.Any(d => d.PropertyType.IsDataAccessObjectType()))
                                        .OrderBy(c => c.FullAccessPropertyPath.Length)
                                        .Concat(memberAccessExpressionsNeedingJoins
                                                .Where(c => c.IncludedPropertyPath.Any(d => d.PropertyType.IsDataAccessObjectType()))
                                                .OrderBy(c => c.FullAccessPropertyPath.Length))
                                        .ToList();

            var referencedPathsWithCollectionIncludes = memberAccessExpressionsNeedingJoins
                                                        .Where(c => c.FullAccessPropertyPath.Last.PropertyType.GetGenericTypeDefinitionOrNull() == typeof(RelatedDataAccessObjects <>))
                                                        .Select((c, x) => new { index = x, path = c })
                                                        .OrderBy(c => c.index)
                                                        .ToList();

            var types = referencedObjectPaths
                        .Select(c => c.FullAccessPropertyPath.Last.PropertyType.JoinedObjectType())
                        .ToList();

            var finalTupleType = CreateFinalTupleType(sourceType, types);
            var replacementExpressionsByPropertyPathForSelector = new Dictionary <PropertyPath, Expression>(PropertyPathEqualityComparer.Default);
            var parameter = Expression.Parameter(finalTupleType);

            var i           = 1;
            var indexByPath = new Dictionary <PropertyPath, int>(PropertyPathEqualityComparer.Default);

            foreach (var value in referencedObjectPaths)
            {
                indexByPath[value.FullAccessPropertyPath] = i++;
            }

            indexByPath[PropertyPath.Empty] = 0;

            foreach (var x in currentRootExpressionsByPath)
            {
                indexByPath[x.Key] = 0;
            }

            foreach (var path in referencedObjectPaths.Select(c => c.FullAccessPropertyPath))
            {
                var replacement = CreateExpressionForPath(referencedObjectPaths.Count, path, parameter, indexByPath);

                replacementExpressionsByPropertyPathForSelector[path] = replacement;
            }

            replacementExpressionsByPropertyPathForSelector[PropertyPath.Empty] = CreateExpressionForPath(referencedObjectPaths.Count, PropertyPath.Empty, parameter, indexByPath);

            foreach (var value in replacementExpressionsByPropertyPathForSelector)
            {
                replacementExpressionForPropertyPath[value.Key] = value.Value;
            }

            var propertyPathsByOriginalExpression = referencedObjectPaths
                                                    .SelectMany(d => d.TargetExpressions.Select(e => new { PropertyPath = d.FullAccessPropertyPath, Expression = e }))
                                                    .ToDictionary(c => c.Expression, c => c.PropertyPath);

            foreach (var lambda in predicateOrSelectorLambdas.Select((c, idx) => new { index = idx, value = c }))
            {
                var parameterLocation = originalSelectors[lambda.index].Item1.Parameters.IndexOf(originalSelectors[lambda.index].Item2);

                propertyPathsByOriginalExpression[lambda.value.Parameters[parameterLocation]] = PropertyPath.Empty;
            }

            var replacementExpressions = propertyPathsByOriginalExpression
                                         .ToDictionary(c => c.Key, c => replacementExpressionsByPropertyPathForSelector[c.Value]);

            var index       = 1;
            var currentLeft = source;

            foreach (var referencedObjectPath in referencedObjectPaths)
            {
                var property = referencedObjectPath.FullAccessPropertyPath[referencedObjectPath.FullAccessPropertyPath.Length - 1];
                var right    = Expression.Constant(this.model.GetDataAccessObjects(property.PropertyType.JoinedObjectType()), typeof(DataAccessObjects <>).MakeGenericType(property.PropertyType.JoinedObjectType()));

                var join = MakeJoinCallExpression(index, currentLeft, right, referencedObjectPath.FullAccessPropertyPath, indexByPath, currentRootExpressionsByPath, referencedObjectPath.SourceParameterExpression);

                currentLeft = join;
                index++;
            }

            Func <Expression, bool, Expression> replace = null;

            replace = (e, b) => SqlExpressionReplacer.Replace(e, c =>
            {
                if (forProjection && b)
                {
                    if (result.IncludedPropertyInfoByExpression.ContainsKey(c))
                    {
                        var x = replace(c, false);
                        var y = result.IncludedPropertyInfoByExpression[c];

                        var newList = y.Select(includedPropertyInfo => new IncludedPropertyInfo
                        {
                            RootExpression         = x,
                            FullAccessPropertyPath = includedPropertyInfo.FullAccessPropertyPath,
                            IncludedPropertyPath   = includedPropertyInfo.IncludedPropertyPath
                        }).ToList();

                        this.includedPropertyInfos[x] = newList;

                        return(x);
                    }
                }

                return(replacementExpressions.GetValueOrDefault(c));
            });

            var newPredicatorOrSelectorBodies = predicateOrSelectorLambdas
                                                .Select(c => replace(c.Body, true))
                                                .ToList();

            var newPredicateOrSelectors = newPredicatorOrSelectorBodies
                                          .Zip(originalSelectors, (x, y) => new { body = x, parameters = y.Item1.Parameters, sourceParameter = y.Item2 })
                                          .Select(c => Expression.Lambda(c.body, c.parameters.Select(d => d == c.sourceParameter ? parameter : c.sourceParameter)))
                                          .ToList();

            return(new RewriteBasicProjectionResults(true)
            {
                NewSource = currentLeft,
                IndexByPath = indexByPath,
                NewSelectors = newPredicateOrSelectors,
                ReferencedObjectPaths = referencedObjectPaths,
                ReplacementExpressionsByPropertyPath = replacementExpressionForPropertyPath
            });
        }