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;
                    }
                }
            }
        }
Example #2
0
        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);
        }
Example #3
0
        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);
        }