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 : property.Value.Type.GetInnerElementType(); return(namedPropertyGenericType.MakeGenericType(elementType)); }
// Expression: // new NamedProperty<T> { Name = property.Name, Value = property.Value, Next = next }. private static Expression CreateNamedPropertyCreationExpression(NamedPropertyExpression property, Expression next) { Contract.Assert(property != null); Contract.Assert(property.Value != null); Type namedPropertyType = GetNamedPropertyType(property, next); List <MemberBinding> memberBindings = new List <MemberBinding>(); memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Name"), property.Name)); if (property.PageSize == null) { memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Value"), property.Value)); } else { memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("Collection"), property.Value)); memberBindings.Add(Expression.Bind(namedPropertyType.GetProperty("PageSize"), Expression.Constant(property.PageSize))); } 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)); }
private Expression BuildPropertyContainer(IEdmEntityType elementType, Expression source, Dictionary <IEdmNavigationProperty, ExpandedNavigationSelectItem> propertiesToExpand, ISet <IEdmStructuralProperty> propertiesToInclude, ISet <IEdmStructuralProperty> autoSelectedProperties) { IList <NamedPropertyExpression> includedProperties = new List <NamedPropertyExpression>(); foreach (KeyValuePair <IEdmNavigationProperty, ExpandedNavigationSelectItem> kvp in propertiesToExpand) { IEdmNavigationProperty propertyToExpand = kvp.Key; ExpandedNavigationSelectItem expandItem = kvp.Value; SelectExpandClause projection = expandItem.SelectAndExpand; Expression propertyName = CreatePropertyNameExpression(elementType, propertyToExpand, source); Expression propertyValue = CreatePropertyValueExpressionWithFilter(elementType, propertyToExpand, source, expandItem.FilterOption); Expression nullCheck = Expression.Equal(propertyValue, Expression.Constant(null)); // projection can be null if the expanded navigation property is not further projected or expanded. if (projection != null) { propertyValue = ProjectAsWrapper(propertyValue, projection, propertyToExpand.ToEntityType()); } NamedPropertyExpression propertyExpression = new NamedPropertyExpression(propertyName, propertyValue); if (projection != null) { if (!propertyToExpand.Type.IsCollection()) { propertyExpression.NullCheck = nullCheck; } else if (_settings.PageSize != null) { propertyExpression.PageSize = _settings.PageSize.Value; } } 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 }); } // create a property container that holds all these property names and values. return(PropertyContainer.CreatePropertyContainer(includedProperties)); }
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)); }
private static Type GetNamedPropertyType(NamedPropertyExpression property, Expression next) { Type namedPropertyGenericType; if (next == null) { if (property.NullCheck != null) { namedPropertyGenericType = typeof(SingleExpandedProperty <>); } else if (property.PageSize != null || property.CountOption != null) { namedPropertyGenericType = typeof(CollectionExpandedProperty <>); } else if (property.AutoSelected) { namedPropertyGenericType = typeof(AutoSelectedNamedProperty <>); } else { namedPropertyGenericType = typeof(NamedProperty <>); } } else { if (property.NullCheck != null) { namedPropertyGenericType = typeof(SingleExpandedPropertyWithNext <>); } else if (property.PageSize != null || property.CountOption != null) { namedPropertyGenericType = typeof(CollectionExpandedPropertyWithNext <>); } else if (property.AutoSelected) { namedPropertyGenericType = typeof(AutoSelectedNamedPropertyWithNext <>); } else { namedPropertyGenericType = typeof(NamedPropertyWithNext <>); } } Type elementType = (property.PageSize == null && property.CountOption == null) ? property.Value.Type : property.Value.Type.GetInnerElementType(); return(namedPropertyGenericType.MakeGenericType(elementType)); }
public void CreatePropertyContainer_WithNullPropertyName_DoesntIncludeTheProperty() { // Arrange Expression propertyName = Expression.Constant(null, typeof(string)); Expression propertyValue = Expression.Constant(new TestEntity()); NamedPropertyExpression property = new NamedPropertyExpression(propertyName, propertyValue); var properties = new[] { property, property }; // Act Expression containerExpression = PropertyContainer.CreatePropertyContainer(properties); // Assert PropertyContainer container = ToContainer(containerExpression); Assert.Empty(container.ToDictionary(new IdentityPropertyMapper(), includeAutoSelected: true)); }
public void ToDictionary_Throws_IfMappingFunctionReturns_NullOrEmpty(string mappedName) { // Arrange IList <NamedPropertyExpression> properties = new NamedPropertyExpression[] { new NamedPropertyExpression(name: Expression.Constant("PropA"), value: Expression.Constant(3)) }; Expression containerExpression = PropertyContainer.CreatePropertyContainer(properties); PropertyContainer container = ToContainer(containerExpression); Mock <IPropertyMapper> mapperMock = new Mock <IPropertyMapper>(); mapperMock.Setup(m => m.MapProperty("PropA")).Returns(mappedName); // Act & Assert Assert.Throws <InvalidOperationException>(() => container.ToDictionary(mapperMock.Object), "The key mapping for the property 'PropA' can't be null or empty."); }
// 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"), 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)); }
// 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); }
public void ToDictionary_AppliesMappingToAllProperties() { // Arrange IList <NamedPropertyExpression> properties = new NamedPropertyExpression[] { new NamedPropertyExpression(name: Expression.Constant("PropA"), value: Expression.Constant(3)), new NamedPropertyExpression(name: Expression.Constant("PropB"), value: Expression.Constant(6)) }; Expression containerExpression = PropertyContainer.CreatePropertyContainer(properties); PropertyContainer container = ToContainer(containerExpression); Mock <IPropertyMapper> mapperMock = new Mock <IPropertyMapper>(); mapperMock.Setup(m => m.MapProperty("PropA")).Returns("PropertyA"); mapperMock.Setup(m => m.MapProperty("PropB")).Returns("PropB"); //Act IDictionary <string, object> result = container.ToDictionary(mapperMock.Object); //Assert Assert.NotNull(result); Assert.True(result.ContainsKey("PropertyA")); Assert.True(result.ContainsKey("PropB")); }
private Expression BuildPropertyContainer(IEdmEntityType elementType, Expression source, Dictionary <IEdmNavigationProperty, ExpandedNavigationSelectItem> propertiesToExpand, ISet <IEdmStructuralProperty> propertiesToInclude, ISet <IEdmStructuralProperty> autoSelectedProperties, bool isSelectingOpenTypeSegments) { IList <NamedPropertyExpression> includedProperties = new List <NamedPropertyExpression>(); foreach (KeyValuePair <IEdmNavigationProperty, ExpandedNavigationSelectItem> kvp in propertiesToExpand) { IEdmNavigationProperty propertyToExpand = kvp.Key; ExpandedNavigationSelectItem expandItem = kvp.Value; SelectExpandClause projection = expandItem.SelectAndExpand; Expression propertyName = CreatePropertyNameExpression(elementType, propertyToExpand, source); Expression propertyValue = CreatePropertyValueExpressionWithFilter(elementType, propertyToExpand, source, expandItem.FilterOption); Expression nullCheck = Expression.Equal(propertyValue, Expression.Constant(null)); Expression countExpression = CreateTotalCountExpression(propertyValue, expandItem); // projection can be null if the expanded navigation property is not further projected or expanded. if (projection != null) { propertyValue = ProjectAsWrapper(propertyValue, projection, propertyToExpand.ToEntityType(), expandItem.NavigationSource as IEdmEntitySet); } NamedPropertyExpression propertyExpression = new NamedPropertyExpression(propertyName, propertyValue); if (projection != null) { if (!propertyToExpand.Type.IsCollection()) { propertyExpression.NullCheck = nullCheck; } else if (_settings.PageSize != null) { propertyExpression.PageSize = _settings.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); 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: propertyValue.Type.ToNullable()), 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)); }
public void ToDictionary_Throws_IfMappingFunctionReturns_NullOrEmpty(string mappedName) { // Arrange IList<NamedPropertyExpression> properties = new NamedPropertyExpression[] { new NamedPropertyExpression(name: Expression.Constant("PropA"), value: Expression.Constant(3)) }; Expression containerExpression = PropertyContainer.CreatePropertyContainer(properties); PropertyContainer container = ToContainer(containerExpression); Mock<IPropertyMapper> mapperMock = new Mock<IPropertyMapper>(); mapperMock.Setup(m => m.MapProperty("PropA")).Returns(mappedName); // Act & Assert Assert.Throws<InvalidOperationException>(() => container.ToDictionary(mapperMock.Object), "The key mapping for the property 'PropA' can't be null or empty."); }
public void ToDictionary_AppliesMappingToAllProperties() { // Arrange IList<NamedPropertyExpression> properties = new NamedPropertyExpression[] { new NamedPropertyExpression(name: Expression.Constant("PropA"), value: Expression.Constant(3)), new NamedPropertyExpression(name: Expression.Constant("PropB"), value: Expression.Constant(6)) }; Expression containerExpression = PropertyContainer.CreatePropertyContainer(properties); PropertyContainer container = ToContainer(containerExpression); Mock<IPropertyMapper> mapperMock = new Mock<IPropertyMapper>(); mapperMock.Setup(m => m.MapProperty("PropA")).Returns("PropertyA"); mapperMock.Setup(m => m.MapProperty("PropB")).Returns("PropB"); //Act IDictionary<string, object> result = container.ToDictionary(mapperMock.Object); //Assert Assert.NotNull(result); Assert.True(result.ContainsKey("PropertyA")); Assert.True(result.ContainsKey("PropB")); }
/// <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.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.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; // 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); }