/// <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);
        }
Exemplo n.º 3
0
        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));
        }
Exemplo n.º 4
0
        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;
            }
        }
Exemplo n.º 5
0
        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));
        }