private static void UpdateCore(object target, string propertyName, object propertyValue) { var odataComplexValue = propertyValue as ODataComplexValue; var odataCollectionValue = propertyValue as ODataCollectionValue; var odataEnumValue = propertyValue as ODataEnumValue; var odataPrimitiveValue = propertyValue as ODataPrimitiveValue; if (odataComplexValue != null) { var property = target.GetType().GetProperty(propertyName); if (property != null) { var value = property.GetValue(target, null); if (value == null) { var valueType = EdmClrTypeUtils.GetInstanceType(odataComplexValue.TypeName); var propertyType = property.PropertyType; if (valueType.IsSubclassOf(propertyType) || valueType == propertyType) { value = Utility.QuickCreateInstance(valueType); } else { throw new InvalidOperationException(string.Format("{0} is not equal or derived from {1}", valueType, propertyType)); } property.SetValue(target, value, null); } foreach (var p in odataComplexValue.Properties) { UpdateCore(value, p.Name, p.Value); } return; } } else if (odataCollectionValue != null) { var property = target.GetType().GetProperty(propertyName); if (property != null) { var collection = Utility.QuickCreateInstance(property.PropertyType); foreach (var item in odataCollectionValue.Items) { var itemType = property.PropertyType.GetGenericArguments().Single(); odataComplexValue = item as ODataComplexValue; odataCollectionValue = item as ODataCollectionValue; object collectionItem = null; if (odataComplexValue != null) { // TODO: call Create method to new instance instead collectionItem = Utility.QuickCreateInstance(itemType); foreach (var p in odataComplexValue.Properties) { UpdateCore(collectionItem, p.Name, p.Value); } } else if (odataCollectionValue != null) { // TODO, check should support this type or not throw new NotImplementedException(); } else { collectionItem = ODataObjectModelConverter.ConvertPropertyValue(item, itemType); } property.PropertyType.GetMethod("Add").Invoke(collection, new object[] { collectionItem }); } property.SetValue(target, collection, null); return; } } else { var property = target.GetType().GetProperty(propertyName); if (property != null) { property.SetValue(target, ODataObjectModelConverter.ConvertPropertyValue(propertyValue, property.PropertyType), null); return; } } var openClrObject = target as OpenClrObject; if (openClrObject != null) { var structuredType = (IEdmStructuredType)EdmClrTypeUtils.GetEdmType(DataSourceManager.GetCurrentDataSource().Model, openClrObject); //check if the edmType is an open type if (structuredType.IsOpen) { if (odataCollectionValue != null) { // Collection of Edm.String if (odataCollectionValue.TypeName == "Collection(Edm.String)") { var collection = new Collection <string>(); foreach (var it in odataCollectionValue.Items) { collection.Add(it as string); } openClrObject.OpenProperties[propertyName] = collection; } else { // TODO: handle other types. throw new NotImplementedException(); } } else if (odataComplexValue != null) { var type = EdmClrTypeUtils.GetInstanceType(odataComplexValue.TypeName); var value = Utility.QuickCreateInstance(type); foreach (var property in odataComplexValue.Properties) { UpdateCore(value, property.Name, property.Value); } openClrObject.OpenProperties[propertyName] = value; } else if (odataEnumValue != null) { var type = EdmClrTypeUtils.GetInstanceType(odataEnumValue.TypeName); openClrObject.OpenProperties[propertyName] = ODataObjectModelConverter.ConvertPropertyValue(propertyValue, type); } else if (odataPrimitiveValue != null) { openClrObject.OpenProperties[propertyName] = odataPrimitiveValue.Value; } else { openClrObject.OpenProperties[propertyName] = propertyValue; } } } }
private void ProcessUpdateRequestBody(IODataRequestMessage requestMessage, IODataResponseMessage responseMessage, object targetObject, bool isUpsert) { if ((this.QueryContext.Target.NavigationSource != null && (this.QueryContext.Target.TypeKind == EdmTypeKind.Entity || this.QueryContext.Target.TypeKind == EdmTypeKind.Complex)) || (this.QueryContext.Target.Property != null && this.QueryContext.Target.TypeKind == EdmTypeKind.Complex)) { using (var messageReader = new ODataMessageReader(requestMessage, this.GetReaderSettings())) { var entryReader = messageReader.CreateODataResourceReader(this.QueryContext.Target.NavigationSource, (IEdmStructuredType)this.QueryContext.Target.Type); // Need to handle complex property or collection of complex property var odataItemStack = new Stack <ODataItem>(); var entityStack = new Stack <IEdmNavigationSource>(); var parentInstances = new Stack <object>(); var currentTargetEntitySet = this.QueryContext.Target.NavigationSource; int levelOfUndeclaredProperty = 0; while (entryReader.Read()) { switch (entryReader.State) { case ODataReaderState.ResourceStart: { if (levelOfUndeclaredProperty > 0) { break; } var entry = (ODataResource)entryReader.Item; if (entry == null) { break; } odataItemStack.Push(entryReader.Item); entityStack.Push(currentTargetEntitySet); if (parentInstances.Count == 0) { parentInstances.Push(targetObject); } else { var parent2 = parentInstances.Peek(); if (parent2 == null) { // Here is for collection, we need to create a brand new instance. var valueType = EdmClrTypeUtils.GetInstanceType(entry.TypeName); parentInstances.Push(Utility.QuickCreateInstance(valueType)); } else { parentInstances.Push(parent2); } } break; } case ODataReaderState.ResourceEnd: { if (levelOfUndeclaredProperty > 0) { break; } var entry = (ODataResource)entryReader.Item; if (entry == null) { break; } object newInstance = parentInstances.Pop(); currentTargetEntitySet = entityStack.Pop(); foreach (var property in entry.Properties) { if (Utility.IsETagProperty(targetObject, property.Name)) { continue; } // the property might be an open property, so test null first var propertyInfo = newInstance.GetType().GetProperty(property.Name); if (propertyInfo != null) { if (!isUpsert && Utility.IsReadOnly(propertyInfo)) { continue; } } this.DataSource.UpdateProvider.Update(newInstance, property.Name, property.Value); } var boundNavPropAnnotation = odataItemStack.Pop().GetAnnotation <BoundNavigationPropertyAnnotation>(); if (boundNavPropAnnotation != null) { foreach (var boundProperty in boundNavPropAnnotation.BoundProperties) { var isCollection = boundProperty.Item1.IsCollection == true; var propertyValue = isCollection ? boundProperty.Item2 : ((IEnumerable <object>)boundProperty.Item2).Single(); this.DataSource.UpdateProvider.Update(newInstance, boundProperty.Item1.Name, propertyValue); } } var parentItem = odataItemStack.Count > 0 ? odataItemStack.Peek() : null; if (parentItem != null) { // This new entry belongs to a navigation property and/or feed - // propagate it up the tree for further processing. AddChildInstanceAnnotation(parentItem, newInstance); } } break; case ODataReaderState.ResourceSetStart: if (levelOfUndeclaredProperty > 0) { break; } odataItemStack.Push(entryReader.Item); //"null" here indicate that we need to create a new instance for collection to replace the old one. parentInstances.Push(null); break; case ODataReaderState.ResourceSetEnd: { if (levelOfUndeclaredProperty > 0) { break; } parentInstances.Pop(); var childAnnotation = odataItemStack.Pop().GetAnnotation <ChildInstanceAnnotation>(); var parentNavLink = odataItemStack.Count > 0 ? odataItemStack.Peek() as ODataNestedResourceInfo : null; if (parentNavLink != null) { // This feed belongs to a navigation property or complex property or a complex collection property. // propagate it up the tree for further processing. AddChildInstanceAnnotations(parentNavLink, childAnnotation == null ? new object[0] : (childAnnotation.ChildInstances ?? new object[0])); } } break; case ODataReaderState.NestedResourceInfoStart: { object parent = parentInstances.Peek(); var nestedResourceInfo = (ODataNestedResourceInfo)entryReader.Item; var property = parent.GetType().GetProperty(nestedResourceInfo.Name); if (property == null || levelOfUndeclaredProperty > 0) { levelOfUndeclaredProperty++; } // skip undeclared property if (levelOfUndeclaredProperty > 0) { break; } odataItemStack.Push(entryReader.Item); var propertyInstance = property.GetValue(parent); parentInstances.Push(propertyInstance); IEdmNavigationProperty navigationProperty = currentTargetEntitySet == null ? null : currentTargetEntitySet.EntityType().FindProperty(nestedResourceInfo.Name) as IEdmNavigationProperty; // Current model implementation doesn't expose associations otherwise this would be much cleaner. if (navigationProperty != null) { currentTargetEntitySet = this.DataSource.Model.EntityContainer.EntitySets().Single(s => s.EntityType() == navigationProperty.Type.Definition); } else { currentTargetEntitySet = null; } entityStack.Push(currentTargetEntitySet); break; } case ODataReaderState.NestedResourceInfoEnd: { // check: skip or not int theLevelOfUndeclaredProperty = levelOfUndeclaredProperty; if (levelOfUndeclaredProperty > 0) { levelOfUndeclaredProperty--; } // skip undeclared property if (theLevelOfUndeclaredProperty > 0) { break; } var navigationLink = (ODataNestedResourceInfo)entryReader.Item; parentInstances.Pop(); var childAnnotation = odataItemStack.Pop().GetAnnotation <ChildInstanceAnnotation>(); if (childAnnotation != null) { // Propagate the bound resources to the parent resource. AddBoundNavigationPropertyAnnotation(odataItemStack.Peek(), navigationLink, childAnnotation == null ? new object[0] : childAnnotation.ChildInstances); } entityStack.Pop(); currentTargetEntitySet = entityStack.Peek(); break; } } } } } else { throw Utility.BuildException( HttpStatusCode.NotImplemented, string.Format("PATCH/PUT for '{0}' type is not supported.", this.QueryContext.Target.TypeKind), null); } var currentETag = Utility.GetETagValue(targetObject); // if the current entity has ETag field if (currentETag != null) { if (!isUpsert) { this.DataSource.UpdateProvider.UpdateETagValue(targetObject); } this.DataSource.UpdateProvider.SaveChanges(); currentETag = Utility.GetETagValue(targetObject); responseMessage.SetHeader(ServiceConstants.HttpHeaders.ETag, currentETag); } else { this.DataSource.UpdateProvider.SaveChanges(); } ResponseWriter.WriteEmptyResponse(responseMessage); }
private object ProcessPostBody(IODataRequestMessage message, IEdmEntitySetBase entitySet, object queryResults) { object lastNewInstance = null; using (var messageReader = new ODataMessageReader(message, this.GetReaderSettings())) { var odataItemStack = new Stack <ODataItem>(); var entityStack = new Stack <IEdmEntitySetBase>(); var entryReader = messageReader.CreateODataResourceReader(entitySet, entitySet.EntityType()); var currentTargetEntitySet = entitySet; while (entryReader.Read()) { switch (entryReader.State) { case ODataReaderState.ResourceStart: { var entry = (ODataResource)entryReader.Item; if (entry == null) { break; } odataItemStack.Push(entryReader.Item); entityStack.Push(currentTargetEntitySet); break; } case ODataReaderState.ResourceEnd: { var entry = (ODataResource)entryReader.Item; if (entry == null) { break; } // TODO: the code here will be changed to handle following scenarios // 1: non-contained navigation, e.g. People(1)/Friends // 2. general entity set, e.g. People // 3. contained navigation, e.g. People(1)/Trips // 4. complex property, e.g. Airports('KSFO')/Location // 5. upsert, e.g. People(1)/Friends(2) object newInstance; currentTargetEntitySet = entityStack.Pop(); if (currentTargetEntitySet == null) { // For entry var valueType = EdmClrTypeUtils.GetInstanceType(entry.TypeName); newInstance = Utility.QuickCreateInstance(valueType); } else { // For complex property newInstance = this.DataSource.UpdateProvider.Create(entry.TypeName, queryResults); } foreach (var property in entry.Properties) { if (Utility.IsETagProperty(newInstance, property.Name)) { continue; } this.DataSource.UpdateProvider.Update(newInstance, property.Name, property.Value); } var boundNavPropAnnotation = odataItemStack.Pop().GetAnnotation <BoundNavigationPropertyAnnotation>(); if (boundNavPropAnnotation != null) { foreach (var boundProperty in boundNavPropAnnotation.BoundProperties) { var isCollection = boundProperty.Item1.IsCollection == true; var propertyValue = isCollection ? boundProperty.Item2 : ((IEnumerable <object>)boundProperty.Item2).Single(); this.DataSource.UpdateProvider.Update(newInstance, boundProperty.Item1.Name, propertyValue); } } var parentItem = odataItemStack.Count > 0 ? odataItemStack.Peek() : null; if (parentItem != null) { // This new resource belongs to a navigation property and/or complex/complex collection property and/or feed - // propagate it up the tree for further processing. AddChildInstanceAnnotation(parentItem, newInstance); } lastNewInstance = newInstance; } break; case ODataReaderState.ResourceSetStart: odataItemStack.Push(entryReader.Item); break; case ODataReaderState.ResourceSetEnd: { var childAnnotation = odataItemStack.Pop().GetAnnotation <ChildInstanceAnnotation>(); var parentNavLink = odataItemStack.Count > 0 ? odataItemStack.Peek() as ODataNestedResourceInfo : null; if (parentNavLink != null) { // This feed belongs to a navigation property - // propagate it up the tree for further processing. AddChildInstanceAnnotations(parentNavLink, childAnnotation == null ? new object[0] : (childAnnotation.ChildInstances ?? new object[0])); } } break; case ODataReaderState.NestedResourceInfoStart: { odataItemStack.Push(entryReader.Item); var nestedResourceInfo = (ODataNestedResourceInfo)entryReader.Item; IEdmNavigationProperty navigationProperty = currentTargetEntitySet == null ? null : currentTargetEntitySet.EntityType().FindProperty(nestedResourceInfo.Name) as IEdmNavigationProperty; // Current model implementation doesn't expose associations otherwise this would be much cleaner. if (navigationProperty != null) { currentTargetEntitySet = this.DataSource.Model.EntityContainer.EntitySets().Single(s => s.EntityType() == navigationProperty.Type.Definition); } else { currentTargetEntitySet = null; } entityStack.Push(currentTargetEntitySet); } break; case ODataReaderState.NestedResourceInfoEnd: { var navigationLink = (ODataNestedResourceInfo)entryReader.Item; var childAnnotation = odataItemStack.Pop().GetAnnotation <ChildInstanceAnnotation>(); if (childAnnotation != null) { // Propagate the bound entries to the parent entry. AddBoundNavigationPropertyAnnotation(odataItemStack.Peek(), navigationLink, childAnnotation == null ? new object[0] : childAnnotation.ChildInstances); } entityStack.Pop(); currentTargetEntitySet = entityStack.Peek(); } break; } } } return(lastNewInstance); }