Пример #1
0
        public void Replace_EntityExistsMoreThanOnce()
        {
            ChangeSet      changeSet = this.GenerateChangeSet();
            ChangeSetEntry op1       = changeSet.ChangeSetEntries.Skip(0).First();
            ChangeSetEntry op2       = changeSet.ChangeSetEntries.Skip(1).First();
            ChangeSetEntry op3       = changeSet.ChangeSetEntries.Skip(2).First();

            A currentEntity = new A(), originalEntity = new A(), returnedEntity = new A();

            op1.Entity         = currentEntity;
            op1.OriginalEntity = originalEntity;

            op2.Entity         = currentEntity;
            op2.OriginalEntity = originalEntity;

            op3.Entity         = currentEntity;
            op3.OriginalEntity = null;

            changeSet.Replace(currentEntity, returnedEntity);

            // Verify we returned the original
            Assert.AreSame(op1.Entity, currentEntity, "Expected to find the current entity.");
            Assert.AreSame(op1.OriginalEntity, originalEntity, "Expected to find the original entity.");
            Assert.AreSame(returnedEntity, changeSet.EntitiesToReplace[op1.Entity], "Expected to find the returned entity.");
            Assert.AreSame(op2.Entity, currentEntity, "Expected to find the current entity.");
            Assert.AreSame(op2.OriginalEntity, originalEntity, "Expected to find the original entity.");
            Assert.AreSame(returnedEntity, changeSet.EntitiesToReplace[op2.Entity], "Expected to find the returned entity.");
            Assert.AreSame(op3.Entity, currentEntity, "Expected to find the current entity.");
            Assert.IsNull(op3.OriginalEntity, "Expected to find a null original entity.");
            Assert.AreSame(returnedEntity, changeSet.EntitiesToReplace[op3.Entity], "Expected to find the returned entity.");
        }
Пример #2
0
        private static string GetAuthorizeMethodName(ChangeSetEntry entry)
        {
            switch (entry.Type)
            {
            case ChangeSetEntryType.DataModification:
                DataModificationEntry dataModification = (DataModificationEntry)entry;
                string operationName = null;
                if (dataModification.IsNew)
                {
                    operationName = ConventionBasedChangeSetConstants.AuthorizeMethodDataModificationInsert;
                }
                else if (dataModification.IsUpdate)
                {
                    operationName = ConventionBasedChangeSetConstants.AuthorizeMethodDataModificationUpdate;
                }
                else if (dataModification.IsDelete)
                {
                    operationName = ConventionBasedChangeSetConstants.AuthorizeMethodDataModificationDelete;
                }

                return(operationName + dataModification.EntitySetName);

            case ChangeSetEntryType.ActionInvocation:
                ActionInvocationEntry actionEntry = (ActionInvocationEntry)entry;
                return(ConventionBasedChangeSetConstants.AuthorizeMethodActionInvocationExecute +
                       actionEntry.ActionName);

            default:
                throw new InvalidOperationException(string.Format(
                                                        CultureInfo.InvariantCulture, Resources.InvalidChangeSetEntryType, entry.Type));
            }
        }
Пример #3
0
        public void Changeset_OriginalInvalidForInserts()
        {
            // can't specify an original for an insert operation
            TimestampEntityA curr = new TimestampEntityA {
                ID = 1, Version = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 }, ValueA = "Foo", ValueB = "Bar"
            };
            TimestampEntityA orig = new TimestampEntityA {
                ID = 1, Version = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 }, ValueA = "x", ValueB = "x"
            };
            ChangeSetEntry entry = new ChangeSetEntry(1, curr, orig, DomainOperation.Insert);
            ChangeSet      cs    = null;

            ExceptionHelper.ExpectInvalidOperationException(delegate
            {
                cs = new ChangeSet(new ChangeSetEntry[] { entry });
            },
                                                            string.Format(Resource.InvalidChangeSet, Resource.InvalidChangeSet_InsertsCantHaveOriginal));

            // get original should throw for insert operations
            entry = new ChangeSetEntry(1, curr, null, DomainOperation.Insert);
            cs    = new ChangeSet(new ChangeSetEntry[] { entry });
            ExceptionHelper.ExpectInvalidOperationException(delegate
            {
                cs.GetOriginal(curr);
            },
                                                            string.Format(Resource.ChangeSet_OriginalNotValidForInsert));
        }
        /// <inheritdoc/>
        public Task <bool> AuthorizeAsync(
            SubmitContext context,
            ChangeSetEntry entry,
            CancellationToken cancellationToken)
        {
            Ensure.NotNull(context, "context");
            bool result = true;

            Type       returnType = typeof(bool);
            string     methodName = ConventionBasedChangeSetAuthorizer.GetAuthorizeMethodName(entry);
            MethodInfo method     = this.targetType.GetQualifiedMethod(methodName);

            if (method != null && method.IsFamily &&
                method.ReturnType == returnType)
            {
                object target = null;
                if (!method.IsStatic)
                {
                    target = context.GetApiService <ApiBase>();
                    if (target == null ||
                        !this.targetType.IsAssignableFrom(target.GetType()))
                    {
                        return(Task.FromResult(result));
                    }
                }

                var parameters = method.GetParameters();
                if (parameters.Length == 0)
                {
                    result = (bool)method.Invoke(target, null);
                }
            }

            return(Task.FromResult(result));
        }
        public async Task DomainService_CallSubmitDirectly()
        {
            DomainServiceDescription description      = DomainServiceDescription.GetDescription(typeof(DomainMethod_ValidProvider_MultipleMethods));
            List <ChangeSetEntry>    changeSetEntries = new List <ChangeSetEntry>();

            ChangeSetEntry processCityOperation = new ChangeSetEntry();

            processCityOperation.Entity = new City {
                Name = "Redmond", CountyName = "King", StateName = "WA"
            };
            processCityOperation.DomainOperationEntry = description.GetCustomMethod(typeof(City), "ProcessCity");
            processCityOperation.Operation            = DomainOperation.Update;
            processCityOperation.EntityActions        = new EntityActionCollection {
                { "ProcessCity", new object[] { new byte[] { byte.MaxValue, byte.MinValue, 123 } } }
            };
            changeSetEntries.Add(processCityOperation);

            ChangeSet changeset = new ChangeSet(changeSetEntries);
            DomainMethod_ValidProvider_MultipleMethods myTestProvider = ServerTestHelper.CreateInitializedDomainService <DomainMethod_ValidProvider_MultipleMethods>(DomainOperationType.Submit);
            await myTestProvider.SubmitAsync(changeset, CancellationToken.None);

            // check that the domain services have invoked the domain method correctly by checking the internal variables set
            Assert.AreEqual <string>("ProcessCity_", myTestProvider.Invoked);
            Assert.AreEqual <int>(3, myTestProvider.InputData.Length);
            Assert.AreEqual <byte>(123, myTestProvider.InputData[2]);
        }
