Beispiel #1
0
        private static Type GetNamedPropertyType(NamedPropertyExpression property, IList <Expression> expressions)
        {
            Type namedPropertyGenericType;

            if (property.NullCheck != null)
            {
                namedPropertyGenericType = SingleExpandedPropertyTypes[expressions.Count];
            }
            else if (property.PageSize != null || property.CountOption != null)
            {
                namedPropertyGenericType = CollectionExpandedPropertyTypes[expressions.Count];
            }
            else if (property.AutoSelected)
            {
                namedPropertyGenericType = AutoSelectedNamedPropertyTypes[expressions.Count];
            }
            else
            {
                namedPropertyGenericType = NamedPropertyTypes[expressions.Count];
            }

            Type elementType = (property.PageSize == null && property.CountOption == null)
                ? property.Value.Type
                : TypeHelper.GetInnerElementType(property.Value.Type);

            return(namedPropertyGenericType.MakeGenericType(elementType));
        }
        private static Expression CreateNextNamedPropertyCreationExpression(NamedPropertyExpression property, Expression next)
        {
            Contract.Assert(property != null);
            Contract.Assert(property.Value != null);

            Type namedPropertyType = null;

            if (next != null)
            {
                if (property.Value.Type == typeof(GroupByWrapper))
                {
                    namedPropertyType = typeof(NestedProperty);
                }
                else
                {
                    namedPropertyType = typeof(AggregationPropertyContainer);
                }
            }
            else
            {
                if (property.Value.Type == typeof(GroupByWrapper))
                {
                    namedPropertyType = typeof(NestedPropertyLastInChain);
                }
                else
                {
                    namedPropertyType = typeof(LastInChain);
                }
            }

            List <MemberBinding> memberBindings = new List <MemberBinding>();

            memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Name"), property.Name));

            if (property.Value.Type == typeof(GroupByWrapper))
            {
                memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("NestedValue"), property.Value));
            }
            else
            {
                memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Value"), property.Value));
            }

            if (next != null)
            {
                memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Next"), next));
            }

            if (property.NullCheck != null)
            {
                memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("IsNull"), property.NullCheck));
            }

            return(Expression.MemberInit(Expression.New(namedPropertyType), memberBindings));
        }
Beispiel #3
0
        // Expression:
        // new NamedProperty<T> { Name = property.Name, Value = property.Value, Next0 = next0, Next1 = next1, .... }.
        private static Expression CreateNamedPropertyCreationExpression(NamedPropertyExpression property, IList <Expression> expressions)
        {
            Contract.Assert(property != null);
            Contract.Assert(property.Value != null);

            Type namedPropertyType = GetNamedPropertyType(property, expressions);
            List <MemberBinding> memberBindings = new List <MemberBinding>();

            memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Name"), property.Name));

            if (property.PageSize != null || property.CountOption != null)
            {
                memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Collection"), property.Value));

                if (property.PageSize != null)
                {
                    memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("PageSize"),
                                                       Expression.Constant(property.PageSize)));
                }

                if (property.CountOption != null && property.CountOption.Value)
                {
                    memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("TotalCount"), ExpressionHelpers.ToNullable(property.TotalCount)));
                }
            }
            else
            {
                memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Value"), property.Value));
            }

            for (int i = 0; i < expressions.Count; i++)
            {
                memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Next" + i), expressions[i]));
            }

            if (property.NullCheck != null)
            {
                memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("IsNull"), property.NullCheck));
            }

            return(Expression.MemberInit(Expression.New(namedPropertyType), memberBindings));
        }
Beispiel #4
0
        // Expression:
        //      new NamedProperty<T>
        //      {
        //          Name = properties[0].Key,
        //          Value = properties[0].Value,
        //
        //          Next0 = new NamedProperty<> { ..... }
        //          Next1 = new NamedProperty<> { ..... },
        //          ...
        //      }
        public static Expression CreatePropertyContainer(IList <NamedPropertyExpression> properties)
        {
            Expression container = null;

            // build the linked list of properties.
            if (properties.Count >= 1)
            {
                NamedPropertyExpression property = properties.First();
                int count = properties.Count - 1;
                List <Expression> nextExpressions = new List <Expression>();
                int parts  = SingleExpandedPropertyTypes.Count - 1;
                int offset = 0;
                for (int step = parts; step > 0; step--)
                {
                    int leftSize = GetLeftSize(count - offset, step);
                    nextExpressions.Add(CreatePropertyContainer(properties.Skip(1 + offset).Take(leftSize).ToList()));
                    offset += leftSize;
                }

                container = CreateNamedPropertyCreationExpression(property, nextExpressions.Where(e => e != null).ToList());
            }

            return(container);
        }
