private void GetListChangesInternal(ChangedRelationShip <TKey> relationshipTemplate, IEnumerable <IChangeTrackable <TKey> > list, IEnumerable <IChangeTrackable <TKey> > compareWith, HashSet <object> alreadyChecked, ChangePackage <TKey> currentPackage) { //Note delayed execution var removalQuery = compareWith.Where(a => !list.Any(b => _keyEqualsDelegate(a.Id, b.Id))); if (relationshipTemplate.OwnershipActions.Delete) { foreach (var remove in removalQuery) { remove.ChangeType = ChangeType.Deleted; currentPackage.OwnedEntities.Add(remove); currentPackage.Relationships.Add(relationshipTemplate.CloneFor(remove, ChangeType.Deleted)); } } else { foreach (var remove in removalQuery) { currentPackage.Relationships.Add(relationshipTemplate.CloneFor(remove, ChangeType.Deleted)); } } foreach (var mainListItem in list) { bool found = false; foreach (var compareWithItem in compareWith) { if (_keyEqualsDelegate(compareWithItem.Id, mainListItem.Id)) { found = true; //Will check for update if the parent is an owner if (relationshipTemplate.OwnershipActions.Update) { SetChangeStatusInternal(relationshipTemplate, mainListItem, compareWithItem, alreadyChecked, currentPackage); } break; } } if (!found) { if (relationshipTemplate.OwnershipActions.Add) { mainListItem.ChangeType = ChangeType.Added; currentPackage.OwnedEntities.Add(mainListItem); currentPackage.Relationships.Add(relationshipTemplate.CloneFor(mainListItem, ChangeType.Added)); } else { currentPackage.Relationships.Add(relationshipTemplate.CloneFor(mainListItem, ChangeType.Added)); } } } }
/// <summary> /// Internally called in order to pass around a list of already checked objects. This will help avoid circular references /// </summary> /// <typeparam name="T"></typeparam> /// <param name="main"></param> /// <param name="compareWith"></param> /// <param name="alreadyChecked"></param> /// <returns></returns> private void SetChangeStatusInternal <T>(ChangedRelationShip <TKey> relationshipTemplate, T main, T compareWith, HashSet <object> alreadyChecked, ChangePackage <TKey> currentPackage) where T : IChangeTrackable <TKey> { //TODO break this function up... alot if (alreadyChecked.Contains(main)) { return; } alreadyChecked.Add(main); //TODO debating if this is meaningful, and whether or not this is the correct approach to a removed single object property //For example currently if you have a sub property //source { c : {} }, destination { c : null }, this is considered an add //source { c : null }, destination { c : {} }, this is considered a delete //But this might be a foreign key and shouldnt be flagged as deleted?? or maybe just in regards to the relationship? //There may need to be configuration for this relationship check per property if (main == null && compareWith == null) { return; } else if (main != null && compareWith != null) { //When the id of a single property changes its both a delete and an add //To avoid this results the user of the API should ignore object properties if they simply need to // check the foreign key if (relationshipTemplate.Parent != null && !_keyEqualsDelegate(main.Id, compareWith.Id)) { if (relationshipTemplate.OwnershipActions.Delete) { compareWith.ChangeType = ChangeType.Deleted; currentPackage.OwnedEntities.Add(compareWith); currentPackage.Relationships.Add(relationshipTemplate.CloneFor(compareWith, ChangeType.Deleted)); } else { currentPackage.Relationships.Add(relationshipTemplate.CloneFor(compareWith, ChangeType.Deleted)); } if (relationshipTemplate.OwnershipActions.Add) { main.ChangeType = ChangeType.Added; currentPackage.OwnedEntities.Add(main); currentPackage.Relationships.Add(relationshipTemplate.CloneFor(main, ChangeType.Added)); } else { currentPackage.Relationships.Add(relationshipTemplate.CloneFor(main, ChangeType.Added)); } } } else if (main == null && compareWith != null) { if (relationshipTemplate.OwnershipActions.Delete) { compareWith.ChangeType = ChangeType.Deleted; currentPackage.OwnedEntities.Add(compareWith); currentPackage.Relationships.Add(relationshipTemplate.CloneFor(compareWith, ChangeType.Deleted)); } else { currentPackage.Relationships.Add(relationshipTemplate.CloneFor(compareWith, ChangeType.Deleted)); } return; } else if (compareWith == null && main != null) { if (relationshipTemplate.OwnershipActions.Add) { main.ChangeType = ChangeType.Added; currentPackage.OwnedEntities.Add(main); currentPackage.Relationships.Add(relationshipTemplate.CloneFor(main, ChangeType.Added)); } else { currentPackage.Relationships.Add(relationshipTemplate.CloneFor(main, ChangeType.Added)); } return; } var details = _mappedTypeDetails[main.GetType()]; //Only check the object if the parent owns this property and can make changes to it if (relationshipTemplate.OwnershipActions.Update) { //TODO most of the other fields will be non-object non complex fields, so I can probably replace deep equals with a custom property check of my own var compareSyntax = main.WithDeepEqual(compareWith); HashSet <string> ignoreNames = new HashSet <string>(); foreach (var ignoreProperty in details.IgnoreProperties) { ignoreNames.Add(ignoreProperty.Name); } compareSyntax.IgnoreProperty((c) => { return(ignoreNames.Contains(c.Name)); }); if (!compareSyntax.Compare()) { main.ChangeType = ChangeType.Updated; currentPackage.OwnedEntities.Add(main); } foreach (var property in details.ObjectProperties) { var mainValue = property.GetValue(main, null) as IChangeTrackable <TKey>; var compareWithValue = property.GetValue(compareWith, null) as IChangeTrackable <TKey>; this.SetChangeStatusInternal(BuildChangeRelationshipTemplate(main, property), mainValue, compareWithValue, alreadyChecked, currentPackage); } } foreach (var property in details.ListProperties) { var mainListValue = ((property.GetValue(main, null) as IList) ?? new IChangeTrackable <TKey> [0]).Cast <IChangeTrackable <TKey> >().ToList(); var compareWithListValue = ((property.GetValue(compareWith, null) as IList) ?? new IChangeTrackable <TKey> [0]).Cast <IChangeTrackable <TKey> >().ToList(); GetListChangesInternal(BuildChangeRelationshipTemplate(main, property), mainListValue, compareWithListValue, alreadyChecked, currentPackage); } }