Пример #1
0
        public IQueryable Bind(IQueryable query)
        {
            PreprocessQuery(query);
            // compute(X add Y as Z, A mul B as C) adds new properties to the output
            // Should return following expression
            // .Select($it => new ComputeWrapper<T> {
            //      Instance = $it,
            //      ModelID = 'Guid',
            //      Container => new AggregationPropertyContainer() {
            //          Name = "X",
            //          Value = $it.X + $it.Y,
            //          Next = new LastInChain() {
            //              Name = "C",
            //              Value = $it.A * $it.B
            //      }
            // })

            List <MemberAssignment> wrapperTypeMemberAssignments = new List <MemberAssignment>();

            // Set Instance property
            var wrapperProperty = this.ResultClrType.GetProperty("Instance");

            wrapperTypeMemberAssignments.Add(Expression.Bind(wrapperProperty, this.LambdaParameter));
            var properties = new List <NamedPropertyExpression>();

            foreach (var computeExpression in this._transformation.Expressions)
            {
                properties.Add(new NamedPropertyExpression(Expression.Constant(computeExpression.Alias), CreateComputeExpression(computeExpression)));
            }

            // Initialize property 'ModelID' on the wrapper class.
            // source = new Wrapper { ModelID = 'some-guid-id' }
            wrapperProperty = this.ResultClrType.GetProperty("ModelID");
            var wrapperPropertyValueExpression = QuerySettings.EnableConstantParameterization ?
                                                 LinqParameterContainer.Parameterize(typeof(string), _modelID) :
                                                 Expression.Constant(_modelID);

            wrapperTypeMemberAssignments.Add(Expression.Bind(wrapperProperty, wrapperPropertyValueExpression));

            // Set new compute properties
            wrapperProperty = ResultClrType.GetProperty("Container");
            wrapperTypeMemberAssignments.Add(Expression.Bind(wrapperProperty, AggregationPropertyContainer.CreateNextNamedPropertyContainer(properties)));

            var initilizedMember =
                Expression.MemberInit(Expression.New(ResultClrType), wrapperTypeMemberAssignments);
            var selectLambda = Expression.Lambda(initilizedMember, this.LambdaParameter);

            var result = ExpressionHelpers.Select(query, selectLambda, this.ElementType);

            return(result);
        }
Пример #2
0
        public IQueryable Bind(IQueryable query)
        {
            PreprocessQuery(query);
            // compute(X add Y as Z, A mul B as C) adds new properties to the output
            // Should return following expression
            // .Select($it => new ComputeWrapper<T> {
            //      Instance = $it,
            //      Model = parametrized(IEdmModel),
            //      Container => new AggregationPropertyContainer() {
            //          Name = "X",
            //          Value = $it.X + $it.Y,
            //          Next = new LastInChain() {
            //              Name = "C",
            //              Value = $it.A * $it.B
            //      }
            // })

            List <MemberAssignment> wrapperTypeMemberAssignments = new List <MemberAssignment>();

            // Set Instance property
            var wrapperProperty = this.ResultClrType.GetProperty("Instance");

            wrapperTypeMemberAssignments.Add(Expression.Bind(wrapperProperty, this.LambdaParameter));
            var properties = new List <NamedPropertyExpression>();

            foreach (var computeExpression in this._transformation.Expressions)
            {
                properties.Add(new NamedPropertyExpression(Expression.Constant(computeExpression.Alias), CreateComputeExpression(computeExpression)));
            }

            // Initialize property 'Model' on the wrapper class.
            // source = new Wrapper { Model = parameterized(a-edm-model) }
            // Always parameterize as EntityFramework does not let you inject non primitive constant values (like IEdmModel).
            wrapperProperty = this.ResultClrType.GetProperty("Model");
            var wrapperPropertyValueExpression = LinqParameterContainer.Parameterize(typeof(IEdmModel), _model);

            wrapperTypeMemberAssignments.Add(Expression.Bind(wrapperProperty, wrapperPropertyValueExpression));

            // Set new compute properties
            wrapperProperty = ResultClrType.GetProperty("Container");
            wrapperTypeMemberAssignments.Add(Expression.Bind(wrapperProperty, AggregationPropertyContainer.CreateNextNamedPropertyContainer(properties)));

            var initilizedMember =
                Expression.MemberInit(Expression.New(ResultClrType), wrapperTypeMemberAssignments);
            var selectLambda = Expression.Lambda(initilizedMember, this.LambdaParameter);

            var result = ExpressionHelpers.Select(query, selectLambda, this.ElementType);

            return(result);
        }