Пример #6
0
        public void Submit_Tree_Success()
        {
            Order order = new Order {
                OrderID = 1, OrderDate = DateTime.Now
            };
            Order_Detail d1 = new Order_Detail {
                ProductID = 1
            };
            Order_Detail d2 = new Order_Detail {
                ProductID = 2
            };
            Dictionary <string, int[]> detailsAssociation = new Dictionary <string, int[]>();

            detailsAssociation.Add("Order_Details", new int[] { 2, 3 });
            ChangeSetEntry[] changeSet = new ChangeSetEntry[] {
                new ChangeSetEntry {
                    Id = 1, Entity = order, Operation = ChangeOperation.Insert, Associations = detailsAssociation
                },
                new ChangeSetEntry {
                    Id = 2, Entity = d1, Operation = ChangeOperation.Insert
                },
                new ChangeSetEntry {
                    Id = 3, Entity = d2, Operation = ChangeOperation.Insert
                }
            };

            ChangeSetEntry[] resultChangeSet = this.ExecuteSubmit(TestConstants.CatalogUrl + "Submit", "Catalog", changeSet);
            Assert.Equal(3, resultChangeSet.Length);
            Assert.True(resultChangeSet.All(p => !p.HasError));
        }
        public void Submit_Validation_Failure()
        {
            Microsoft.Web.Http.Data.Test.Models.EF.Product newProduct = new Microsoft.Web.Http.Data.Test.Models.EF.Product { ProductID = 1, ProductName = String.Empty, UnitPrice = -1 };
            Microsoft.Web.Http.Data.Test.Models.EF.Product updateProduct = new Microsoft.Web.Http.Data.Test.Models.EF.Product { ProductID = 1, ProductName = new string('x', 50), UnitPrice = 55.77M };
            ChangeSetEntry[] changeSet = new ChangeSetEntry[] { 
                new ChangeSetEntry { Id = 1, Entity = newProduct, Operation = ChangeOperation.Insert },
                new ChangeSetEntry { Id = 2, Entity = updateProduct, Operation = ChangeOperation.Update }
            };

            HttpResponseMessage response = this.ExecuteSelfHostRequest("http://testhost/NorthwindEFTest/Submit", "NorthwindEFTest", changeSet);
            changeSet = response.Content.ReadAsAsync<ChangeSetEntry[]>().Result;

            // errors for the new product
            ValidationResultInfo[] errors = changeSet[0].ValidationErrors.ToArray();
            Assert.Equal(2, errors.Length);
            Assert.True(changeSet[0].HasError);

            // validation rule inferred from EF model
            Assert.Equal("ProductName", errors[0].SourceMemberNames.Single());
            Assert.Equal("The ProductName field is required.", errors[0].Message);

            // validation rule coming from buddy class
            Assert.Equal("UnitPrice", errors[1].SourceMemberNames.Single());
            Assert.Equal("The field UnitPrice must be between 0 and 1000000.", errors[1].Message);

            // errors for the updated product
            errors = changeSet[1].ValidationErrors.ToArray();
            Assert.Equal(1, errors.Length);
            Assert.True(changeSet[1].HasError);

            // validation rule inferred from EF model
            Assert.Equal("ProductName", errors[0].SourceMemberNames.Single());
            Assert.Equal("The field ProductName must be a string with a maximum length of 40.", errors[0].Message);
        }
        public void Changeset_OriginalInvalidForInserts()
        {
            // can't specify an original for an insert operation
            Product curr = new Product {
                ProductID = 1
            };
            Product orig = new Product {
                ProductID = 1
            };
            ChangeSetEntry entry = new ChangeSetEntry {
                Id = 1, Entity = curr, OriginalEntity = orig, Operation = ChangeOperation.Insert
            };
            ChangeSet cs = null;

            Assert.Throws <InvalidOperationException>(delegate
            {
                cs = new ChangeSet(new ChangeSetEntry[] { entry });
            },
                                                      String.Format(Resource.InvalidChangeSet, Resource.InvalidChangeSet_InsertsCantHaveOriginal));

            // get original should throw for insert operations
            entry = new ChangeSetEntry {
                Id = 1, Entity = curr, OriginalEntity = null, Operation = ChangeOperation.Insert
            };
            cs = new ChangeSet(new ChangeSetEntry[] { entry });
            Assert.Throws <InvalidOperationException>(delegate
            {
                cs.GetOriginal(curr);
            },
                                                      String.Format(Resource.ChangeSet_OriginalNotValidForInsert));
        }
Пример #9
0
 /// <inheritdoc/>
 public Task OnExecutedEntryAsync(
     SubmitContext context,
     ChangeSetEntry entry,
     CancellationToken cancellationToken)
 {
     return(this.InvokeFilterMethodAsync(context, entry, "ed"));
 }
Пример #10
0
        private static string GetMethodName(ChangeSetEntry entry, string suffix)
        {
            switch (entry.Type)
            {
            case ChangeSetEntryType.DataModification:
                DataModificationEntry dataModification = (DataModificationEntry)entry;
                string operationName = null;
                if (dataModification.IsNew)
                {
                    operationName = "Insert";
                }
                else if (dataModification.IsUpdate)
                {
                    operationName = "Updat";
                }
                else if (dataModification.IsDelete)
                {
                    operationName = "Delet";
                }

                return("On" + operationName + suffix + dataModification.EntitySetName);

            case ChangeSetEntryType.ActionInvocation:
                ActionInvocationEntry actionEntry = (ActionInvocationEntry)entry;
                return("OnExecut" + suffix + actionEntry.ActionName);

            default:
                throw new InvalidOperationException(string.Format(
                                                        CultureInfo.InvariantCulture, Resources.InvalidChangeSetEntryType, entry.Type));
            }
        }
Пример #11
0
        public static ChangeSetEntry GetCustomUpdateChangeSetEntry(OperationContext context, Expression expression, object original)
        {
            context.OperationName = Utility.GetNameFromLambda(expression);
            IEnumerable <object> parameterValues = Utility.GetParametersFromLambda(expression);
            object entity = parameterValues.First();

            ChangeSetEntry changeSetEntry = new ChangeSetEntry(Utility.DefaultChangeSetEntryId, entity, original, DomainOperation.Update);

            DomainOperationEntry domainOperationEntry = context.DomainServiceDescription.GetCustomMethod(entity.GetType(), context.OperationName);

            if (domainOperationEntry == null)
            {
                throw new InvalidOperationException(string.Format(
                                                        CultureInfo.CurrentCulture,
                                                        Resources.NoCustomUpdateOperation,
                                                        context.OperationName,
                                                        context.DomainServiceDescription.DomainServiceType));
            }
            changeSetEntry.EntityActions = new EntityActionCollection
            {
                { context.OperationName, parameterValues.Skip(1).ToArray() },
            };

            return(changeSetEntry);
        }
