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