public virtual async Task <IEnumerable <string> > PushAsync(IPropertyChangeTracker tracker, IDataAccessLayer dal, int action, int commentId)
        {
            InvokeBeforePushActions();
            long utcTimestamp = DateTime.UtcNow.Ticks;

            IsPushing = true;
            RaisePushing();
            IEnumerable <string> changes = DbEntityChanges(tracker, utcTimestamp);

            try
            {
                int editDuration = (int)Math.Round(EditDuration);
                IDictionary <string, Tuple <string, object>[]> newEntitiesPrimaryKeys = await dal.UpdateEntity(GetType(), ChangesAsJson(tracker, utcTimestamp), changes, action, editDuration, commentId);

                tracker.Untracked((e) =>
                {
                    DbEntityUtilities.UpdatePrimaryKeys(newEntitiesPrimaryKeys, this);
                });
                // TODO: Only clear the changes if the push has been successful
                PurgeChanges(tracker, utcTimestamp);
                RaisePushed(changes);
            }
            catch (Exception) { changes = Enumerable.Empty <string>(); }
            finally { IsPushing = false; }

            return(changes);
        }
        public bool Contains(TEntity entity)
        {
            bool contains = false;

            if (entity.State == EntityState.New)
            {
                contains = Collection.Any(e => !e.IsDeleted && e.Guid.Equals(entity.Guid));
            }
            else
            {
                contains = Collection.Any(e => !e.IsDeleted && DbEntityUtilities.PrimaryKeysEqual(e.PrimaryKeys, entity.PrimaryKeys));
            }
            return(contains);
        }
        private void ProcessCollectionChange(NotifyCollectionChangedAction action, IEnumerable items, string path, string propertyName, IDbEntity parentEntity)
        {
            if (!_isCreatingHandlers && IsValid(parentEntity.GetType(), path, propertyName))
            {
                if (action == NotifyCollectionChangedAction.Add)
                {
                    ICollection <PropertyChange> changes = new List <PropertyChange>();
                    foreach (IDbEntity navigationEntity in items.Cast <IDbEntity>())
                    {
                        string         entityPath = DbEntityUtilities.GenerateCollectionItemPath(path + propertyName, navigationEntity.Guid);
                        PropertyChange change     = new PropertyChange(entityPath, string.Empty, navigationEntity.Guid, null, navigationEntity, true, navigationEntity.State);
                        if (_entity.IsTrackingChanges)
                        {
                            changes.Add(change);
                            MergeChanges(navigationEntity, entityPath, !_entity.IsTrackingChanges);
                        }
                        CreateHandlers(navigationEntity, entityPath);
                    }

                    if (_entity.IsTrackingChanges)
                    {
                        AddChanges(changes);
                    }
                    Validate(null, true);
                }
                else if (action == NotifyCollectionChangedAction.Remove)
                {
                    ICollection <PropertyChange> changes = new List <PropertyChange>();
                    string propertyPath = DbEntityUtilities.GeneratePropertyPath(path, propertyName);
                    foreach (IDbEntity removedEntity in items.Cast <IDbEntity>())
                    {
                        string         entityPath = DbEntityUtilities.GenerateCollectionItemPath(propertyPath, removedEntity.Guid);
                        PropertyChange change     = new PropertyChange(entityPath, string.Empty, removedEntity.Guid, removedEntity, null, true, removedEntity.State);
                        if (_entity.IsTrackingChanges)
                        {
                            changes.Add(change);
                        }
                        RemoveHandlers(removedEntity, entityPath);
                        Validate(change, true);
                    }

                    if (_entity.IsTrackingChanges)
                    {
                        AddChanges(changes);
                    }
                    changes.Clear();
                }
            }
        }
        private void RevertCollectionChange <T>(IEnumerable entities)
            where T : class, IDbEntity
        {
            ICollection <T> collection = entities as ICollection <T>;

            if (Before == null && After != null && DbEntityUtilities.IsIDbEntity(After.GetType()))
            {
                // This property change refers to a newly added entity to this collection. To revert it, remove the added entity from the collection
                collection.Remove(After as T);
            }
            else if (Before != null && DbEntityUtilities.IsIDbEntity(Before.GetType()) && After == null && !collection.Contains(Before as T))
            {
                // This property change refers to an entity removed from this collection. To revert it, re-add the removed entity to the collection
                collection.Add(Before as T);
            }
        }
        private void ApplyCollectionChange <T>(IEnumerable entities)
            where T : class, IDbEntity
        {
            // If remove (i.e. before = typeof(IDbEntity), after = null), remove before value
            ICollection <T> collection = entities as ICollection <T>;

            if (Before == null && After != null && DbEntityUtilities.IsIDbEntity(After.GetType()) && !collection.Contains(After as T))
            {
                // This property change refers to a newly added entity to this collection. To re-apply it, re-add the added entity to the collection
                collection.Add(After as T);
            }
            else if (Before != null && DbEntityUtilities.IsIDbEntity(Before.GetType()) && After == null)
            {
                // This property change refers to an entity removed from this collection. To re-apply it, remove the removed entity from the collection
                collection.Remove(Before as T);
            }
        }
        private ISet <string> GetChangePathsPrefixes(IDbEntity root)
        {
            ISet <string> changePathsPrefixes = new HashSet <string>()
            {
                "."
            };

            if (root != null)
            {
                ISet <string> entitiesPaths;
                if (_entitiesPaths.TryGetValue(root.Guid, out entitiesPaths))
                {
                    changePathsPrefixes.Clear();
                    foreach (string prefix in entitiesPaths)
                    {
                        changePathsPrefixes.Add(DbEntityUtilities.GetDbEntityPropertyPath(prefix, _entity));
                        changePathsPrefixes.Add(prefix);
                    }
                }
            }
            return(changePathsPrefixes);
        }
 public TEntity Get(Tuple <string, object>[] primaryKeys)
 {
     return(Collection.SingleOrDefault(e => /*!e.IsDeleted && */ DbEntityUtilities.PrimaryKeysEqual(e.PrimaryKeys, primaryKeys)));
 }
 public string DbEntityPropertyPath(IDbEntity entity)
 {
     return(DbEntityUtilities.GetDbEntityPropertyPath(PropertyPath, entity));
 }
        private void EntityPropertyChanged(object sender, DbEntityPropertyChangedEventArgs e, string path)
        {
            IDbEntity      entity = sender as IDbEntity;
            PropertyChange change = null;

            // Save a reference to the property changed
            if (!_isCreatingHandlers && IsValid(sender.GetType(), path, e.PropertyName) && (!e.PropertyName.Equals("IsDeleted") || entity.State == EntityState.Persisted))
            {
                string       propertyPath         = path + e.PropertyName;
                PropertyInfo p                    = entity.GetType().GetProperty(e.PropertyName);
                bool         isEnumerable         = p.GetGetMethod().ReturnType.IsIEnumerable();
                bool         isDbEntityEnumerable = false;
                bool         isIDbEntity          = DbEntityUtilities.IsIDbEntity(p);
                if (isIDbEntity)
                {
                    propertyPath = propertyPath + ".";
                    if (e.Before != null)
                    {
                        RemoveHandlers(e.Before as IDbEntity, propertyPath);
                    }

                    if (e.After != null)
                    {
                        // Defer any changes tracking that might have been recorded on the navigation entity to the root entity
                        IDbEntity navigationEntity = e.After as IDbEntity;
                        if (_entity.IsTrackingChanges)
                        {
                            MergeChanges(navigationEntity, propertyPath, !_entity.IsTrackingChanges);
                        }
                        CreateHandlers(navigationEntity, propertyPath);
                    }
                }
                else if ((isEnumerable && p.GetGetMethod().ReturnType.GetGenericArguments().Count() > 0 && DbEntityUtilities.IsIDbEntity(p.GetGetMethod().ReturnType.GetGenericArguments()[0])))
                {
                    isDbEntityEnumerable = true;
                    if (e.Before != null)
                    {
                        RemoveCollectionHandlers(entity, e.Before as IEnumerable <IDbEntity>, path, propertyPath + ".", p);
                    }

                    if (e.After != null)
                    {
                        foreach (IDbEntity navigationEntity in e.After as IEnumerable <IDbEntity> )
                        {
                            if (navigationEntity != null)
                            {
                                string entityPath = DbEntityUtilities.GenerateCollectionItemPath(propertyPath, navigationEntity.Guid);
                                if (_entity.IsTrackingChanges)
                                {
                                    MergeChanges(navigationEntity, entityPath, !_entity.IsTrackingChanges);
                                }
                                CreateHandlers(navigationEntity, entityPath);
                            }
                        }
                    }
                }

                if (_entity.IsTrackingChanges)
                {
                    change = new PropertyChange(path, e.PropertyName, entity.Guid, e.Before, e.After, entity.State);
                    if (isDbEntityEnumerable)
                    {
                        change = new PropertyChange(path, e.PropertyName, entity.Guid, e.Before, e.After);
                    }

                    PurgeLast(p.GetGetMethod().ReturnType, change);
                    AddChange(change);
                    Validate(change, isIDbEntity);
                }

                //_logTo.DebugQueue(new EntityPropertyChangedLogEntry(SettingsModel.Current.User.id, entity.PrimaryKeys, entity.GetType(), propertyPath));
            }
        }