Пример #12
0
        /// <summary>
        /// Updates each entry in the ChangeSet with its corresponding conflict info.
        /// </summary>
        /// <param name="operationConflictMap">Map of conflicts to their corresponding operations entries.</param>
        private void SetChangeSetConflicts(Dictionary <ObjectStateEntry, ChangeSetEntry> operationConflictMap)
        {
            object    storeValue;
            EntityKey refreshEntityKey;

            foreach (var conflictEntry in operationConflictMap)
            {
                ObjectStateEntry stateEntry = conflictEntry.Key;

                if (stateEntry.State == EntityState.Unchanged)
                {
                    continue;
                }

                // Note: we cannot call Refresh StoreWins since this will overwrite Current entity and remove the optimistic concurrency ex.
                ChangeSetEntry operationInConflict = conflictEntry.Value;
                refreshEntityKey = RefreshContext.CreateEntityKey(stateEntry.EntitySet.Name, stateEntry.Entity);
                RefreshContext.TryGetObjectByKey(refreshEntityKey, out storeValue);
                operationInConflict.StoreEntity = storeValue;

                // StoreEntity will be null if the entity has been deleted in the store (i.e. Delete/Delete conflict)
                bool isDeleted = (operationInConflict.StoreEntity == null);
                if (isDeleted)
                {
                    operationInConflict.IsDeleteConflict = true;
                }
                else
                {
                    // Determine which members are in conflict by comparing original values to the current DB values
                    PropertyDescriptorCollection propDescriptors = TypeDescriptor.GetProperties(operationInConflict.Entity.GetType());
                    List <string>      membersInConflict         = new List <string>();
                    object             originalValue;
                    PropertyDescriptor pd;
                    for (int i = 0; i < stateEntry.OriginalValues.FieldCount; i++)
                    {
                        originalValue = stateEntry.OriginalValues.GetValue(i);
                        if (originalValue is DBNull)
                        {
                            originalValue = null;
                        }

                        string propertyName = stateEntry.OriginalValues.GetName(i);
                        pd = propDescriptors[propertyName];
                        if (pd == null)
                        {
                            // This might happen in the case of a private model
                            // member that isn't mapped
                            continue;
                        }

                        if (!Object.Equals(originalValue, pd.GetValue(operationInConflict.StoreEntity)))
                        {
                            membersInConflict.Add(pd.Name);
                        }
                    }
                    operationInConflict.ConflictMembers = membersInConflict;
                }
            }
        }
        public async Task SubmitAsync_Exceptions()
        {
            var mockDomainClient = new CitiesMockDomainClient();

            Cities.CityDomainContext ctx = new Cities.CityDomainContext(mockDomainClient);
            DomainOperationException ex  = new DomainOperationException("Submit Failed!", OperationErrorStatus.ServerError, 42, "StackTrace");

            // If cancellation results in request beeing cancelled the result should be cancelled
            mockDomainClient.SubmitCompletedResult = Task.FromException <SubmitCompletedResult>(ex);

            ctx.Cities.Add(new City()
            {
                Name = "NewCity", StateName = "NN", CountyName = "NewCounty"
            });

            Assert.IsTrue(ctx.EntityContainer.HasChanges);
            var submitEx = await ExceptionHelper.ExpectExceptionAsync <SubmitOperationException>(() => ctx.SubmitChangesAsync());

            // verify the exception properties
            Assert.AreEqual(string.Format(Resource.DomainContext_SubmitOperationFailed, ex.Message), submitEx.Message);
            Assert.AreEqual(ex.StackTrace, submitEx.StackTrace);
            Assert.AreEqual(ex.Status, submitEx.Status);
            Assert.AreEqual(ex.ErrorCode, submitEx.ErrorCode);
            Assert.IsTrue(ctx.EntityContainer.HasChanges);

            // now test with validation exception
            var changeSet = ctx.EntityContainer.GetChanges();
            IEnumerable <ChangeSetEntry> entries = ChangeSetBuilder.Build(changeSet);
            ChangeSetEntry entry = entries.First();

            entry.ValidationErrors = new ValidationResultInfo[] { new ValidationResultInfo("Foo", new string[] { "Bar" }) };

            mockDomainClient.SubmitCompletedResult = Task.FromResult(new SubmitCompletedResult(changeSet, entries));
            submitEx = await ExceptionHelper.ExpectExceptionAsync <SubmitOperationException>(() => ctx.SubmitChangesAsync());

            // verify the exception properties
            Assert.AreEqual(Resource.DomainContext_SubmitOperationFailed_Validation, submitEx.Message);
            Assert.AreEqual(OperationErrorStatus.ValidationFailed, submitEx.Status);
            Assert.AreEqual(1, submitEx.EntitiesInError.Count);
            Assert.AreEqual(entry.ClientEntity, submitEx.EntitiesInError.First());

            // now test again with conflicts
            entries = ChangeSetBuilder.Build(changeSet);
            entry   = entries.First();
            entry.ConflictMembers = new string[] { nameof(City.CountyName) };
            entry.StoreEntity     = new City()
            {
                CountyName = "OtherCounty"
            };

            mockDomainClient.SubmitCompletedResult = Task.FromResult(new SubmitCompletedResult(changeSet, entries));
            submitEx = await ExceptionHelper.ExpectExceptionAsync <SubmitOperationException>(() => ctx.SubmitChangesAsync());

            // verify the exception properties
            Assert.AreEqual(Resource.DomainContext_SubmitOperationFailed_Conflicts, submitEx.Message);
            Assert.AreEqual(OperationErrorStatus.Conflicts, submitEx.Status);
            Assert.AreEqual(1, submitEx.EntitiesInError.Count);
            Assert.AreEqual(entry.ClientEntity, submitEx.EntitiesInError.First());
        }
Пример #14
0
 private static string GetErrorMessageForConflicts(ChangeSetEntry changeSetEntry)
 {
     return(string.Format(
                CultureInfo.CurrentCulture,
                "There are conflicts for one or more members on the entity '{0}': {1}.",
                changeSetEntry.Entity.GetType(),
                string.Join(", ", changeSetEntry.ConflictMembers)));
 }
Пример #15
0
 private static string GetErrorMessageForValidation(ChangeSetEntry changeSetEntry)
 {
     return(string.Format(
                CultureInfo.CurrentCulture,
                "Validation failed for the entity '{0}' with one or more errors: {1}.",
                changeSetEntry.Entity.GetType(),
                string.Join(", ", changeSetEntry.ValidationErrors.Select(vri => ErrorUtility.GetErrorMessageForValidation(vri)))));
 }
 /// <inheritdoc/>
 public Task OnExecutedEntryAsync(
     SubmitContext context,
     ChangeSetEntry entry,
     CancellationToken cancellationToken)
 {
     return(this.InvokeFilterMethodAsync(
                context, entry, ConventionBasedChangeSetConstants.FilterMethodNamePostFilterSuffix));
 }
Пример #17
0
        /// <summary>
        /// Record an operation invocation, and validate that all parent operations
        /// have been completed
        /// </summary>
        /// <param name="entity"></param>
        private void SetOperationInvoked(object entity)
        {
            VerifyOperationOrdering(entity, false);

            // record the operation invocation
            ChangeSetEntry op = this.ChangeSet.ChangeSetEntries.Single(p => p.Entity == entity);

            _invokedOperations.Add(op);
        }
Пример #18
0
        public void TestAssociations_UpdatedReferencingNew()
        {
            NorthwindEntityContainer ec = new NorthwindEntityContainer();
            Product p1 = new Product {
                ProductID = 1, CategoryID = 1
            };
            Product p2 = new Product {
                ProductID = 2, CategoryID = 2
            };
            Category c1 = new Category {
                CategoryID = 1
            };
            Category c2 = new Category {
                CategoryID = 2
            };

            ec.LoadEntities(new Entity[] { p1, p2, c1, c2 });

            // take two existing parents (the FK side of the association)
            // access their existing children
            Category prevCat = p1.Category;

            Assert.IsNotNull(prevCat);
            prevCat = p2.Category;
            Assert.IsNotNull(prevCat);

            // create two new children
            Category newCat1 = new Category {
                CategoryID = 3
            };
            Category newCat2 = new Category {
                CategoryID = 4
            };

            // assign the two new children
            p1.Category = newCat1;
            p2.Category = newCat2;

            EntityChangeSet cs = ec.GetChanges();

            Assert.AreEqual(2, cs.AddedEntities.Count);
            Assert.AreEqual(2, cs.ModifiedEntities.Count);

            List <ChangeSetEntry> entries = ChangeSetBuilder.Build(cs);
            ChangeSetEntry        entry   = entries.Single(p => p.Entity == p1);

            // the bug was that we weren't populating the association map in this
            // scenario since previously we required BOTH parent and child to be new.
            // We've relaxed that to ensure that if the child is new, the association
            // shows up in the map.
            Assert.IsNotNull(entry.Associations);
            int[]    ids        = entry.Associations["Category"];
            Category referenced = (Category)entries.Single(p => p.Id == ids.Single()).Entity;

            Assert.AreSame(newCat1, referenced);
        }
