/// <summary> /// Translates Joins from type (1) to type (2) /// 1) Join(outer, inner, c => c.outerKey, c => d.innerKey, (x, y) => new { x.a.b.c, y.a.b.c }) /// 2) Join(outer, inner, c => c.outerKey, c => d.innerKey, (x, y) => new { x, y }).Select(c => new { c.x.a.b.c, c.y.a.b.v }) /// </summary> protected Expression RewriteExplicitJoinProjection(MethodCallExpression methodCallExpression) { var outer = Visit(methodCallExpression.Arguments[0]); var inner = Visit(methodCallExpression.Arguments[1]); var outerKeySelector = methodCallExpression.Arguments[2].StripQuotes(); var innerKeySelector = methodCallExpression.Arguments[3].StripQuotes(); var resultSelector = methodCallExpression.Arguments[4].StripQuotes(); var originalOuterKeyParam = resultSelector.StripQuotes().Parameters[0]; var originalInnerKeyParam = resultSelector.StripQuotes().Parameters[1]; var outerKey = Expression.Parameter(outerKeySelector.Parameters[0].Type); var innerKey = Expression.Parameter(innerKeySelector.Parameters[0].Type); var resultValue = Expression.Parameter(typeof(ExpandedJoinSelectKey <,>).MakeGenericType(outerKey.Type, innerKey.Type)); var newResultSelector = Expression.Lambda(Expression.MemberInit(resultValue.Type.CreateNewExpression(), Expression.Bind(resultValue.Type.GetProperty("Outer"), outerKey), Expression.Bind(resultValue.Type.GetProperty("Inner"), innerKey)), outerKey, innerKey); var newJoin = Expression.Call(MethodInfoFastRef.QueryableJoinMethod.MakeGenericMethod(outer.Type.GetSequenceElementType() ?? outer.Type, inner.Type.GetSequenceElementType() ?? inner.Type, outerKeySelector.ReturnType, newResultSelector.ReturnType), outer, inner, outerKeySelector, innerKeySelector, newResultSelector); var selectorParameter = Expression.Parameter(resultValue.Type); var selectProjectorBody = SqlExpressionReplacer.Replace(resultSelector.Body, originalOuterKeyParam, Expression.Property(selectorParameter, "Outer")); selectProjectorBody = SqlExpressionReplacer.Replace(selectProjectorBody, originalInnerKeyParam, Expression.Property(selectorParameter, "Inner")); var selectProjector = Expression.Lambda(selectProjectorBody, selectorParameter); var select = Expression.Call(MethodInfoFastRef.QueryableSelectMethod.MakeGenericMethod(selectorParameter.Type, selectProjector.ReturnType), newJoin, selectProjector); return(select); }
protected Expression RewriteSelectManyProjection(MethodCallExpression methodCallExpression) { var outer = Visit(methodCallExpression.Arguments[0]); var collection = Visit(methodCallExpression.Arguments[1]); var resultSelector = methodCallExpression.Arguments.Count == 3 ? methodCallExpression.Arguments[2].StripQuotes() : null; var originalSelectorA = resultSelector.StripQuotes().Parameters[0]; var originalSelectorB = resultSelector.StripQuotes().Parameters[1]; var newA = Expression.Parameter(originalSelectorA.Type); var newB = Expression.Parameter(originalSelectorB.Type); var resultValue = Expression.Parameter(typeof(ExpandedJoinSelectKey <,>).MakeGenericType(originalSelectorA.Type, originalSelectorB.Type)); var newResultSelector = Expression.Lambda(Expression.MemberInit(resultValue.Type.CreateNewExpression(), Expression.Bind(resultValue.Type.GetProperty("Outer"), newA), Expression.Bind(resultValue.Type.GetProperty("Inner"), newB)), newA, newB); var newSelectMany = Expression.Call(MethodInfoFastRef.QueryableSelectManyMethod.MakeGenericMethod(methodCallExpression.Method.GetGenericArguments()[0], methodCallExpression.Method.GetGenericArguments()[1], newResultSelector.ReturnType), outer, collection, newResultSelector); var selectorParameter = Expression.Parameter(resultValue.Type); var selectProjectorBody = SqlExpressionReplacer.Replace(resultSelector?.Body ?? selectorParameter, originalSelectorA, Expression.Property(selectorParameter, "Outer")); selectProjectorBody = SqlExpressionReplacer.Replace(selectProjectorBody, originalSelectorB, Expression.Property(selectorParameter, "Inner")); var selectProjector = Expression.Lambda(selectProjectorBody, selectorParameter); var select = Expression.Call(MethodInfoFastRef.QueryableSelectMethod.MakeGenericMethod(selectorParameter.Type, selectProjector.ReturnType), newSelectMany, selectProjector); return(select); }
public static SubstituteConstantsResultWithValues SubstituteConstants(Expression expression) { object[] args = null; SubstituteConstantsResult result; if (!cachedSubstitutedExpressions.TryGetValue(expression, out result)) { var values = new List <object>(); var parameters = new List <ParameterExpression>(); var replacement = SqlExpressionReplacer.Replace(expression, c => { if (c.NodeType != ExpressionType.Constant) { return(null); } var constantExpression = (ConstantExpression)c; values.Add(constantExpression.Value); var parameter = Expression.Parameter(constantExpression.Type); parameters.Add(parameter); return(parameter); }); args = values.ToArray(); var parametersArray = parameters.ToArray(); result = new SubstituteConstantsResult(replacement, parametersArray); cachedSubstitutedExpressions = cachedSubstitutedExpressions.Clone(expression, result, "cachedSubstitutedExpressions"); } if (args == null) { var i = 0; args = new object[result.AdditionalParameters.Length]; SqlExpressionReplacer.Replace(expression, c => { if (c.NodeType != ExpressionType.Constant) { return(null); } var constantExpression = (ConstantExpression)c; args[i++] = constantExpression.Value; return(null); }); } return(new SubstituteConstantsResultWithValues(result, args)); }
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { if (methodCallExpression.Method.GetGenericMethodOrRegular() != MethodInfoFastRef.QueryableExtensionsIncludeMethod) { return(base.VisitMethodCall(methodCallExpression)); } var saveAlreadyInsideInclude = this.alreadyInsideInclude; this.alreadyInsideInclude = true; try { var parameter = Expression.Parameter(methodCallExpression.Method.GetGenericArguments()[0]); var currentIncludeSelector = Visit(methodCallExpression.Arguments[1]).StripQuotes(); if (methodCallExpression.Arguments[0].Type.GetGenericTypeDefinitionOrNull() == typeof(RelatedDataAccessObjects <>)) { var source = Visit(methodCallExpression.Arguments[0]); var includedItems = Expression.Call(QueryableExtensions.IncludedItemsMethod.MakeGenericMethod(source.Type.GetSequenceElementType()), source); if (saveAlreadyInsideInclude) { // Instead of (1) we unwrap and get (2) which ReferencedRelatedObjectPropertyGatherer can process // (See: Test_Include_Two_Level_Of_Collections2) // // Include(c => c.Shops.Include(d => d.Toys.Include(e => e.Shop.Mall.Shops2))) // // 1) c.Shops.IncludedItems().IncludeDirect(d => d.Toys.IncludedItems().IncludeDirect(e => e.Shop.Mall.Shops2)) // 2) c.Shops.IncludedItems().Toys.IncludedItems().Shop.Mall.Shops2 var result = SqlExpressionReplacer.Replace(currentIncludeSelector.Body, currentIncludeSelector.Parameters[0], includedItems); return(result); } else { var result = Expression.Call(MethodInfoFastRef.DataAccessObjectExtensionsIncludeMethod.MakeGenericMethod(parameter.Type, currentIncludeSelector.ReturnType), includedItems, currentIncludeSelector); return(result); } } else { var body = Expression.Call(MethodInfoFastRef.DataAccessObjectExtensionsIncludeMethod.MakeGenericMethod(parameter.Type, currentIncludeSelector.ReturnType), parameter, currentIncludeSelector); var selector = Expression.Lambda(body, parameter); var source = Visit(methodCallExpression.Arguments[0]); var selectCall = Expression.Call(MethodInfoFastRef.QueryableSelectMethod.MakeGenericMethod(parameter.Type, parameter.Type), source, selector); return(selectCall); } } finally { this.alreadyInsideInclude = saveAlreadyInsideInclude; } }
public static Expression Collate(Expression expression) { var visitor = new SqlIncludeExpressionCollator(); var result = visitor.Visit(expression); foreach (var keyValue in visitor.includeExpressionsBySource) { var source = keyValue.Key; var expressions = keyValue.Value; var newSource = Expression.Call(MethodInfoFastRef.QueryableExtensionsIncludeManyMethod.MakeGenericMethod(source.Type.GetSequenceElementType()), source, Expression.NewArrayInit(typeof(LambdaExpression), expressions)); result = SqlExpressionReplacer.Replace(result, source, newSource); } return(result); }
public static SqlReferencedRelatedObjectPropertyGathererResults Gather(DataAccessModel model, IList <Tuple <ParameterExpression, Expression> > expressions, bool forProjection) { var gatherer = new SqlReferencedRelatedObjectPropertyGatherer(model, null, forProjection); var reducedExpressions = expressions.Select(c => { gatherer.sourceParameterExpression = c.Item1; return(SqlExpressionReplacer.Replace(gatherer.Visit(c.Item2), d => d.StripItemsCalls())); }).ToArray(); return(new SqlReferencedRelatedObjectPropertyGathererResults { ReducedExpressions = reducedExpressions, ReferencedRelatedObjects = gatherer.results.Values.ToList(), RootExpressionsByPath = gatherer.rootExpressionsByPath, IncludedPropertyInfoByExpression = gatherer .includedPropertyInfos .GroupBy(c => c.RootExpression) .ToDictionary(c => c.Key, c => c.ToList()) }); }
protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { if (methodCallExpression.Method.GetGenericMethodOrRegular() == MethodInfoFastRef.QueryableSelectMethod) { this.expressionByParameter[methodCallExpression.Arguments[1].StripQuotes().Parameters[0]] = methodCallExpression.Arguments[0]; return(base.VisitMethodCall(methodCallExpression)); } if (methodCallExpression.Method.GetGenericMethodOrRegular() == MethodInfoFastRef.DataAccessObjectExtensionsIncludeMethod) { if (!this.forProjection) { throw new InvalidOperationException(); } var args0 = methodCallExpression.Arguments[0]; var selector = methodCallExpression.Arguments[1].StripQuotes(); var newSelector = SqlExpressionReplacer.Replace(selector.Body, selector.Parameters[0], args0); var originalReferencedRelatedObjects = this.referencedRelatedObjects; var originalParent = this.currentParent; this.currentParent = methodCallExpression.Arguments[0].StripItemsCalls(); this.referencedRelatedObjects = new List <ReferencedRelatedObject>(); this.nesting++; Visit(newSelector); if (this.referencedRelatedObjects.Count == 0) { return(Visit(methodCallExpression.Arguments[0])); } var referencedRelatedObject = this.referencedRelatedObjects[0]; this.referencedRelatedObjects = originalReferencedRelatedObjects; this.currentParent = originalParent; var retval = Visit(methodCallExpression.Arguments[0].StripItemsCalls()); if (this.nesting > 1 && (retval != this.sourceParameterExpression) && retval is MemberExpression) { // For supporting: Select(c => c.Include(d => d.Address.Include(e => e.Region))) var prefixProperties = new List <PropertyInfo>(); var current = (MemberExpression)retval.StripItemsCalls(); while (current != null) { if (!current.Member.ReflectedType.IsTypeRequiringJoin() || current == this.currentParent) { break; } prefixProperties.Add((PropertyInfo)current.Member); if (current.Expression.StripItemsCalls() == this.sourceParameterExpression) { break; } current = current.Expression.StripItemsCalls() as MemberExpression; } prefixProperties.Reverse(); AddIncludedProperty(this.sourceParameterExpression, referencedRelatedObject, new PropertyPath(c => c.Name, prefixProperties)); } else { AddIncludedProperty(retval, referencedRelatedObject, PropertyPath.Empty); } this.nesting--; return(retval); } return(base.VisitMethodCall(methodCallExpression)); }
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)); }