internal static void WhenChanged(ITrackableObject item, Action onChangedDelegate, TrackingCache cache)
        {
            if (onChangedDelegate == null)
            {
                throw new ArgumentNullException(nameof(onChangedDelegate));
            }

            TrackingState tracker = GetTracker(item);

            tracker.OnChanged.Add(onChangedDelegate);

            PropertyInfo[] properties;
            if (!cache.Properties.TryGetValue(item.GetType(), out properties))
            {
                properties = item.GetType().GetProperties();
                cache.Properties[item.GetType()] = properties;
            }

            foreach (var property in properties)
            {
                if (property.PropertyType != typeof(TrackingState) && ShouldAllowProperty(property, cache))
                {
                    var value = property.GetValue(item);
                    if (property.PropertyType.IsGenericType && property.PropertyType.IsAssignableTo(typeof(System.Collections.IEnumerable)))
                    {
                        System.Collections.IEnumerable list = (System.Collections.IEnumerable)value;
                        foreach (object subItem in list)
                        {
                            ITrackableObject trackedItem = subItem as ITrackableObject;
                            if (trackedItem != null)
                            {
                                WhenChanged(trackedItem, onChangedDelegate, cache);
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Determines whether this entity requires printing.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <returns>
        ///   <c>true</c> if the specified source has print; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsPrintRequired(this ITrackableObject source)
        {
            TrackingState tracker = GetTracker(source);

            return(tracker.Print);
        }
        public static bool IsModified(this ITrackableObject source, bool includeAddDelete = false)
        {
            TrackingState tracker = GetTracker(source);

            return(IsModified(source, tracker, includeAddDelete));
        }
        public static bool IsNew(this ITrackableObject source)
        {
            TrackingState tracker = GetTracker(source);

            return(IsNew(source, tracker));
        }
        public static bool IsDeleted(this ITrackableObject source)
        {
            TrackingState tracker = GetTracker(source);

            return(IsDeleted(source, tracker, null));
        }
        public static void Delete(this ITrackableObject source)
        {
            TrackingState tracker = GetTracker(source);

            tracker.Deleted = true;
        }
        private static TrackingState StartTracking(ITrackableObject source, TrackingCache cache)
        {
            TrackingState tracker = new TrackingState(source);

            PropertyInfo[] properties;
            if (!cache.Properties.TryGetValue(source.GetType(), out properties))
            {
                properties = source.GetType().GetProperties();
                cache.Properties[source.GetType()] = properties;
            }

            foreach (var property in properties)
            {
                if (property.PropertyType != typeof(TrackingState) && ShouldAllowProperty(property, cache))
                {
                    var value = property.GetValue(source);

                    if (property.PropertyType.IsGenericType && property.PropertyType.IsAssignableTo(typeof(System.Collections.IEnumerable)))
                    {
                        System.Collections.IEnumerable list = (System.Collections.IEnumerable)value;


                        List <ITrackableObject> deletedItems = new List <ITrackableObject>();
                        foreach (object item in list)
                        {
                            ITrackableObject trackedItem = item as ITrackableObject;
                            if (trackedItem != null)
                            {
                                if (trackedItem.IsTracking() && trackedItem.IsDeleted())
                                {
                                    deletedItems.Add(trackedItem);
                                    StopTracking(trackedItem);
                                }
                                else
                                {
                                    StartTracking(trackedItem, cache);
                                }
                            }
                        }

                        //Remove any items we detected as having been deleted.
                        IList editableList = list as IList;
                        if (editableList != null)
                        {
                            foreach (var removeItem in deletedItems)
                            {
                                editableList.Remove(removeItem);
                            }
                        }
                    }
                    else if (property.PropertyType.IsAssignableTo(typeof(ITrackableObject)))
                    {
                        ITrackableObject trackedItem = value as ITrackableObject;
                        if (trackedItem != null)
                        {
                            StartTracking(trackedItem, cache);
                        }
                    }

                    tracker.UnmodifiedState.Add(property, value);
                }
            }

            trackedObjects[source] = tracker;
            return(tracker);
        }