Пример #3
0
        private IQueryable BindSelect(IQueryable grouping)
        {
            // Should return following expression
            // .Select($it => New DynamicType2()
            //                  {
            //                      GroupByContainer = $it.Key.GroupByContainer // If groupby section present
            //                      Container => new AggregationPropertyContainer() {
            //                          Name = "Alias1",
            //                          Value = $it.AsQuaryable().Sum(i => i.AggregatableProperty),
            //                          Next = new LastInChain() {
            //                              Name = "Alias2",
            //                              Value = $it.AsQuaryable().Sum(i => i.AggregatableProperty)
            //                          }
            //                      }
            //                  })
            var groupingType          = typeof(IGrouping <,>).MakeGenericType(this._groupByClrType, this.ElementType);
            ParameterExpression accum = Expression.Parameter(groupingType, "$it");

            List <MemberAssignment> wrapperTypeMemberAssignments = new List <MemberAssignment>();

            // Setting GroupByContainer property when previous step was grouping
            if (this._groupingProperties != null && this._groupingProperties.Any())
            {
                var wrapperProperty = this.ResultClrType.GetProperty(GroupByContainerProperty);

                wrapperTypeMemberAssignments.Add(Expression.Bind(wrapperProperty, Expression.Property(Expression.Property(accum, "Key"), GroupByContainerProperty)));
            }

            // Setting Container property when we have aggregation clauses
            if (_aggregateExpressions != null)
            {
                var properties = new List <NamedPropertyExpression>();
                foreach (var aggExpression in _aggregateExpressions)
                {
                    properties.Add(new NamedPropertyExpression(Expression.Constant(aggExpression.Alias), CreateAggregationExpression(accum, aggExpression, this.ElementType)));
                }

                var wrapperProperty = ResultClrType.GetProperty("Container");
                wrapperTypeMemberAssignments.Add(Expression.Bind(wrapperProperty, AggregationPropertyContainer.CreateNextNamedPropertyContainer(properties)));
            }

            var initilizedMember =
                Expression.MemberInit(Expression.New(ResultClrType), wrapperTypeMemberAssignments);
            var selectLambda = Expression.Lambda(initilizedMember, accum);

            var result = ExpressionHelpers.Select(grouping, selectLambda, groupingType);

            return(result);
        }
Пример #4
0
        private IQueryable BindSelect(IQueryable grouping)
        {
            // Should return following expression
            // .Select($it => New DynamicType2()
            //                  {
            //                      Prop1 = $it.Prop1,
            //                      Prop2 = $it.Prop2,
            //                      Prop3 = $it.NavProp.Prop3
            //                      ...
            //                      Alias1 = $it.AsQuaryable().Sum(i => i.AggregatableProperty)
            //                  })

            var groupingType          = typeof(IGrouping <,>).MakeGenericType(this._groupByClrType, this._elementType);
            ParameterExpression accum = Expression.Parameter(groupingType, "$it");

            List <MemberAssignment> wrapperTypeMemberAssignments = null;

            // Setting GroupByContainer property when previous step was grouping
            var propertyAccessor = Expression.Property(accum, "Key");

            wrapperTypeMemberAssignments = CreateSelectMemberAssigments(ResultClrType, propertyAccessor, _groupingProperties);

            // Setting Container property when we have aggregation clauses
            if (_aggregateStatements != null)
            {
                foreach (var aggStatement in _aggregateStatements)
                {
                    wrapperTypeMemberAssignments.Add(Expression.Bind(ResultClrType.GetMember(aggStatement.Alias).Single(), CreateAggregationExpression(accum, aggStatement)));
                }
            }

            var selectLambda = Expression.Lambda(Expression.MemberInit(Expression.New(ResultClrType), wrapperTypeMemberAssignments), accum);

            var result = ExpressionHelpers.Select(grouping, selectLambda, groupingType);

            return(result);
        }
