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); }
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 var propertyAccessor = Expression.Property(accum, "Key"); if (this._groupingProperties != null && this._groupingProperties.Any()) { var wrapperProperty = this.ResultClrType.GetProperty("GroupByContainer"); wrapperTypeMemberAssignments.Add(Expression.Bind(wrapperProperty, Expression.Property(Expression.Property(accum, "Key"), "GroupByContainer"))); } // 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))); } 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); }
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); }
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, // 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.ComputeClause.ComputedItems) { properties.Add(new NamedPropertyExpression(Expression.Constant(computeExpression.Alias), CreateComputeExpression(computeExpression))); } // 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); }
/// <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 begining 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; this._elementType = wrapperType; } return(query); }