Пример #19
0
        private bool InvokeSaveChanges(bool retryOnConflict)
        {
            try
            {
                this.ObjectContext.SaveChanges();
            }
            catch (OptimisticConcurrencyException ex)
            {
                // Map the operations that could have caused a conflict to an entity.
                Dictionary <ObjectStateEntry, ChangeSetEntry> operationConflictMap = new Dictionary <ObjectStateEntry, ChangeSetEntry>();
                foreach (ObjectStateEntry conflict in ex.StateEntries)
                {
                    ChangeSetEntry entry = this.ChangeSet.ChangeSetEntries.SingleOrDefault(p => object.ReferenceEquals(p.Entity, conflict.Entity));
                    if (entry == null)
                    {
                        // If we're unable to find the object in our changeset, propagate
                        // the original exception
                        throw;
                    }
                    operationConflictMap.Add(conflict, entry);
                }

                this.SetChangeSetConflicts(operationConflictMap);

                // Call out to any user resolve code and resubmit if all conflicts
                // were resolved
                if (retryOnConflict && this.ResolveConflicts(ex.StateEntries))
                {
                    // clear the conflics from the entries
                    foreach (ChangeSetEntry entry in this.ChangeSet.ChangeSetEntries)
                    {
                        entry.StoreEntity      = null;
                        entry.ConflictMembers  = null;
                        entry.IsDeleteConflict = false;
                    }

                    // If all conflicts were resolved attempt a resubmit
                    return(this.InvokeSaveChanges(/* retryOnConflict */ false));
                }

                // if the conflict wasn't resolved, call the error handler
                this.OnError(new DomainServiceErrorInfo(ex));

                // if there was a conflict but no conflict information was
                // extracted to the individual entries, we need to ensure the
                // error makes it back to the client
                if (!this.ChangeSet.HasError)
                {
                    throw;
                }

                return(false);
            }

            return(true);
        }
        public void Submit_Multiple_Success()
        {
            Order order = new Order { OrderID = 1, OrderDate = DateTime.Now };
            Product product = new Product { ProductID = 1, ProductName = "Choco Wafers" };
            ChangeSetEntry[] changeSet = new ChangeSetEntry[] { 
                new ChangeSetEntry { Id = 1, Entity = order, Operation = ChangeOperation.Insert },
                new ChangeSetEntry { Id = 2, Entity = product, Operation = ChangeOperation.Update }
            };

            ChangeSetEntry[] resultChangeSet = this.ExecuteSubmit(TestConstants.CatalogUrl + "Submit", "Catalog", changeSet);
            Assert.Equal(2, resultChangeSet.Length);
            Assert.True(resultChangeSet.All(p => !p.HasError));
        }
Пример #21
0
        public void GetOriginal()
        {
            ChangeSet      changeSet = this.GenerateChangeSet();
            ChangeSetEntry op        = changeSet.ChangeSetEntries.First();

            A currentEntity = new A(), originalEntity = new A();

            op.Entity         = currentEntity;
            op.OriginalEntity = originalEntity;

            A changeSetOriginalEntity = changeSet.GetOriginal(currentEntity);

            // Verify we returned the original
            Assert.AreSame(originalEntity, changeSetOriginalEntity, "Expected to find original entity.");
        }
Пример #22
0
        public void Constructor_Initialization()
        {
            ChangeSet changeSet;
            IEnumerable <ChangeSetEntry> ops = this.GenerateEntityOperations(false);

            changeSet = new ChangeSet(ops);
            Assert.AreEqual(ops.Count(), ops.Intersect(changeSet.ChangeSetEntries).Count(), "Expected ChangeSetEntries count to match what was provided to the constructor");

            ChangeSetEntry changeSetEntry = new ChangeSetEntry(0, new E(), new E()
            {
                E_ID2 = 5
            }, DomainOperation.Update);

            Assert.IsTrue(changeSetEntry.HasMemberChanges);
        }
        /// <inheritdoc/>
        public Task ValidateEntityAsync(
            SubmitContext context,
            ChangeSetEntry entry,
            ValidationResults validationResults,
            CancellationToken cancellationToken)
        {
            Ensure.NotNull(validationResults, "validationResults");
            DataModificationEntry dataModificationEntry = entry as DataModificationEntry;

            if (dataModificationEntry != null)
            {
                object entity = dataModificationEntry.Entity;

                // TODO GitHubIssue#50 : should this PropertyDescriptorCollection be cached?
                PropertyDescriptorCollection properties =
                    new DataAnnotations.AssociatedMetadataTypeTypeDescriptionProvider(entity.GetType())
                    .GetTypeDescriptor(entity).GetProperties();

                DataAnnotations.ValidationContext validationContext = new DataAnnotations.ValidationContext(entity);

                foreach (PropertyDescriptor property in properties)
                {
                    validationContext.MemberName = property.Name;

                    IEnumerable <DataAnnotations.ValidationAttribute> validationAttributes =
                        property.Attributes.OfType <DataAnnotations.ValidationAttribute>();
                    foreach (DataAnnotations.ValidationAttribute validationAttribute in validationAttributes)
                    {
                        object value = property.GetValue(entity);
                        DataAnnotations.ValidationResult validationResult =
                            validationAttribute.GetValidationResult(value, validationContext);
                        if (validationResult != DataAnnotations.ValidationResult.Success)
                        {
                            validationResults.Add(new ValidationResult()
                            {
                                Id           = validationAttribute.GetType().FullName,
                                Message      = validationResult.ErrorMessage,
                                Severity     = ValidationSeverity.Error,
                                Target       = entity,
                                PropertyName = property.Name
                            });
                        }
                    }
                }
            }

            return(Task.WhenAll());
        }
Пример #24
0
        public void Replace()
        {
            ChangeSet      changeSet = this.GenerateChangeSet();
            ChangeSetEntry op        = changeSet.ChangeSetEntries.First();

            A currentEntity = new A(), originalEntity = new A(), returnedEntity = new A();

            op.Entity         = currentEntity;
            op.OriginalEntity = originalEntity;

            changeSet.Replace(currentEntity, returnedEntity);

            Assert.AreSame(op.Entity, currentEntity, "Expected to find current entity.");
            Assert.AreSame(op.OriginalEntity, originalEntity, "Expected to find original entity.");
            Assert.AreSame(returnedEntity, changeSet.EntitiesToReplace[op.Entity], "Expected to find returned entity.");
        }
        public void GetOriginal()
        {
            ChangeSet      changeSet = this.GenerateChangeSet();
            ChangeSetEntry op        = changeSet.ChangeSetEntries.First();

            Product currentEntity  = new Product();
            Product originalEntity = new Product();

            op.Entity         = currentEntity;
            op.OriginalEntity = originalEntity;

            Product changeSetOriginalEntity = changeSet.GetOriginal(currentEntity);

            // Verify we returned the original
            Assert.Same(originalEntity, changeSetOriginalEntity);
        }
        public void Submit_Tree_Success()
        {
            Order order = new Order { OrderID = 1, OrderDate = DateTime.Now };
            Order_Detail d1 = new Order_Detail { ProductID = 1 };
            Order_Detail d2 = new Order_Detail { ProductID = 2 };
            Dictionary<string, int[]> detailsAssociation = new Dictionary<string, int[]>();
            detailsAssociation.Add("Order_Details", new int[] { 2, 3 });
            ChangeSetEntry[] changeSet = new ChangeSetEntry[] { 
                new ChangeSetEntry { Id = 1, Entity = order, Operation = ChangeOperation.Insert, Associations = detailsAssociation },
                new ChangeSetEntry { Id = 2, Entity = d1, Operation = ChangeOperation.Insert },
                new ChangeSetEntry { Id = 3, Entity = d2, Operation = ChangeOperation.Insert }
            };

            ChangeSetEntry[] resultChangeSet = this.ExecuteSubmit(TestConstants.CatalogUrl + "Submit", "Catalog", changeSet);
            Assert.Equal(3, resultChangeSet.Length);
            Assert.True(resultChangeSet.All(p => !p.HasError));
        }
        private static object[] GetParameters(ChangeSetEntry entry)
        {
            switch (entry.Type)
            {
            case ChangeSetEntryType.DataModification:
                DataModificationEntry dataModification = (DataModificationEntry)entry;
                return(new object[] { dataModification.Entity });

            case ChangeSetEntryType.ActionInvocation:
                ActionInvocationEntry actionEntry = (ActionInvocationEntry)entry;
                return(actionEntry.GetArgumentArray());

            default:
                throw new InvalidOperationException(string.Format(
                                                        CultureInfo.InvariantCulture, Resources.InvalidChangeSetEntryType, entry.Type));
            }
        }
Пример #28
0
        public void Submit_Authorization_Success()
        {
            TestAuthAttribute.Reset();

            Product product = new Product {
                ProductID = 1, ProductName = "Choco Wafers"
            };

            ChangeSetEntry[] changeSet = new ChangeSetEntry[] {
                new ChangeSetEntry {
                    Id = 1, Entity = product, Operation = ChangeOperation.Update
                }
            };

            ChangeSetEntry[] resultChangeSet = this.ExecuteSubmit("http://testhost/TestAuth/Submit", "TestAuth", changeSet);
            Assert.Equal(1, resultChangeSet.Length);
            Assert.True(TestAuthAttribute.Log.SequenceEqual(new string[] { "Global", "Class", "SubmitMethod", "UserMethod" }));
        }