Пример #5
0
        /// <summary>
        /// Pre flattens properties referenced in aggregate clause to avoid generation of nested queries by EF.
        /// For query like groupby((A), aggregate(B/C with max as Alias1, B/D with max as Alias2)) we need to generate
        /// .Select(
        ///     $it => new FlattenninWrapper () {
        ///         Source = $it, // Will used in groupby stage
        ///         Container = new {
        ///             Value = $it.B.C
        ///             Next = new {
        ///                 Value = $it.B.D
        ///             }
        ///         }
        ///     }
        /// )
        /// Also we need to populate expressions to access B/C and B/D in aggregate stage. It will look like:
        /// B/C : $it.Container.Value
        /// B/D : $it.Container.Next.Value
        /// </summary>
        /// <param name="query"></param>
        /// <returns>Query with Select that flattens properties</returns>
        private IQueryable FlattenReferencedProperties(IQueryable query)
        {
            if (_aggregateExpressions != null &&
                _aggregateExpressions.OfType <AggregateExpression>().Any(e => e.Method != AggregationMethod.VirtualPropertyCount) &&
                _groupingProperties != null &&
                _groupingProperties.Any() &&
                (FlattenedPropertyContainer == null || !FlattenedPropertyContainer.Any()))
            {
                var wrapperType             = typeof(FlatteningWrapper <>).MakeGenericType(this.ElementType);
                var sourceProperty          = wrapperType.GetProperty("Source");
                List <MemberAssignment> wta = new List <MemberAssignment>();
                wta.Add(Expression.Bind(sourceProperty, this.LambdaParameter));

                var aggrregatedPropertiesToFlatten = _aggregateExpressions.OfType <AggregateExpression>().Where(e => e.Method != AggregationMethod.VirtualPropertyCount).ToList();
                // Generated Select will be stack like, meaning that first property in the list will be deepest one
                // For example if we add $it.B.C, $it.B.D, select will look like
                // new {
                //      Value = $it.B.C
                //      Next = new {
                //          Value = $it.B.D
                //      }
                // }
                // We are generated references (in currentContainerExpression) from  the beginning of the  Select ($it.Value, then $it.Next.Value etc.)
                // We have proper match we need insert properties in reverse order
                // After this
                // properties = { $it.B.D, $it.B.C}
                // _preFlattendMAp = { {$it.B.C, $it.Value}, {$it.B.D, $it.Next.Value} }
                var properties = new NamedPropertyExpression[aggrregatedPropertiesToFlatten.Count];
                var aliasIdx   = aggrregatedPropertiesToFlatten.Count - 1;
                var aggParam   = Expression.Parameter(wrapperType, "$it");
                var currentContainerExpression = Expression.Property(aggParam, GroupByContainerProperty);
                foreach (var aggExpression in aggrregatedPropertiesToFlatten)
                {
                    var alias = "Property" + aliasIdx.ToString(CultureInfo.CurrentCulture); // We just need unique alias, we aren't going to use it

                    // Add Value = $it.B.C
                    var propAccessExpression = BindAccessor(aggExpression.Expression);
                    var type = propAccessExpression.Type;
                    propAccessExpression = WrapConvert(propAccessExpression);
                    properties[aliasIdx] = new NamedPropertyExpression(Expression.Constant(alias), propAccessExpression);

                    // Save $it.Container.Next.Value for future use
                    UnaryExpression flatAccessExpression = Expression.Convert(
                        Expression.Property(currentContainerExpression, "Value"),
                        type);
                    currentContainerExpression = Expression.Property(currentContainerExpression, "Next");
                    _preFlattenedMap.Add(aggExpression.Expression, flatAccessExpression);
                    aliasIdx--;
                }

                var wrapperProperty = ResultClrType.GetProperty(GroupByContainerProperty);

                wta.Add(Expression.Bind(wrapperProperty, AggregationPropertyContainer.CreateNextNamedPropertyContainer(properties)));

                var flatLambda = Expression.Lambda(Expression.MemberInit(Expression.New(wrapperType), wta), LambdaParameter);

                query = ExpressionHelpers.Select(query, flatLambda, this.ElementType);

                // We applied flattening let .GroupBy know about it.
                this.LambdaParameter = aggParam;
            }

            return(query);
        }