Beispiel #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 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);
        }
        private Expression BuildPropertyContainer(IEdmEntityType elementType, Expression source,
                                                  Dictionary <IEdmNavigationProperty, ExpandedReferenceSelectItem> propertiesToExpand,
                                                  ISet <IEdmStructuralProperty> propertiesToInclude, ISet <IEdmStructuralProperty> autoSelectedProperties, bool isSelectingOpenTypeSegments)
        {
            IList <NamedPropertyExpression> includedProperties = new List <NamedPropertyExpression>();

            foreach (KeyValuePair <IEdmNavigationProperty, ExpandedReferenceSelectItem> kvp in propertiesToExpand)
            {
                IEdmNavigationProperty      propertyToExpand = kvp.Key;
                ExpandedReferenceSelectItem expandItem       = kvp.Value;

                SelectExpandClause projection = GetOrCreateSelectExpandClause(kvp);

                ModelBoundQuerySettings querySettings = EdmLibHelpers.GetModelBoundQuerySettings(propertyToExpand,
                                                                                                 propertyToExpand.ToEntityType(),
                                                                                                 _context.Model);

                Expression propertyName  = CreatePropertyNameExpression(elementType, propertyToExpand, source);
                Expression propertyValue = CreatePropertyValueExpressionWithFilter(elementType, propertyToExpand, source,
                                                                                   expandItem.FilterOption);
                Expression nullCheck = GetNullCheckExpression(propertyToExpand, propertyValue, projection);

                Expression countExpression = CreateTotalCountExpression(propertyValue, expandItem);

                // projection can be null if the expanded navigation property is not further projected or expanded.
                if (projection != null)
                {
                    int?modelBoundPageSize = querySettings == null ? null : querySettings.PageSize;
                    propertyValue = ProjectAsWrapper(propertyValue, projection, propertyToExpand.ToEntityType(), expandItem.NavigationSource, expandItem, modelBoundPageSize);
                }

                NamedPropertyExpression propertyExpression = new NamedPropertyExpression(propertyName, propertyValue);
                if (projection != null)
                {
                    if (!propertyToExpand.Type.IsCollection())
                    {
                        propertyExpression.NullCheck = nullCheck;
                    }
                    else if (_settings.PageSize.HasValue)
                    {
                        propertyExpression.PageSize = _settings.PageSize.Value;
                    }
                    else
                    {
                        if (querySettings != null && querySettings.PageSize.HasValue)
                        {
                            propertyExpression.PageSize = querySettings.PageSize.Value;
                        }
                    }

                    propertyExpression.TotalCount  = countExpression;
                    propertyExpression.CountOption = expandItem.CountOption;
                }

                includedProperties.Add(propertyExpression);
            }

            foreach (IEdmStructuralProperty propertyToInclude in propertiesToInclude)
            {
                Expression propertyName  = CreatePropertyNameExpression(elementType, propertyToInclude, source);
                Expression propertyValue = CreatePropertyValueExpression(elementType, propertyToInclude, source);
                includedProperties.Add(new NamedPropertyExpression(propertyName, propertyValue));
            }

            foreach (IEdmStructuralProperty propertyToInclude in autoSelectedProperties)
            {
                Expression propertyName  = CreatePropertyNameExpression(elementType, propertyToInclude, source);
                Expression propertyValue = CreatePropertyValueExpression(elementType, propertyToInclude, source);
                includedProperties.Add(new NamedPropertyExpression(propertyName, propertyValue)
                {
                    AutoSelected = true
                });
            }

            if (isSelectingOpenTypeSegments)
            {
                var dynamicPropertyDictionary = EdmLibHelpers.GetDynamicPropertyDictionary(elementType, _model);
                if (dynamicPropertyDictionary != null)
                {
                    Expression propertyName          = Expression.Constant(dynamicPropertyDictionary.Name);
                    Expression propertyValue         = Expression.Property(source, dynamicPropertyDictionary.Name);
                    Expression nullablePropertyValue = ExpressionHelpers.ToNullable(propertyValue);
                    if (_settings.HandleNullPropagation == HandleNullPropagationOption.True)
                    {
                        // source == null ? null : propertyValue
                        propertyValue = Expression.Condition(
                            test: Expression.Equal(source, Expression.Constant(value: null)),
                            ifTrue: Expression.Constant(value: null, type: TypeHelper.ToNullable(propertyValue.Type)),
                            ifFalse: nullablePropertyValue);
                    }
                    else
                    {
                        propertyValue = nullablePropertyValue;
                    }

                    includedProperties.Add(new NamedPropertyExpression(propertyName, propertyValue));
                }
            }

            // create a property container that holds all these property names and values.
            return(PropertyContainer.CreatePropertyContainer(includedProperties));
        }