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)))); }
/// <summary> /// Returns a map of composition members to associated child operations for the specified /// entity, caching the results. /// </summary> /// <param name="entity">The entity to get associated changes for.</param> /// <returns>The map of child changes.</returns> private Dictionary <PropertyDescriptor, IEnumerable <ChangeSetEntry> > GetAssociatedChanges(object entity) { // first check our cache to see if we've already computed the associated changes // for the specified entity. Dictionary <PropertyDescriptor, IEnumerable <ChangeSetEntry> > associatedChanges = null; if (this._associatedChangesMap == null) { this._associatedChangesMap = new Dictionary <object, Dictionary <PropertyDescriptor, IEnumerable <ChangeSetEntry> > >(); } else if (this._associatedChangesMap.TryGetValue(entity, out associatedChanges)) { return(associatedChanges); } // compute the associated changes for the specified entity Dictionary <int, ChangeSetEntry> entityOperationMap = this._changeSetEntries.ToDictionary(p => p.Id); associatedChanges = new Dictionary <PropertyDescriptor, IEnumerable <ChangeSetEntry> >(); IEnumerable <PropertyDescriptor> compositionMembers = TypeDescriptor.GetProperties(entity.GetType()).Cast <PropertyDescriptor>() .Where(p => p.Attributes[typeof(CompositionAttribute)] != null); foreach (PropertyDescriptor compositionMember in compositionMembers) { // first get any current child operations List <ChangeSetEntry> associatedChangesList = new List <ChangeSetEntry>(); ChangeSetEntry changeSetEntry = this._changeSetEntries.Single(p => p.Entity == entity); if (changeSetEntry.Associations != null) { if (changeSetEntry.Associations.ContainsKey(compositionMember.Name)) { int[] associatedIds = changeSetEntry.Associations[compositionMember.Name]; IEnumerable <ChangeSetEntry> childOperations = associatedIds.Select(p => entityOperationMap[p]); associatedChangesList.AddRange(childOperations); } } // next get any child delete operations if (changeSetEntry.OriginalAssociations != null) { if (changeSetEntry.OriginalAssociations.ContainsKey(compositionMember.Name)) { int[] originalAssociatedIds = changeSetEntry.OriginalAssociations[compositionMember.Name]; IEnumerable <ChangeSetEntry> deletedChildOperations = originalAssociatedIds .Select(p => entityOperationMap[p]) .Where(p => p.Operation == DomainOperation.Delete); associatedChangesList.AddRange(deletedChildOperations); } } associatedChanges[compositionMember] = associatedChangesList; } this._associatedChangesMap[entity] = associatedChanges; return(associatedChanges); }
private void VerifyExistsInChangeset(object entity) { ChangeSetEntry entry = this._changeSetEntries.FirstOrDefault(p => object.ReferenceEquals(entity, p.Entity)); if (entry == null) { throw new ArgumentException(Resource.ChangeSet_ChangeSetEntryNotFound, "entity"); } }
/// <summary> /// Recursively orders the specified operation and all child operations, adding them to the /// <paramref name="orderedOperations"/> list. /// </summary> /// <param name="operation">The operation to order.</param> /// <param name="operationChildMap">Map of operation to child operations.</param> /// <param name="orderedOperations">The list of ordered operations.</param> private void OrderOperations(ChangeSetEntry operation, Dictionary <ChangeSetEntry, List <ChangeSetEntry> > operationChildMap, List <ChangeSetEntry> orderedOperations) { // first add the operation orderedOperations.Add(operation); // recursively add all its children List <ChangeSetEntry> childOps; if (!operationChildMap.TryGetValue(operation, out childOps)) { return; } foreach (ChangeSetEntry childOperation in childOps) { this.OrderOperations(childOperation, operationChildMap, orderedOperations); } }
/// <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 new ArgumentNullException("clientEntity"); } ChangeSetEntry entry = this._changeSetEntries.FirstOrDefault(p => object.ReferenceEquals(p.Entity, clientEntity)); if (entry == null) { throw new ArgumentException(Resource.ChangeSet_ChangeSetEntryNotFound); } if (entry.Operation == DomainOperation.Insert) { throw new InvalidOperationException(Resource.ChangeSet_OriginalNotValidForInsert); } return((TEntity)entry.OriginalEntity); }
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; }
/// <summary> /// Reorders the specified changeset operations to respect compositional hierarchy ordering /// rules. For compositional hierarchies, all parent operations are ordered before operations /// on their children, recursively. /// </summary> /// <param name="changeSetEntries">The changeset operations to order.</param> /// <returns>The ordered operations.</returns> private IEnumerable <ChangeSetEntry> OrderChangeset(IEnumerable <ChangeSetEntry> changeSetEntries) { Dictionary <int, ChangeSetEntry> cudOpIdMap = changeSetEntries.ToDictionary(p => p.Id); Dictionary <ChangeSetEntry, List <ChangeSetEntry> > operationChildMap = new Dictionary <ChangeSetEntry, List <ChangeSetEntry> >(); // we group by entity type so we can cache per Type computations and lookups bool hasComposition = false; foreach (var group in changeSetEntries.GroupBy(p => p.Entity.GetType())) { IEnumerable <PropertyDescriptor> compositionMembers = TypeDescriptor.GetProperties(group.Key).Cast <PropertyDescriptor>().Where(p => p.Attributes[typeof(CompositionAttribute)] != null).ToArray(); // foreach operation in the changeset, identify all child operations // and add them to a map of operation to child operations foreach (ChangeSetEntry operation in group) { foreach (PropertyDescriptor compositionMember in compositionMembers) { hasComposition = true; // add any current associations List <int> childIds = new List <int>(); if (operation.Associations != null) { if (operation.Associations.ContainsKey(compositionMember.Name)) { childIds.AddRange(operation.Associations[compositionMember.Name]); } } // add any original associations if (operation.OriginalAssociations != null) { if (operation.OriginalAssociations.ContainsKey(compositionMember.Name)) { childIds.AddRange(operation.OriginalAssociations[compositionMember.Name]); } } // foreach identified child operation, set the parent // and build the composition maps foreach (int id in childIds.Distinct()) { // find the operation corresponding to the child entry ChangeSetEntry childOperation = null; if (!cudOpIdMap.TryGetValue(id, out childOperation)) { continue; } // set the parent of this operation if (childOperation.ParentOperation != null) { // an child operation can only have a single parent throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resource.ChangeSet_ChildHasMultipleParents, childOperation.Id)); } childOperation.ParentOperation = operation; // add the current operation to the list of child operations // for the current operation List <ChangeSetEntry> currChildOps; if (!operationChildMap.TryGetValue(operation, out currChildOps)) { currChildOps = new List <ChangeSetEntry>(); operationChildMap[operation] = currChildOps; } currChildOps.Add(childOperation); } } } } if (!hasComposition) { // there were no compositions, so leave the original changeset // as is return(changeSetEntries); } // For each "root" operation with no child operations, recursively add // all child operations in a recursive preorder traversal from each root List <ChangeSetEntry> orderedOperations = new List <ChangeSetEntry>(); IEnumerable <ChangeSetEntry> rootOperations = changeSetEntries.Where(p => p.ParentOperation == null); foreach (ChangeSetEntry operation in rootOperations) { this.OrderOperations(operation, operationChildMap, orderedOperations); } // now add any remaining operations return(orderedOperations.Union(changeSetEntries).ToArray()); }
public static ChangeSet CreateChangeSet(ChangeSetEntry entry) { return new ChangeSet(new[] { entry }); }
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)); }
/// <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.Submit(changeSet); if (changeSetEntry.HasError) { throw new OperationException(Resource.DomainServiceProxy_OperationError, changeSetEntry.ValidationErrors); } }