/// <summary> /// Removes the the entity with the given key from the given the given property on the given structural value /// </summary> /// <param name="keyToRemove">The key of the entity to remove</param> /// <param name="related">The structural value to remove from</param> /// <param name="navigation">The property to remove from</param> private void RemoveFromNavigationProperty(EntityDataKey keyToRemove, QueryStructuralValue related, NavigationProperty navigation) { if (navigation.ToAssociationEnd.Multiplicity == EndMultiplicity.Many) { var collection = related.GetCollectionValue(navigation.Name); // remove the element with the given key from the collection, if it is present foreach (var element in collection.Elements.Cast <QueryStructuralValue>().ToList()) { if (this.GetEntityKey(element).Equals(keyToRemove)) { collection.Elements.Remove(element); } } } else { // if the value's key matches, set it to null var value = related.GetStructuralValue(navigation.Name); if (!value.IsNull && this.GetEntityKey(value).Equals(keyToRemove)) { related.SetValue(navigation.Name, value.Type.NullValue); } } }
private void ExpandRelated(Dictionary<QueryStructuralValue, QueryStructuralValue> identityMap, QueryValue item, string[] splitPath, int position, IEnumerable<string> expandedPath, IEnumerable<string> selectedPath) { QueryStructuralValue qsv = item as QueryStructuralValue; if (qsv == null) { return; } if (position >= splitPath.Length) { return; } var memberName = splitPath[position]; var member = qsv.Type.Properties.SingleOrDefault(c => c.Name == memberName); if (member == null) { return; } // do not expand if the selected paths don't contain the expand value if (selectedPath.Count() > 0 && !selectedPath.Contains(member.Name)) { return; } var clone = this.CloneStructuralValue(identityMap, qsv); if (member.PropertyType is QueryCollectionType) { var oldValue = qsv.GetCollectionValue(memberName); clone.SetValue(memberName, this.CloneCollectionValue(identityMap, oldValue)); if (!oldValue.IsNull) { foreach (var e in oldValue.Elements) { this.ExpandRelated(identityMap, e, splitPath, position + 1, expandedPath, selectedPath); } } } else if (member.PropertyType is QueryStructuralType) { var oldValue = qsv.GetStructuralValue(memberName); var newValue = this.CloneStructuralValue(identityMap, oldValue); clone.SetValue(memberName, newValue); this.ExpandRelated(identityMap, oldValue, splitPath, position + 1, expandedPath, selectedPath); } }
private void FixupReferencesOfVisibleEntity(QueryStructuralValue originalEntity, QueryStructuralValue visibleEntity) { foreach (var property in originalEntity.Type.Properties) { var entityProperty = property.PropertyType as QueryEntityType; if (entityProperty != null) { var originalProperty = originalEntity.GetStructuralValue(property.Name); QueryStructuralValue fixedUpProperty; if (!this.VisibleEntitiesGraph.TryGetValue(originalProperty, out fixedUpProperty)) { fixedUpProperty = entityProperty.NullValue; } var navigationProperty = ((QueryEntityType)visibleEntity.Type).EntityType.AllNavigationProperties.Where(p => p.Name == property.Name).SingleOrDefault(); ExceptionUtilities.CheckObjectNotNull(navigationProperty, "Could not find navigation property '" + property.Name + "' on entity."); this.FixupBothEndsOfNavigationProperty(visibleEntity, fixedUpProperty, navigationProperty); } } }
/// <summary> /// Removes the given entity from the query data set /// Will remove all references to the entity if the provider is relational, and will delete any related objects that are marked as cascade-delete /// </summary> /// <param name="entity">The entity to remove</param> private void RemoveEntity(QueryStructuralValue entity) { ExceptionUtilities.CheckArgumentNotNull(entity, "entity"); QueryEntityType entityType = entity.Type as QueryEntityType; ExceptionUtilities.CheckObjectNotNull(entityType, "Value was not an entity"); // remove it from the top level set this.QueryDataSet[entityType.EntitySet.Name].Elements.Remove(entity); // remove all other references to this entity this.RemoveAllReferences(entityType, entity); // if any of this entity's relationships have cascading deletes, we need to emulate them foreach (var navigation in entityType.EntityType.AllNavigationProperties) { if (navigation.ToAssociationEnd.DeleteBehavior == OperationAction.Cascade) { if (navigation.ToAssociationEnd.Multiplicity == EndMultiplicity.Many) { var collection = entity.GetCollectionValue(navigation.Name); // recurse into the elements foreach (var related in collection.Elements.Cast <QueryStructuralValue>()) { this.RemoveEntity(related); } } else { // recurse into the reference var reference = entity.GetStructuralValue(navigation.Name); if (!reference.IsNull) { this.RemoveEntity(reference); } } } } }
private void CompareValues(QueryStructuralValue instance, IEnumerable<NamedValue> namedValues, string propertyPath) { foreach (QueryProperty property in instance.Type.Properties) { string childPropertyPath = property.Name; if (propertyPath != null) { childPropertyPath = propertyPath + "." + property.Name; } // Skip if its an EntityType, this only handles structural types var queryEntityType = property.PropertyType as QueryEntityType; if (queryEntityType != null) { continue; } var collectionType = property.PropertyType as QueryCollectionType; var scalarType = property.PropertyType as QueryScalarType; var complexDataType = property.PropertyType as QueryComplexType; QueryEntityType collectionQueryElementType = null; if (collectionType != null) { collectionQueryElementType = collectionType.ElementType as QueryEntityType; } // Skip if its a collection of QueryEntityType if (collectionQueryElementType != null) { continue; } if (scalarType != null) { // The following code block handles the case where the value is server generated. // For instance, server generated Keys would fail so if a property is marked as being server generated // we make sure to remove that property from the list of properties to verify MemberProperty memberProperty; var instanceEntityType = instance.Type as QueryEntityType; if (instanceEntityType != null) { memberProperty = instanceEntityType.EntityType.AllProperties.SingleOrDefault(p => p.Name == property.Name); } else { memberProperty = ((QueryComplexType)instance.Type).ComplexType.Properties.SingleOrDefault(p => p.Name == property.Name); } if (memberProperty != null && memberProperty.Annotations.OfType<StoreGeneratedPatternAnnotation>().Any()) { // TODO: be more fine-grained about whether this is an update or insert (ie, look at the annotation's property values) this.unusedNamedValuePaths.Remove(childPropertyPath); continue; } NamedValue primitivePropertyNamedValue = namedValues.SingleOrDefault(nv => nv.Name == childPropertyPath); if (primitivePropertyNamedValue != null) { var queryValue = instance.GetScalarValue(property.Name); this.WriteErrorIfNotEqual(childPropertyPath, primitivePropertyNamedValue.Value, queryValue); this.unusedNamedValuePaths.Remove(childPropertyPath); } } else if (collectionType != null) { List<NamedValue> bagNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (bagNamedValues.Any()) { this.CompareBagProperty(instance, property, collectionType.ElementType, childPropertyPath, bagNamedValues); } else { this.CompareBagPropertyWithNullOrEmpty(instance, property, childPropertyPath, namedValues); } } else if (complexDataType != null) { // NOTE: we cannot assert that it is complex/primitive/bag, because there may be new query types added in other assemblies that we know nothing about here QueryStructuralValue complexTypeValue = instance.GetStructuralValue(property.Name); List<NamedValue> complexInstanceNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (complexInstanceNamedValues.Any()) { if (!this.WriteErrorIfNull(childPropertyPath, complexTypeValue)) { this.CompareValues(complexTypeValue, complexInstanceNamedValues, childPropertyPath); } } else { // Check for null case List<NamedValue> exactMatches = namedValues.Where(nv => nv.Name == childPropertyPath).ToList(); ExceptionUtilities.Assert(exactMatches.Count < 2, "Should only find at most one property path {0} when looking for null value", childPropertyPath); if (exactMatches.Count == 1) { ExceptionUtilities.Assert( exactMatches[0].Value == null, "Named value at path '{0}' was unexpectedly non-null. Value was '{1}'", childPropertyPath, exactMatches[0].Value); QueryValue queryValue = instance.GetValue(property.Name); this.WriteErrorIfNotNull(childPropertyPath, queryValue); this.unusedNamedValuePaths.Remove(childPropertyPath); } } } } }
internal void UpdateValues(QueryStructuralValue instance, IEnumerable <NamedValue> namedValues, string propertyPath) { foreach (QueryProperty property in instance.Type.Properties) { string childPropertyPath = property.Name; if (propertyPath != null) { childPropertyPath = propertyPath + "." + property.Name; } // Skip if its an EntityType, this only handles structural types var queryEntityType = property.PropertyType as QueryEntityType; if (queryEntityType != null) { continue; } var collectionType = property.PropertyType as QueryCollectionType; var scalarType = property.PropertyType as QueryScalarType; var complexDataType = property.PropertyType as QueryComplexType; QueryEntityType collectionQueryElementType = null; if (collectionType != null) { collectionQueryElementType = collectionType.ElementType as QueryEntityType; } // Skip if its a collection of QueryEntityType if (collectionQueryElementType != null) { continue; } if (scalarType != null) { NamedValue primitivePropertyNamedValue = namedValues.SingleOrDefault(nv => nv.Name == childPropertyPath); if (primitivePropertyNamedValue != null) { instance.SetPrimitiveValue(property.Name, primitivePropertyNamedValue.Value); this.unusedNamedValuePaths.Remove(childPropertyPath); } } else if (collectionType != null) { List <NamedValue> bagNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (bagNamedValues.Any()) { this.UpdateBagProperty(instance, property, collectionType.ElementType, childPropertyPath, bagNamedValues); } else { this.UpdateBagPropertyWithNullOrEmpty(instance, property, childPropertyPath, namedValues); } } else if (complexDataType != null) { // NOTE: we cannot assert that it is complex/primitive/bag, because there may be new query types added in other assemblies that we know nothing about here QueryStructuralValue complexTypeValue = instance.GetStructuralValue(property.Name); List <NamedValue> complexInstanceNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (complexInstanceNamedValues.Any()) { if (complexTypeValue.IsNull) { complexTypeValue = complexDataType.CreateNewInstance(); } this.UpdateValues(complexTypeValue, complexInstanceNamedValues, childPropertyPath); instance.SetValue(property.Name, complexTypeValue); } else { // Check for null case List <NamedValue> exactMatches = namedValues.Where(nv => nv.Name == childPropertyPath).ToList(); ExceptionUtilities.Assert(exactMatches.Count < 2, "Should only find at most one property path {0} when looking for null value", childPropertyPath); if (exactMatches.Count == 1) { ExceptionUtilities.Assert( exactMatches[0].Value == null, "Named value at path '{0}' was unexpectedly non-null. Value was '{1}'", childPropertyPath, exactMatches[0].Value); instance.SetValue(property.Name, complexDataType.NullValue); this.unusedNamedValuePaths.Remove(childPropertyPath); } } } } }
private QueryStructuralValue CloneStructuralValue(Dictionary<QueryStructuralValue, QueryStructuralValue> identityMap, QueryStructuralValue qsv) { if (qsv.IsNull) { return qsv; } QueryStructuralValue clonedValue; if (identityMap.TryGetValue(qsv, out clonedValue)) { return clonedValue; } clonedValue = qsv.Type.CreateNewInstance(); identityMap.Add(qsv, clonedValue); foreach (var m in qsv.Type.Properties) { // copy scalar properties if (m.PropertyType is QueryScalarType) { clonedValue.SetValue(m.Name, qsv.GetScalarValue(m.Name)); continue; } // copy stream properties if (m.PropertyType is AstoriaQueryStreamType) { if (m.Name.Contains("DefaultStream")) { clonedValue.SetDefaultStreamValue(qsv.GetDefaultStreamValue()); } else { clonedValue.SetStreamValue(m.Name, qsv.GetStreamValue(m.Name)); } continue; } var qst = m.PropertyType as QueryStructuralType; if (m.PropertyType is QueryStructuralType) { if (!qst.IsValueType) { // skip reference types, clone everything else continue; } clonedValue.SetValue(m.Name, this.CloneStructuralValue(identityMap, qsv.GetStructuralValue(m.Name))); } var qct = m.PropertyType as QueryCollectionType; if (qct != null) { var elementStructuralType = qct.ElementType as QueryStructuralType; if (elementStructuralType != null) { if (!elementStructuralType.IsValueType) { // skip collections of reference types, clone everything else continue; } } clonedValue.SetValue(m.Name, this.CloneCollectionValue(identityMap, qsv.GetCollectionValue(m.Name))); } } return clonedValue; }
private void CompareValues(QueryStructuralValue instance, IEnumerable <NamedValue> namedValues, string propertyPath) { foreach (QueryProperty property in instance.Type.Properties) { string childPropertyPath = property.Name; if (propertyPath != null) { childPropertyPath = propertyPath + "." + property.Name; } // Skip if its an EntityType, this only handles structural types var queryEntityType = property.PropertyType as QueryEntityType; if (queryEntityType != null) { continue; } var collectionType = property.PropertyType as QueryCollectionType; var scalarType = property.PropertyType as QueryScalarType; var complexDataType = property.PropertyType as QueryComplexType; QueryEntityType collectionQueryElementType = null; if (collectionType != null) { collectionQueryElementType = collectionType.ElementType as QueryEntityType; } // Skip if its a collection of QueryEntityType if (collectionQueryElementType != null) { continue; } if (scalarType != null) { // The following code block handles the case where the value is server generated. // For instance, server generated Keys would fail so if a property is marked as being server generated // we make sure to remove that property from the list of properties to verify MemberProperty memberProperty; var instanceEntityType = instance.Type as QueryEntityType; if (instanceEntityType != null) { memberProperty = instanceEntityType.EntityType.AllProperties.SingleOrDefault(p => p.Name == property.Name); } else { memberProperty = ((QueryComplexType)instance.Type).ComplexType.Properties.SingleOrDefault(p => p.Name == property.Name); } if (memberProperty != null && memberProperty.Annotations.OfType <StoreGeneratedPatternAnnotation>().Any()) { // TODO: be more fine-grained about whether this is an update or insert (ie, look at the annotation's property values) this.unusedNamedValuePaths.Remove(childPropertyPath); continue; } NamedValue primitivePropertyNamedValue = namedValues.SingleOrDefault(nv => nv.Name == childPropertyPath); if (primitivePropertyNamedValue != null) { var queryValue = instance.GetScalarValue(property.Name); this.WriteErrorIfNotEqual(childPropertyPath, primitivePropertyNamedValue.Value, queryValue); this.unusedNamedValuePaths.Remove(childPropertyPath); } } else if (collectionType != null) { List <NamedValue> bagNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (bagNamedValues.Any()) { this.CompareBagProperty(instance, property, collectionType.ElementType, childPropertyPath, bagNamedValues); } else { this.CompareBagPropertyWithNullOrEmpty(instance, property, childPropertyPath, namedValues); } } else if (complexDataType != null) { // NOTE: we cannot assert that it is complex/primitive/bag, because there may be new query types added in other assemblies that we know nothing about here QueryStructuralValue complexTypeValue = instance.GetStructuralValue(property.Name); List <NamedValue> complexInstanceNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (complexInstanceNamedValues.Any()) { if (!this.WriteErrorIfNull(childPropertyPath, complexTypeValue)) { this.CompareValues(complexTypeValue, complexInstanceNamedValues, childPropertyPath); } } else { // Check for null case List <NamedValue> exactMatches = namedValues.Where(nv => nv.Name == childPropertyPath).ToList(); ExceptionUtilities.Assert(exactMatches.Count < 2, "Should only find at most one property path {0} when looking for null value", childPropertyPath); if (exactMatches.Count == 1) { ExceptionUtilities.Assert( exactMatches[0].Value == null, "Named value at path '{0}' was unexpectedly non-null. Value was '{1}'", childPropertyPath, exactMatches[0].Value); QueryValue queryValue = instance.GetValue(property.Name); this.WriteErrorIfNotNull(childPropertyPath, queryValue); this.unusedNamedValuePaths.Remove(childPropertyPath); } } } } }
internal void UpdateValues(QueryStructuralValue instance, IEnumerable<NamedValue> namedValues, string propertyPath) { foreach (QueryProperty property in instance.Type.Properties) { string childPropertyPath = property.Name; if (propertyPath != null) { childPropertyPath = propertyPath + "." + property.Name; } // Skip if its an EntityType, this only handles structural types var queryEntityType = property.PropertyType as QueryEntityType; if (queryEntityType != null) { continue; } var collectionType = property.PropertyType as QueryCollectionType; var scalarType = property.PropertyType as QueryScalarType; var complexDataType = property.PropertyType as QueryComplexType; QueryEntityType collectionQueryElementType = null; if (collectionType != null) { collectionQueryElementType = collectionType.ElementType as QueryEntityType; } // Skip if its a collection of QueryEntityType if (collectionQueryElementType != null) { continue; } if (scalarType != null) { NamedValue primitivePropertyNamedValue = namedValues.SingleOrDefault(nv => nv.Name == childPropertyPath); if (primitivePropertyNamedValue != null) { instance.SetPrimitiveValue(property.Name, primitivePropertyNamedValue.Value); this.unusedNamedValuePaths.Remove(childPropertyPath); } } else if (collectionType != null) { List<NamedValue> bagNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (bagNamedValues.Any()) { this.UpdateBagProperty(instance, property, collectionType.ElementType, childPropertyPath, bagNamedValues); } else { this.UpdateBagPropertyWithNullOrEmpty(instance, property, childPropertyPath, namedValues); } } else if (complexDataType != null) { // NOTE: we cannot assert that it is complex/primitive/bag, because there may be new query types added in other assemblies that we know nothing about here QueryStructuralValue complexTypeValue = instance.GetStructuralValue(property.Name); List<NamedValue> complexInstanceNamedValues = namedValues.Where(nv => nv.Name.StartsWith(childPropertyPath + ".", StringComparison.Ordinal)).ToList(); if (complexInstanceNamedValues.Any()) { if (complexTypeValue.IsNull) { complexTypeValue = complexDataType.CreateNewInstance(); } this.UpdateValues(complexTypeValue, complexInstanceNamedValues, childPropertyPath); instance.SetValue(property.Name, complexTypeValue); } else { // Check for null case List<NamedValue> exactMatches = namedValues.Where(nv => nv.Name == childPropertyPath).ToList(); ExceptionUtilities.Assert(exactMatches.Count < 2, "Should only find at most one property path {0} when looking for null value", childPropertyPath); if (exactMatches.Count == 1) { ExceptionUtilities.Assert( exactMatches[0].Value == null, "Named value at path '{0}' was unexpectedly non-null. Value was '{1}'", childPropertyPath, exactMatches[0].Value); instance.SetValue(property.Name, complexDataType.NullValue); this.unusedNamedValuePaths.Remove(childPropertyPath); } } } } }