Esempio n. 1
0
        /// <summary>
        /// Returns the original unmodified entity for the provided <paramref name="clientEntity"/>.
        /// </summary>
        /// <remarks>
        /// Note that only members marked with <see cref="RoundtripOriginalAttribute"/> will be set
        /// in the returned instance.
        /// </remarks>
        /// <typeparam name="TEntity">The entity type.</typeparam>
        /// <param name="clientEntity">The client modified entity.</param>
        /// <returns>The original unmodified entity for the provided <paramref name="clientEntity"/>.</returns>
        /// <exception cref="ArgumentNullException">if <paramref name="clientEntity"/> is null.</exception>
        /// <exception cref="ArgumentException">if <paramref name="clientEntity"/> is not in the change set.</exception>
        public TEntity GetOriginal <TEntity>(TEntity clientEntity) where TEntity : class
        {
            if (clientEntity == null)
            {
                throw Error.ArgumentNull("clientEntity");
            }

            ChangeSetEntry entry = _changeSetEntries.FirstOrDefault(p => Object.ReferenceEquals(p.Entity, clientEntity));

            if (entry == null)
            {
                throw Error.Argument(Resource.ChangeSet_ChangeSetEntryNotFound);
            }

            if (entry.Operation == ChangeOperation.Insert)
            {
                throw Error.InvalidOperation(Resource.ChangeSet_OriginalNotValidForInsert);
            }

            return((TEntity)entry.OriginalEntity);
        }
        private void InvokeAction(HttpActionDescriptor action, object[] parameters, ChangeSetEntry changeSetEntry)
        {
            try
            {
                Collection <HttpParameterDescriptor> pds      = action.GetParameters();
                Dictionary <string, object>          paramMap = new Dictionary <string, object>(pds.Count);
                for (int i = 0; i < pds.Count; i++)
                {
                    paramMap.Add(pds[i].ParameterName, parameters[i]);
                }

                // TODO - Issue #103
                // This method is not correctly observing the execution results, the catch block below is wrong.
                // Submit should be Task<bool>, not bool, and should model bind for the CancellationToken which would then
                // be propagated through to all the helper methods (one or more of which might also need to be made async,
                // once we start respecting the fact that the read/write actions should be allowed to be async).
                action.ExecuteAsync(ActionContext.ControllerContext, paramMap, CancellationToken.None);
            }
            catch (TargetInvocationException tie)
            {
                ValidationException vex = tie.GetBaseException() as ValidationException;
                if (vex != null)
                {
                    ValidationResultInfo error = new ValidationResultInfo(vex.Message, 0, String.Empty, vex.ValidationResult.MemberNames);
                    if (changeSetEntry.ValidationErrors != null)
                    {
                        changeSetEntry.ValidationErrors = changeSetEntry.ValidationErrors.Concat(new ValidationResultInfo[] { error }).ToArray();
                    }
                    else
                    {
                        changeSetEntry.ValidationErrors = new ValidationResultInfo[] { error };
                    }
                }
                else
                {
                    throw;
                }
            }
        }
        public override Task <object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary <string, object> arguments, CancellationToken cancellationToken)
        {
            return(TaskHelpers.RunSynchronously <object>(() =>
            {
                // create the changeset
                object entity = arguments.Single().Value; // there is only a single parameter - the entity being submitted
                ChangeSetEntry[] changeSetEntries = new ChangeSetEntry[]
                {
                    new ChangeSetEntry
                    {
                        Id = 1,
                        ActionDescriptor = _updateAction,
                        Entity = entity,
                        Operation = _updateAction.ChangeOperation
                    }
                };
                ChangeSet changeSet = new ChangeSet(changeSetEntries);
                changeSet.SetEntityAssociations();

                DomainController controller = (DomainController)controllerContext.Controller;
                if (!controller.Submit(changeSet) &&
                    controller.ActionContext.Response != null)
                {
                    // If the submit failed due to an authorization failure,
                    // return the authorization response directly
                    return controller.ActionContext.Response;
                }

                // return the entity
                entity = changeSet.ChangeSetEntries[0].Entity;
                // REVIEW does JSON make sense here?
                return new HttpResponseMessage()
                {
                    Content = new ObjectContent(_updateAction.EntityType, entity, new JsonMediaTypeFormatter())
                };
            }, cancellationToken));
        }
        /// <summary>
        /// This method invokes the user overridable <see cref="PersistChangeSet"/> method wrapping the call
        /// with the appropriate exception handling logic. All framework calls to <see cref="PersistChangeSet"/>
        /// must go through this method. Some data sources have their own validation hook points,
        /// so if a <see cref="ValidationException"/> is thrown at that level, we want to capture it.
        /// </summary>
        /// <returns>True if the <see cref="ChangeSet"/> was persisted successfully, false otherwise.</returns>
        private bool PersistChangeSetInternal()
        {
            try
            {
                PersistChangeSet();
            }
            catch (ValidationException e)
            {
                // if a validation exception is thrown for one of the entities in the changeset
                // set the error on the corresponding ChangeSetEntry
                if (e.Value != null && e.ValidationResult != null)
                {
                    IEnumerable <ChangeSetEntry> updateOperations =
                        ChangeSet.ChangeSetEntries.Where(
                            p => p.Operation == ChangeOperation.Insert ||
                            p.Operation == ChangeOperation.Update ||
                            p.Operation == ChangeOperation.Delete);

                    ChangeSetEntry operation = updateOperations.SingleOrDefault(p => Object.ReferenceEquals(p.Entity, e.Value));
                    if (operation != null)
                    {
                        ValidationResultInfo error = new ValidationResultInfo(e.ValidationResult.ErrorMessage, e.ValidationResult.MemberNames);
                        error.StackTrace           = e.StackTrace;
                        operation.ValidationErrors = new List <ValidationResultInfo>()
                        {
                            error
                        };
                    }
                }
                else
                {
                    throw;
                }
            }

            return(!ChangeSet.HasError);
        }
        public override Task<object> ExecuteAsync(HttpControllerContext controllerContext, IDictionary<string, object> arguments, CancellationToken cancellationToken)
        {
            return TaskHelpers.RunSynchronously<object>(() =>
            {
                // create the changeset
                object entity = arguments.Single().Value; // there is only a single parameter - the entity being submitted
                ChangeSetEntry[] changeSetEntries = new ChangeSetEntry[]
                {
                    new ChangeSetEntry
                    {
                        Id = 1,
                        ActionDescriptor = _updateAction,
                        Entity = entity,
                        Operation = _updateAction.ChangeOperation
                    }
                };
                ChangeSet changeSet = new ChangeSet(changeSetEntries);
                changeSet.SetEntityAssociations();

                DomainController controller = (DomainController)controllerContext.Controller;
                if (!controller.Submit(changeSet) &&
                    controller.ActionContext.Response != null)
                {
                    // If the submit failed due to an authorization failure,
                    // return the authorization response directly
                    return controller.ActionContext.Response;
                }

                // return the entity
                entity = changeSet.ChangeSetEntries[0].Entity;
                // REVIEW does JSON make sense here?
                return new HttpResponseMessage()
                {
                    Content = new ObjectContent(_updateAction.EntityType, entity, new JsonMediaTypeFormatter())
                };
            }, cancellationToken);
        }
        private void InvokeAction(HttpActionDescriptor action, object[] parameters, ChangeSetEntry changeSetEntry)
        {
            try
            {
                Collection<HttpParameterDescriptor> pds = action.GetParameters();
                Dictionary<string, object> paramMap = new Dictionary<string, object>(pds.Count);
                for (int i = 0; i < pds.Count; i++)
                {
                    paramMap.Add(pds[i].ParameterName, parameters[i]);
                }

                // TODO - Issue #103
                // This method is not correctly observing the execution results, the catch block below is wrong.
                // Submit should be Task<bool>, not bool, and should model bind for the CancellationToken which would then
                // be propagated through to all the helper methods (one or more of which might also need to be made async,
                // once we start respecting the fact that the read/write actions should be allowed to be async).
                action.ExecuteAsync(ActionContext.ControllerContext, paramMap, CancellationToken.None);
            }
            catch (TargetInvocationException tie)
            {
                ValidationException vex = tie.GetBaseException() as ValidationException;
                if (vex != null)
                {
                    ValidationResultInfo error = new ValidationResultInfo(vex.Message, 0, String.Empty, vex.ValidationResult.MemberNames);
                    if (changeSetEntry.ValidationErrors != null)
                    {
                        changeSetEntry.ValidationErrors = changeSetEntry.ValidationErrors.Concat(new ValidationResultInfo[] { error }).ToArray();
                    }
                    else
                    {
                        changeSetEntry.ValidationErrors = new ValidationResultInfo[] { error };
                    }
                }
                else
                {
                    throw;
                }
            }
        }