/// <summary>
        /// Will compare the source (potential updated object) and the destination (persisted object)
        /// If the object differs, or its sub properties are changed they will have the proper ChangeType assigned
        /// (Added, Updated, Deleted, None)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">(potential updated object)</param>
        /// <param name="destination">(persisted object)</param>
        /// <returns>Returns an array of all changed objects from the source objects tree</returns>
        public ChangePackage <TKey> SetChangeStatusList <T>(IList <T> list, IList <T> compareWith) where T : IChangeTrackable <TKey>
        {
            this.SetupConfigurationForType(typeof(T));
            ChangePackage <TKey> changes = new ChangePackage <TKey>();

            GetListChangesInternal(
                new ChangedRelationShip <TKey>(null, null, new OwnershipActions()
            {
                Add    = true,
                Update = true,
                Delete = true
            }),
                ((IEnumerable <IChangeTrackable <TKey> >)list ?? new IChangeTrackable <TKey> [0]).Cast <IChangeTrackable <TKey> >(),
                ((IEnumerable <IChangeTrackable <TKey> >)compareWith ?? new IChangeTrackable <TKey> [0]).Cast <IChangeTrackable <TKey> >(), new HashSet <object>(),
                changes);
            return(changes);
        }
        /// <summary>
        /// Will compare the source (potential updated object) and the destination (persisted object)
        /// If the object differs, or its sub properties are changed they will have the proper ChangeType assigned
        /// (Added, Updated, Deleted, None)
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="source">(potential updated object)</param>
        /// <param name="destination">(persisted object)</param>
        /// <returns>Returns an array of all changed objects from the source objects tree</returns>
        public ChangePackage <TKey> SetChangeStatus <T>(T source, T destination) where T : IChangeTrackable <TKey>
        {
            this.SetupConfigurationForType(typeof(T));
            //Execute right away with array, TODO maybe I should get rid of the yields as we would want this to execute right away

            ChangePackage <TKey> changes = new ChangePackage <TKey>();

            SetChangeStatusInternal(
                new ChangedRelationShip <TKey>(null, null, new OwnershipActions()
            {
                Add    = true,
                Update = true,
                Delete = true
            }),
                source,
                destination,
                new HashSet <object>(),
                changes);
            return(changes);
        }
        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);
            }
        }