Пример #29
0
        public void Submit_Validation_Failure()
        {
            Microsoft.Web.Http.Data.Test.Models.EF.Product newProduct = new Microsoft.Web.Http.Data.Test.Models.EF.Product {
                ProductID = 1, ProductName = String.Empty, UnitPrice = -1
            };
            Microsoft.Web.Http.Data.Test.Models.EF.Product updateProduct = new Microsoft.Web.Http.Data.Test.Models.EF.Product {
                ProductID = 1, ProductName = new string('x', 50), UnitPrice = 55.77M
            };
            ChangeSetEntry[] changeSet = new ChangeSetEntry[] {
                new ChangeSetEntry {
                    Id = 1, Entity = newProduct, Operation = ChangeOperation.Insert
                },
                new ChangeSetEntry {
                    Id = 2, Entity = updateProduct, Operation = ChangeOperation.Update
                }
            };

            HttpResponseMessage response = this.ExecuteSelfHostRequest("http://testhost/NorthwindEFTest/Submit", "NorthwindEFTest", changeSet);

            changeSet = response.Content.ReadAsAsync <ChangeSetEntry[]>().Result;

            // errors for the new product
            ValidationResultInfo[] errors = changeSet[0].ValidationErrors.ToArray();
            Assert.Equal(2, errors.Length);
            Assert.True(changeSet[0].HasError);

            // validation rule inferred from EF model
            Assert.Equal("ProductName", errors[0].SourceMemberNames.Single());
            Assert.Equal("The ProductName field is required.", errors[0].Message);

            // validation rule coming from buddy class
            Assert.Equal("UnitPrice", errors[1].SourceMemberNames.Single());
            Assert.Equal("The field UnitPrice must be between 0 and 1000000.", errors[1].Message);

            // errors for the updated product
            errors = changeSet[1].ValidationErrors.ToArray();
            Assert.Equal(1, errors.Length);
            Assert.True(changeSet[1].HasError);

            // validation rule inferred from EF model
            Assert.Equal("ProductName", errors[0].SourceMemberNames.Single());
            Assert.Equal("The field ProductName must be a string with a maximum length of 40.", errors[0].Message);
        }
Пример #30
0
        public void Submit_ResolveActions_UnsupportedAction()
        {
            Product product = new Product {
                ProductID = 1, ProductName = "Choco Wafers"
            };

            ChangeSetEntry[] changeSet = new ChangeSetEntry[] {
                new ChangeSetEntry {
                    Id = 1, Entity = product, Operation = ChangeOperation.Delete
                }
            };

            HttpConfiguration         configuration        = new HttpConfiguration();
            HttpControllerDescriptor  controllerDescriptor = new HttpControllerDescriptor(configuration, "NorthwindEFTestController", typeof(NorthwindEFTestController));
            DataControllerDescription description          = DataControllerDescription.GetDescription(controllerDescriptor);

            Assert.Throws <InvalidOperationException>(
                () => DataController.ResolveActions(description, changeSet),
                String.Format(Resource.DataController_InvalidAction, "Delete", "Product"));
        }
        private Task InvokeFilterMethodAsync(
            SubmitContext context,
            ChangeSetEntry entry,
            string methodNameSuffix)
        {
            string methodName = ConventionBasedChangeSetEntryFilter.GetMethodName(entry, methodNameSuffix);

            object[] parameters = ConventionBasedChangeSetEntryFilter.GetParameters(entry);

            MethodInfo method = this.targetType.GetQualifiedMethod(methodName);

            if (method != null &&
                (method.ReturnType == typeof(void) ||
                 typeof(Task).IsAssignableFrom(method.ReturnType)))
            {
                object target = null;
                if (!method.IsStatic)
                {
                    target = context.ApiContext.GetProperty(
                        typeof(Api).AssemblyQualifiedName);
                    if (target == null ||
                        !this.targetType.IsAssignableFrom(target.GetType()))
                    {
                        return(Task.WhenAll());
                    }
                }

                ParameterInfo[] methodParameters = method.GetParameters();
                if (ConventionBasedChangeSetEntryFilter.ParametersMatch(methodParameters, parameters))
                {
                    object result     = method.Invoke(target, parameters);
                    Task   resultTask = result as Task;
                    if (resultTask != null)
                    {
                        return(resultTask);
                    }
                }
            }

            return(Task.WhenAll());
        }
        public void DomainService_CallSubmitDirectly()
        {
            DomainServiceDescription description = DomainServiceDescription.GetDescription(typeof(DomainMethod_ValidProvider_MultipleMethods));
            List<ChangeSetEntry> changeSetEntries = new List<ChangeSetEntry>();

            ChangeSetEntry processCityOperation = new ChangeSetEntry();
            processCityOperation.Entity = new City { Name = "Redmond", CountyName = "King", StateName = "WA" };
            processCityOperation.DomainOperationEntry = description.GetCustomMethod(typeof(City), "ProcessCity");
            processCityOperation.Operation = DomainOperation.Update;
            processCityOperation.EntityActions = new EntityActionCollection { { "ProcessCity", new object[] { new byte[] { byte.MaxValue, byte.MinValue, 123 } } } };
            changeSetEntries.Add(processCityOperation);

            ChangeSet changeset = new ChangeSet(changeSetEntries);
            DomainMethod_ValidProvider_MultipleMethods myTestProvider = ServerTestHelper.CreateInitializedDomainService<DomainMethod_ValidProvider_MultipleMethods>(DomainOperationType.Submit);
            myTestProvider.Submit(changeset);

            // check that the domain services have invoked the domain method correctly by checking the internal variables set
            Assert.AreEqual<string>("ProcessCity_", myTestProvider.Invoked);
            Assert.AreEqual<int>(3, myTestProvider.InputData.Length);
            Assert.AreEqual<byte>(123, myTestProvider.InputData[2]);
        }
Пример #33
0
        public void Submit_Authorization_Fail_Global()
        {
            TestAuthAttribute.Reset();

            Product product = new Product {
                ProductID = 1, ProductName = "Choco Wafers"
            };

            ChangeSetEntry[] changeSet = new ChangeSetEntry[] {
                new ChangeSetEntry {
                    Id = 1, Entity = product, Operation = ChangeOperation.Update
                }
            };

            TestAuthAttribute.FailLevel = "Global";
            HttpResponseMessage response = this.ExecuteSelfHostRequest("http://testhost/TestAuth/Submit", "TestAuth", changeSet);

            Assert.True(TestAuthAttribute.Log.SequenceEqual(new string[] { "Global" }));
            Assert.Equal("Not Authorized", response.ReasonPhrase);
            Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
        }
