Example #1
0
        public async Task TestEntityFilterReturnsTask()
        {
            var container = new RestierContainerBuilder(typeof(TestEntityFilterReturnsTaskApi));
            var provider = container.BuildContainer();
            var api = provider.GetService<ApiBase>();

            DataModificationItem<Customer> createCustomer = new DataModificationItem<Customer>(
                "Customers",
                typeof(Customer),
                null,
                DataModificationItemAction.Insert,
                null,
                null,
                new Dictionary<string, object>()
                {
                    {"CustomerID", "NEW01"},
                    {"CompanyName", "New Cust"},
                });

            await api.SubmitAsync(new ChangeSet(new ChangeSetItem[] { createCustomer }));

            NorthwindContext ctx = new NorthwindContext();

#if EF7
            Customer newCustomer = await ctx.Customers.FirstOrDefaultAsync(e => e.CustomerID == "NEW01");
#else
            Customer newCustomer = await ctx.Customers.FindAsync("NEW01");
#endif
            // The "OnInserting" should have been appended by the OnInsertingCustomers filter
            Assert.Equal("New CustOnInserting", newCustomer.CompanyName);

            ctx.Customers.Remove(newCustomer);
            await ctx.SaveChangesAsync();
        }
        public async Task ComplexTypeUpdate()
        {
            // Arrange
            var container = new RestierContainerBuilder(typeof(LibraryApi));
            var provider = container.BuildContainer();
            var libraryApi = provider.GetService<ApiBase>();

            var item = new DataModificationItem(
                "Readers",
                typeof(Person),
                null,
                DataModificationItemAction.Update, 
                new Dictionary<string, object> { { "Id", new Guid("53162782-EA1B-4712-AF26-8AA1D2AC0461") } },
                new Dictionary<string, object>(),
                new Dictionary<string, object> { { "Addr", new Dictionary<string, object> { { "Zip", "332" } } } });
            var changeSet = new ChangeSet(new[] { item });
            var sc = new SubmitContext(libraryApi.Context, changeSet);

            // Act
            var changeSetPreparer = libraryApi.Context.GetApiService<IChangeSetInitializer>();
            await changeSetPreparer.InitializeAsync(sc, CancellationToken.None);
            var person = item.Resource as Person;

            // Assert
            Assert.NotNull(person);
            Assert.Equal("332", person.Addr.Zip);
        }
        private void SetValues(DbEntityEntry dbEntry, DataModificationItem item, Type resourceType)
        {
            if (item.IsFullReplaceUpdateRequest)
            {
                // The algorithm for a "FullReplaceUpdate" is taken from ObjectContextServiceProvider.ResetResource
                // in WCF DS, and works as follows:
                //  - Create a new, blank instance of the entity.
                //  - Copy over the key values and set any updated values from the client on the new instance.
                //  - Then apply all the properties of the new instance to the instance to be updated.
                //    This will set any unspecified properties to their default value.
                object newInstance = Activator.CreateInstance(resourceType);

                SetValues(newInstance, resourceType, item.ResourceKey);
                SetValues(newInstance, resourceType, item.LocalValues);

                dbEntry.CurrentValues.SetValues(newInstance);
            }
            else
            {
                foreach (KeyValuePair<string, object> propertyPair in item.LocalValues)
                {
                    DbPropertyEntry propertyEntry = dbEntry.Property(propertyPair.Key);
                    object value = propertyPair.Value;
                    if (value == null)
                    {
                        // If the property value is null, we set null in the item too.
                        propertyEntry.CurrentValue = null;
                        continue;
                    }

                    Type type = null;
                    if (propertyEntry.CurrentValue != null)
                    {
                        type = propertyEntry.CurrentValue.GetType();
                    }
                    else
                    {
                        // If property does not have value now, will get property type from model
                        var propertyInfo = dbEntry.Entity.GetType().GetProperty(propertyPair.Key);
                        type = propertyInfo.PropertyType;
                    }

                    if (propertyEntry is DbComplexPropertyEntry)
                    {
                        var dic = value as IReadOnlyDictionary<string, object>;
                        if (dic == null)
                        {
                            throw new NotSupportedException(string.Format(
                                CultureInfo.InvariantCulture,
                                Resources.UnsupportedPropertyType,
                                propertyPair.Key));
                        }

                        value = Activator.CreateInstance(type);
                        SetValues(value, type, dic);
                    }

                    propertyEntry.CurrentValue = ConvertToEfValue(type, value);
                }
            }
        }
        private static async Task<object> FindResource(
            SubmitContext context,
            DataModificationItem item,
            CancellationToken cancellationToken)
        {
            IQueryable query = context.ApiContext.GetQueryableSource(item.ResourceSetName);
            query = item.ApplyTo(query);

            QueryResult result = await context.ApiContext.QueryAsync(new QueryRequest(query), cancellationToken);

            object resource = result.Results.SingleOrDefault();
            if (resource == null)
            {
                throw new ResourceNotFoundException(Resources.ResourceNotFound);
            }

            // This means no If-Match or If-None-Match header
            if (item.OriginalValues == null || item.OriginalValues.Count == 0)
            {
                return resource;
            }

            resource = item.ValidateEtag(result.Results.AsQueryable());
            return resource;
        }
        private async Task<IHttpActionResult> Update(
            EdmEntityObject edmEntityObject,
            bool isFullReplaceUpdate,
            CancellationToken cancellationToken)
        {
            ODataPath path = this.GetPath();
            IEdmEntitySet entitySet = path.NavigationSource as IEdmEntitySet;
            if (entitySet == null)
            {
                throw new NotImplementedException(Resources.UpdateOnlySupportedOnEntitySet);
            }

            var propertiesInEtag = await this.GetOriginalValues(entitySet);
            if (propertiesInEtag == null)
            {
                throw new PreconditionRequiredException(Resources.PreconditionRequired);
            }

            // In case of type inheritance, the actual type will be different from entity type
            // This is only needed for put case, and does not need for patch case
            // For put request, it will create a new, blank instance of the entity.
            // copy over the key values and set any updated values from the client on the new instance.
            // Then apply all the properties of the new instance to the instance to be updated.
            // This will set any unspecified properties to their default value.
            var expectedEntityType = path.EdmType;
            var actualEntityType = path.EdmType as IEdmStructuredType;
            if (edmEntityObject.ActualEdmType != null)
            {
                expectedEntityType = edmEntityObject.ExpectedEdmType;
                actualEntityType = edmEntityObject.ActualEdmType;
            }

            DataModificationItem updateItem = new DataModificationItem(
                entitySet.Name,
                expectedEntityType.GetClrType(Api),
                actualEntityType.GetClrType(Api),
                DataModificationItemAction.Update,
                RestierQueryBuilder.GetPathKeyValues(path),
                propertiesInEtag,
                edmEntityObject.CreatePropertyDictionary(actualEntityType, api, false));
            updateItem.IsFullReplaceUpdateRequest = isFullReplaceUpdate;

            RestierChangeSetProperty changeSetProperty = this.Request.GetChangeSet();
            if (changeSetProperty == null)
            {
                ChangeSet changeSet = new ChangeSet();
                changeSet.Entries.Add(updateItem);

                SubmitResult result = await Api.SubmitAsync(changeSet, cancellationToken);
            }
            else
            {
                changeSetProperty.ChangeSet.Entries.Add(updateItem);

                await changeSetProperty.OnChangeSetCompleted(this.Request);
            }

            return this.CreateUpdatedODataResult(updateItem.Resource);
        }
        /// <summary>
        /// Handles a DELETE request to delete an entity.
        /// </summary>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task object that contains the deletion result.</returns>
        public async Task<IHttpActionResult> Delete(CancellationToken cancellationToken)
        {
            ODataPath path = this.GetPath();
            IEdmEntitySet entitySet = path.NavigationSource as IEdmEntitySet;
            if (entitySet == null)
            {
                throw new NotImplementedException(Resources.DeleteOnlySupportedOnEntitySet);
            }

            var propertiesInEtag = await this.GetOriginalValues(entitySet);
            if (propertiesInEtag == null)
            {
                throw new PreconditionRequiredException(Resources.PreconditionRequired);
            }

            DataModificationItem deleteItem = new DataModificationItem(
                entitySet.Name,
                path.EdmType.GetClrType(Api),
                null,
                DataModificationItemAction.Remove,
                RestierQueryBuilder.GetPathKeyValues(path),
                propertiesInEtag,
                null);

            RestierChangeSetProperty changeSetProperty = this.Request.GetChangeSet();
            if (changeSetProperty == null)
            {
                ChangeSet changeSet = new ChangeSet();
                changeSet.Entries.Add(deleteItem);

                SubmitResult result = await Api.SubmitAsync(changeSet, cancellationToken);
            }
            else
            {
                changeSetProperty.ChangeSet.Entries.Add(deleteItem);

                await changeSetProperty.OnChangeSetCompleted(this.Request);
            }

            return this.StatusCode(HttpStatusCode.NoContent);
        }
        /// <summary>
        /// Handles a POST request to create an entity.
        /// </summary>
        /// <param name="edmEntityObject">The entity object to create.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task object that contains the creation result.</returns>
        public async Task<IHttpActionResult> Post(EdmEntityObject edmEntityObject, CancellationToken cancellationToken)
        {
            if (!this.ModelState.IsValid)
            {
                return BadRequest(this.ModelState);
            }

            ODataPath path = this.GetPath();
            IEdmEntitySet entitySet = path.NavigationSource as IEdmEntitySet;
            if (entitySet == null)
            {
                throw new NotImplementedException(Resources.InsertOnlySupportedOnEntitySet);
            }

            // In case of type inheritance, the actual type will be different from entity type
            var expectedEntityType = path.EdmType;
            var actualEntityType = path.EdmType as IEdmStructuredType;
            if (edmEntityObject.ActualEdmType != null)
            {
                expectedEntityType = edmEntityObject.ExpectedEdmType;
                actualEntityType = edmEntityObject.ActualEdmType;
            }

            DataModificationItem postItem = new DataModificationItem(
                entitySet.Name,
                expectedEntityType.GetClrType(Api),
                actualEntityType.GetClrType(Api),
                DataModificationItemAction.Insert,
                null,
                null,
                edmEntityObject.CreatePropertyDictionary(actualEntityType, api, true));

            RestierChangeSetProperty changeSetProperty = this.Request.GetChangeSet();
            if (changeSetProperty == null)
            {
                ChangeSet changeSet = new ChangeSet();
                changeSet.Entries.Add(postItem);

                SubmitResult result = await Api.SubmitAsync(changeSet, cancellationToken);
            }
            else
            {
                changeSetProperty.ChangeSet.Entries.Add(postItem);

                await changeSetProperty.OnChangeSetCompleted(this.Request);
            }

            return this.CreateCreatedODataResult(postItem.Resource);
        }