Пример #34
0
        /// <summary>
        /// Helper method performs a submit operation against a given proxy instance.
        /// </summary>
        /// <param name="domainService">The type of <see cref="DomainService"/> to perform this query operation against.</param>
        /// <param name="context">The current context.</param>
        /// <param name="domainServiceInstances">The list of tracked <see cref="DomainService"/> instances that any newly created
        /// <see cref="DomainServices"/> will be added to.</param>
        /// <param name="currentOriginalEntityMap">The mapping of current and original entities used with the utility <see cref="DomainServiceProxy.AssociateOriginal"/> method.</param>
        /// <param name="entity">The entity being submitted.</param>
        /// <param name="operationName">The name of the submit operation. For CUD operations, this can be null.</param>
        /// <param name="parameters">The submit operation parameters.</param>
        /// <param name="domainOperation">The type of submit operation.</param>
        /// <exception cref="ArgumentNullException">if <paramref name="context"/> is null.</exception>
        /// <exception cref="ArgumentNullException">if <paramref name="entity"/> is null.</exception>
        /// <exception cref="OperationException">if operation errors are thrown during execution of the submit operation.</exception>
        public static void Submit(Type domainService, DomainServiceContext context, IList <DomainService> domainServiceInstances, IDictionary <object, object> currentOriginalEntityMap, object entity, string operationName, object[] parameters, DomainOperation domainOperation)
        {
            context = new DomainServiceContext(context, DomainOperationType.Submit);
            DomainService service = CreateDomainServiceInstance(domainService, context, domainServiceInstances);

            object originalEntity = null;

            currentOriginalEntityMap.TryGetValue(entity, out originalEntity);

            // if this is an update operation, regardless of whether original
            // values have been specified, we need to mark the operation as
            // modified
            bool hasMemberChanges = domainOperation == DomainOperation.Update;

            // when custom methods are invoked, the operation type
            // is Update
            if (domainOperation == DomainOperation.Custom)
            {
                domainOperation = DomainOperation.Update;
            }

            ChangeSetEntry changeSetEntry = new ChangeSetEntry(1, entity, originalEntity, domainOperation);

            changeSetEntry.HasMemberChanges = hasMemberChanges;
            if (!string.IsNullOrEmpty(operationName))
            {
                changeSetEntry.EntityActions = new List <Serialization.KeyValue <string, object[]> >();
                changeSetEntry.EntityActions.Add(new Serialization.KeyValue <string, object[]>(operationName, parameters));
            }

            ChangeSet changeSet = new ChangeSet(new[] { changeSetEntry });

            service.SubmitAsync(changeSet, CancellationToken.None)
            .GetAwaiter().GetResult();

            if (changeSetEntry.HasError)
            {
                throw new OperationException(Resource.DomainServiceProxy_OperationError, changeSetEntry.ValidationErrors);
            }
        }
        public void Changeset_OriginalInvalidForInserts()
        {
            // can't specify an original for an insert operation
            TimestampEntityA curr = new TimestampEntityA { ID = 1, Version = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 }, ValueA = "Foo", ValueB = "Bar" } ;
            TimestampEntityA orig = new TimestampEntityA { ID = 1, Version = new byte[] { 8, 7, 6, 5, 4, 3, 2, 1 }, ValueA = "x", ValueB = "x" };
            ChangeSetEntry entry = new ChangeSetEntry(1, curr, orig, DomainOperation.Insert);
            ChangeSet cs = null;
            ExceptionHelper.ExpectInvalidOperationException(delegate 
            {
                cs = new ChangeSet(new ChangeSetEntry[] { entry });
            }, 
            string.Format(Resource.InvalidChangeSet, Resource.InvalidChangeSet_InsertsCantHaveOriginal));

            // get original should throw for insert operations
            entry = new ChangeSetEntry(1, curr, null, DomainOperation.Insert);
            cs = new ChangeSet(new ChangeSetEntry[] { entry });
            ExceptionHelper.ExpectInvalidOperationException(delegate
            {
                cs.GetOriginal(curr);
            },
            string.Format(Resource.ChangeSet_OriginalNotValidForInsert));
        }
        public void Authorization_Custom_Authorization_On_CUD()
        {
            // Specifically, the City data is marked so that no one can delete a Zip code
            // from WA unless their user name is WAGuy
            MockUser notWaGuy = new MockUser("notWAGuy");
            notWaGuy.IsAuthenticated = true;

            DomainServiceDescription serviceDescription = DomainServiceDescription.GetDescription(typeof(CityDomainService));
            Zip zip = null;

            // First execute a query to get some zips
            DomainOperationEntry getZipsQuery = serviceDescription.GetQueryMethod("GetZips");
            DomainServiceContext ctxt = new DomainServiceContext(new MockDataService(notWaGuy), DomainOperationType.Query);

            using (CityDomainService cities = new CityDomainService())
            {
                // Now prepare for a query to find a Zip in WA
                ctxt = new DomainServiceContext(new MockDataService(notWaGuy), DomainOperationType.Query);
                cities.Initialize(ctxt);

                int count = -1;
                IEnumerable<ValidationResult> validationErrors = null;
                IEnumerable result = cities.Query(new QueryDescription(getZipsQuery), out validationErrors, out count);

                zip = result.OfType<Zip>().FirstOrDefault(z => z.StateName == "WA");
                Assert.IsNotNull(zip, "Could not find a zip code in WA");
            }

            // Prepare a submit to delete this zip from a user who is not authorized
            using (CityDomainService cities = new CityDomainService())
            {
                // Now prepare for a query to find a Zip in WA
                ctxt = new DomainServiceContext(new MockDataService(notWaGuy), DomainOperationType.Submit);
                cities.Initialize(ctxt);

                // Prepare an attempt to delete this with a user whose name is not WAGuy
                // This should fail due to a custom auth attribute
                List<ChangeSetEntry> entries = new List<ChangeSetEntry>();

                ChangeSetEntry entry = new ChangeSetEntry(1, zip, zip, DomainOperation.Delete);
                entries.Add(entry);
                UnauthorizedAccessException exception = null;
                try
                {
                    ChangeSetProcessor.Process(cities, entries);
                }
                catch (UnauthorizedAccessException ex)
                {
                    exception = ex;
                }
                Assert.IsNotNull(exception, "Expected failure attempting to delete a zip from WA with inappropriate user name");
                Assert.AreEqual("Only one user can delete zip codes from that state, and it isn't you.", exception.Message);
            }

            // Now do that again but with a user who is WAGuy -- it should succeed
            using (CityDomainService cities = new CityDomainService())
            {
                MockUser waGuy = new MockUser("WAGuy");
                waGuy.IsAuthenticated = true;

                // Now try a submit where the user *is* Mathew to validate we succeed
                ctxt = new DomainServiceContext(new MockDataService(waGuy), DomainOperationType.Submit);
                cities.Initialize(ctxt);
                List<ChangeSetEntry> entries = new List<ChangeSetEntry>();

                ChangeSetEntry entry = new ChangeSetEntry(1, zip, zip, DomainOperation.Delete);
                entries.Add(entry);
                Exception exception = null;
                try
                {
                    ChangeSetProcessor.Process(cities, entries);
                }
                catch (UnauthorizedAccessException ex)
                {
                    exception = ex;
                }
                Assert.IsNull(exception, "Expected success attempting to delete a zip from WA with inappropriate user name");
            }
        }
        public void Constructor_Initialization()
        {
            ChangeSet changeSet;
            IEnumerable<ChangeSetEntry> ops = this.GenerateEntityOperations(false);

            changeSet = new ChangeSet(ops);
            Assert.AreEqual(ops.Count(), ops.Intersect(changeSet.ChangeSetEntries).Count(), "Expected ChangeSetEntries count to match what was provided to the constructor");

            ChangeSetEntry changeSetEntry = new ChangeSetEntry(0, new E(), new E() { E_ID2 = 5 }, DomainOperation.Update);
            Assert.IsTrue(changeSetEntry.HasMemberChanges);
        }
 /// <summary>
 /// Execute a full roundtrip Submit request for the specified changeset, going through
 /// the full serialization pipeline.
 /// </summary>
 private ChangeSetEntry[] ExecuteSubmit(string url, string controllerName, ChangeSetEntry[] changeSet)
 {
     HttpResponseMessage response = this.ExecuteSelfHostRequest(url, controllerName, changeSet);
     ChangeSetEntry[] resultChangeSet = GetChangesetResponse(response);
     return changeSet;
 }
        public void Authorization_Custom_Authorization_On_Custom_Update()
        {
            // Specifically, the City data is marked so that no one can delete a Zip code
            // from WA unless their user name is WAGuy
            MockUser notWaGuy = new MockUser("notWAGuy");
            notWaGuy.IsAuthenticated = true;

            DomainServiceDescription serviceDescription = DomainServiceDescription.GetDescription(typeof(CityDomainService));
            City city = null;

            // Execute a query to get a City from WA
            using (CityDomainService cities = new CityDomainService())
            {
                DomainOperationEntry getCitiesQuery = serviceDescription.GetQueryMethod("GetCities");

                // Now prepare for a query to find a Zip in WA
                DomainServiceContext ctxt = new DomainServiceContext(new MockDataService(notWaGuy), DomainOperationType.Query);
                cities.Initialize(ctxt);

                int count = -1;
                IEnumerable<ValidationResult> validationErrors = null;
                IEnumerable result = cities.Query(new QueryDescription(getCitiesQuery), out validationErrors, out count);

                city = result.OfType<City>().FirstOrDefault(z => z.StateName == "WA");
                Assert.IsNotNull(city, "Could not find a city in WA");
            }


            using (CityDomainService cities = new CityDomainService())
            {
                // Now prepare for a submit to invoke AssignCityZoneIfAuthorized as a named update method
                DomainServiceContext ctxt = new DomainServiceContext(new MockDataService(notWaGuy), DomainOperationType.Submit);
                cities.Initialize(ctxt);

                // Prepare an attempt to delete this with a user whose name is not WAGuy
                // This should fail due to a custom auth attribute
                List<ChangeSetEntry> entries = new List<ChangeSetEntry>();

                ChangeSetEntry entry = new ChangeSetEntry();
                entry.DomainOperationEntry = serviceDescription.GetCustomMethod(typeof(City), "AssignCityZoneIfAuthorized");
                entry.EntityActions = new EntityActionCollection { { "AssignCityZoneIfAuthorized", new object[] { "SomeZone" } } };
                entry.Operation = DomainOperation.Update;
                entry.Entity = city;
                entries.Add(entry);

                UnauthorizedAccessException exception = null;
                try
                {
                    ChangeSetProcessor.Process(cities, entries);
                }
                catch (UnauthorizedAccessException ex)
                {
                    exception = ex;
                }
                Assert.IsNotNull(exception, "Expected failure attempting to perform custom method on WA with inappropriate user name");
                Assert.AreEqual("Only one user is authorized to execute operation 'AssignCityZoneIfAuthorized', and it isn't you.", exception.Message);
            }

            // Now do that again but with a user who is WAGuy -- it should succeed
            using (CityDomainService cities = new CityDomainService())
            {
                MockUser waGuy = new MockUser("WAGuy");
                waGuy.IsAuthenticated = true;

                // Now prepare for a submit to invoke AssignCityZoneIfAuthorized as a named update method
                DomainServiceContext ctxt = new DomainServiceContext(new MockDataService(waGuy), DomainOperationType.Submit);
                cities.Initialize(ctxt);

                // Prepare an attempt to delete this with a user whose name is not WAGuy
                // This should fail due to a custom auth attribute

                // Prepare a submit to call the AssignCityZoneIfAuthorized with an unauthorized user
                List<ChangeSetEntry> entries = new List<ChangeSetEntry>();

                ChangeSetEntry entry = new ChangeSetEntry();
                entry.DomainOperationEntry = serviceDescription.GetCustomMethod(typeof(City), "AssignCityZoneIfAuthorized");
                entry.EntityActions = new EntityActionCollection { { "AssignCityZoneIfAuthorized", new object[] { "SomeZone" } } };
                entry.Operation = DomainOperation.Update;
                entry.Entity = city;
                entries.Add(entry);

                Exception exception = null;
                try
                {
                    ChangeSetProcessor.Process(cities, entries);
                }
                catch (UnauthorizedAccessException ex)
                {
                    exception = ex;
                }
                Assert.IsNull(exception, "Expected success attempting to delete a zip from WA with inappropriate user name");
            }
        }
        public void Submit_ResolveActions_UnsupportedAction()
        {
            Product product = new Product { ProductID = 1, ProductName = "Choco Wafers" };
            ChangeSetEntry[] changeSet = new ChangeSetEntry[] { 
                new ChangeSetEntry { Id = 1, Entity = product, Operation = ChangeOperation.Delete }
            };

            HttpConfiguration configuration = new HttpConfiguration();
            HttpControllerDescriptor controllerDescriptor = new HttpControllerDescriptor(configuration, "NorthwindEFTestController", typeof(NorthwindEFTestController));
            DataControllerDescription description = DataControllerDescription.GetDescription(controllerDescriptor);
            Assert.Throws<InvalidOperationException>(
                () => DataController.ResolveActions(description, changeSet),
                String.Format(Resource.DataController_InvalidAction, "Delete", "Product"));
        }
        public void Submit_Authorization_Fail_Global()
        {
            TestAuthAttribute.Reset();

            Product product = new Product { ProductID = 1, ProductName = "Choco Wafers" };
            ChangeSetEntry[] changeSet = new ChangeSetEntry[] { 
                new ChangeSetEntry { Id = 1, Entity = product, Operation = ChangeOperation.Update }
            };

            TestAuthAttribute.FailLevel = "Global";
            HttpResponseMessage response = this.ExecuteSelfHostRequest("http://testhost/TestAuth/Submit", "TestAuth", changeSet);

            Assert.True(TestAuthAttribute.Log.SequenceEqual(new string[] { "Global" }));
            Assert.Equal("Not Authorized", response.ReasonPhrase);
            Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
        }
        public void Submit_Authorization_Success()
        {
            TestAuthAttribute.Reset();

            Product product = new Product { ProductID = 1, ProductName = "Choco Wafers" };
            ChangeSetEntry[] changeSet = new ChangeSetEntry[] { 
                new ChangeSetEntry { Id = 1, Entity = product, Operation = ChangeOperation.Update }
            };

            ChangeSetEntry[] resultChangeSet = this.ExecuteSubmit("http://testhost/TestAuth/Submit", "TestAuth", changeSet);
            Assert.Equal(1, resultChangeSet.Length);
            Assert.True(TestAuthAttribute.Log.SequenceEqual(new string[] { "Global", "Class", "SubmitMethod", "UserMethod" }));
        }
        public void Changeset_GetAssociatedChanges_Singleton()
        {
            DomainServiceDescription dsd = DomainServiceDescription.GetDescription(typeof(CompositionScenarios_Explicit));

            // verify singleton change of None
            GreatGrandChild unmodifiedGgc = new GreatGrandChild();
            GrandChild currGrandChild = new GrandChild
            {
                Child = unmodifiedGgc
            };
            GrandChild origGrandChild = new GrandChild
            {
                Child = unmodifiedGgc
            };
            ChangeSetEntry gcOperation = new ChangeSetEntry(1, currGrandChild, origGrandChild, DomainOperation.Update);
            gcOperation.Associations = new Dictionary<string, int[]> { { "Child", new int[] { 2 } } };
            ChangeSetEntry ggcOperation = new ChangeSetEntry(2, unmodifiedGgc, null, DomainOperation.None);
            ChangeSet cs = new ChangeSet(new ChangeSetEntry[] { gcOperation, ggcOperation });
            GreatGrandChild ggcChange = cs.GetAssociatedChanges(currGrandChild, p => p.Child, ChangeOperation.None).Cast<GreatGrandChild>().SingleOrDefault();
            Assert.AreSame(unmodifiedGgc, ggcChange);

            // verify singleton insert
            GreatGrandChild newGgc = new GreatGrandChild();
            currGrandChild = new GrandChild
            {
                Child = newGgc
            };
            origGrandChild = new GrandChild
            {
                Child = null
            };
            gcOperation = new ChangeSetEntry(1, currGrandChild, origGrandChild, DomainOperation.Update);
            gcOperation.Associations = new Dictionary<string, int[]> { { "Child", new int[] { 2 } } };
            ggcOperation = new ChangeSetEntry(2, newGgc, null, DomainOperation.Insert);
            cs = new ChangeSet(new ChangeSetEntry[] { gcOperation, ggcOperation });
            ggcChange = cs.GetAssociatedChanges(currGrandChild, p => p.Child, ChangeOperation.Insert).Cast<GreatGrandChild>().SingleOrDefault();
            Assert.AreSame(newGgc, ggcChange);
            Assert.AreEqual(ChangeOperation.Insert, cs.GetChangeOperation(newGgc));

            // verify singleton update
            GreatGrandChild modifiedGgc = new GreatGrandChild();
            currGrandChild = new GrandChild
            {
                Child = modifiedGgc
            };
            origGrandChild = new GrandChild
            {
                Child = modifiedGgc
            };
            gcOperation = new ChangeSetEntry(1, currGrandChild, origGrandChild, DomainOperation.Update);
            gcOperation.Associations = new Dictionary<string, int[]> { { "Child", new int[] { 2 } } };
            ggcOperation = new ChangeSetEntry(2, modifiedGgc, unmodifiedGgc, DomainOperation.Update);
            cs = new ChangeSet(new ChangeSetEntry[] { gcOperation, ggcOperation });
            ggcChange = cs.GetAssociatedChanges(currGrandChild, p => p.Child, ChangeOperation.Update).Cast<GreatGrandChild>().SingleOrDefault();
            Assert.AreSame(modifiedGgc, ggcChange);
            Assert.AreSame(unmodifiedGgc, cs.GetOriginal(modifiedGgc));
            Assert.AreEqual(ChangeOperation.Update, cs.GetChangeOperation(modifiedGgc));

            // verify singleton delete
            GreatGrandChild deletedGgc = new GreatGrandChild();
            currGrandChild = new GrandChild
            {
                Child = null
            };
            origGrandChild = new GrandChild
            {
                Child = deletedGgc
            };
            gcOperation = new ChangeSetEntry(1, currGrandChild, null, DomainOperation.Update);
            gcOperation.OriginalAssociations = new Dictionary<string, int[]>() { { "Child", new int[] { 2 } } };
            ggcOperation = new ChangeSetEntry(2, deletedGgc, null, DomainOperation.Delete);
            gcOperation.OriginalAssociations = new Dictionary<string, int[]>() { { "Child", new int[] { 2 } } };
            cs = new ChangeSet(new ChangeSetEntry[] { gcOperation, ggcOperation });
            ggcChange = cs.GetAssociatedChanges(currGrandChild, p => p.Child, ChangeOperation.Delete).Cast<GreatGrandChild>().SingleOrDefault();
            Assert.AreSame(deletedGgc, ggcChange);
            Assert.AreSame(null, cs.GetOriginal(deletedGgc));
            Assert.AreEqual(ChangeOperation.Delete, cs.GetChangeOperation(deletedGgc));
        }
        public void Changeset_GetAssociatedChanges_Enumerable()
        {
            DomainServiceDescription dsd = DomainServiceDescription.GetDescription(typeof(CompositionScenarios_Explicit));

            #region build up a compositional hierarchy
            int id = 1;
            Parent currParent = new Parent();
            List<ChangeSetEntry> changeSetEntries = new List<ChangeSetEntry>();
            ChangeSetEntry parentUpdateOperation = new ChangeSetEntry(id++, currParent, null, DomainOperation.Update);
            List<int> currentAssociatedChildren = new List<int>();
            changeSetEntries.Add(parentUpdateOperation);

            // add 3 unmodified children
            List<Child> unmodifiedChildren = new List<Child>() { new Child(), new Child(), new Child() };
            foreach (Child c in unmodifiedChildren)
            {
                ChangeSetEntry operation = new ChangeSetEntry(id++, c, c, DomainOperation.None);
                changeSetEntries.Add(operation);
                currParent.Children.Add(c);
                currentAssociatedChildren.Add(operation.Id);
            }

            // add 2 child edits
            List<Child> originalEditedChildren = new List<Child> { new Child(), new Child() };
            List<Child> currentEditedChildren = new List<Child> { new Child(), new Child() };
            for (int i = 0; i < originalEditedChildren.Count; i++)
            {
                Child currChild = currentEditedChildren[i];
                Child origChild = originalEditedChildren[i];
                ChangeSetEntry operation = new ChangeSetEntry(id++, currChild, origChild, DomainOperation.Update);
                changeSetEntries.Add(operation);
                currParent.Children.Add(currChild);
                currentAssociatedChildren.Add(operation.Id);
            }

            // add a 2 new children children
            List<Child> newChildren = new List<Child> { new Child(), new Child() };
            foreach (Child c in newChildren)
            {
                ChangeSetEntry operation = new ChangeSetEntry(id++, c, null, DomainOperation.Insert);
                changeSetEntries.Add(operation);
                currParent.Children.Add(c);
                currentAssociatedChildren.Add(operation.Id);
            }

            // add 2 removes by adding operations for the deleted children
            // and setting up the original association
            List<Child> removedChildren = new List<Child> { new Child(), new Child() };
            List<int> deletedChildren = new List<int>();
            foreach (Child c in removedChildren)
            {
                int deletedId = id++;
                changeSetEntries.Add(new ChangeSetEntry(deletedId, c, c, DomainOperation.Delete));
                deletedChildren.Add(deletedId);
            }
            parentUpdateOperation.OriginalAssociations = new Dictionary<string, int[]>() { { "Children", deletedChildren.ToArray() } };
            parentUpdateOperation.Associations = new Dictionary<string, int[]>() { { "Children", currentAssociatedChildren.ToArray() } };
            #endregion

            // verify unmodified children
            ChangeSet cs = new ChangeSet(changeSetEntries);
            IEnumerable<Child> childChanges = cs.GetAssociatedChanges(currParent, p => p.Children, ChangeOperation.None).Cast<Child>();
            Assert.AreEqual(3, childChanges.Count());
            foreach (Child c in childChanges)
            {
                Assert.IsTrue(unmodifiedChildren.Contains(c));
            }

            // verify inserted children
            childChanges = cs.GetAssociatedChanges(currParent, p => p.Children, ChangeOperation.Insert).Cast<Child>();
            Assert.AreEqual(2, childChanges.Count());
            foreach (Child c in childChanges)
            {
                Assert.IsTrue(newChildren.Contains(c));
            }

            // verify deleted children
            childChanges = cs.GetAssociatedChanges(currParent, p => p.Children, ChangeOperation.Delete).Cast<Child>();
            Assert.AreEqual(2, childChanges.Count());
            foreach (Child c in childChanges)
            {
                Assert.IsTrue(removedChildren.Contains(c));
            }

            // verify modified children
            childChanges = cs.GetAssociatedChanges(currParent, p => p.Children, ChangeOperation.Update).Cast<Child>();
            Assert.AreEqual(2, childChanges.Count());
            foreach (Child c in childChanges)
            {
                Assert.IsTrue(currentEditedChildren.Contains(c));
            }

            // verify overload that returns all - should return all unmodified
            // in addition to all changed
            IEnumerable<Child> allChildren = cs.GetAssociatedChanges(currParent, p => p.Children).Cast<Child>();
            Assert.AreEqual(9, allChildren.Count());
            foreach (Child c in allChildren)
            {
                ChangeOperation operationType = cs.GetChangeOperation(c);
                switch (operationType)
                {
                    case ChangeOperation.None:
                        Assert.IsTrue(unmodifiedChildren.Contains(c));
                        break;
                    case ChangeOperation.Insert:
                        Assert.IsTrue(newChildren.Contains(c));
                        break;
                    case ChangeOperation.Update:
                        Assert.IsTrue(currentEditedChildren.Contains(c));
                        break;
                    case ChangeOperation.Delete:
                        Assert.IsTrue(removedChildren.Contains(c));
                        break;
                };
            }

            // verify calls that return empty
            cs = new ChangeSet(new ChangeSetEntry[] { new ChangeSetEntry(1, currParent, null, DomainOperation.None) });
            IEnumerable<Child> children = cs.GetAssociatedChanges(currParent, p => p.Children).Cast<Child>();
            Assert.AreEqual(0, children.Count());
            children = cs.GetAssociatedChanges(currParent, p => p.Children).Cast<Child>();
            Assert.AreEqual(0, children.Count());

            // test null collection properties
            currParent = new Parent { Children = null };
            cs = new ChangeSet(new ChangeSetEntry[] { new ChangeSetEntry(1, currParent, new Parent { Children = null }, DomainOperation.None) });
            Assert.AreEqual(0, cs.GetAssociatedChanges(currParent, p => p.Children).Cast<Child>().Count());
            Assert.AreEqual(0, cs.GetAssociatedChanges(currParent, p => p.Children, ChangeOperation.None).Cast<Child>().Count());
        }