Exemple #1
0
        /// <summary>Merges the temporary storage with the global storage.</summary>
        /// <param name="dataSourceInfo">The parameter is not used.</param>
        protected override void SaveChangesCore(DataSourceInfo dataSourceInfo)
        {
            MemoryStore <TEntity> memoryStore = this.SelectMemoryStore(dataSourceInfo);

            try {
                /* Make sure no one has access to the global and local storage */
                memoryStore.EnterWriteLock();
                this.temporaryStorageLock.EnterWriteLock();

                /* First, check if all the updated entities still exist in the global storage */
                Dictionary <int, TEntity> updatedEntities = new Dictionary <int, TEntity>();
                foreach (TEntity entity in this.updateCache)
                {
                    var storedEntity = memoryStore.Storage.Select((item, index) => new { Entity = item, Index = index })
                                       .FirstOrDefault(a => a.Entity.RecordId == entity.RecordId);
                    if (storedEntity == null)
                    {
                        throw new InvalidOperationException(
                                  string.Format(CultureInfo.CurrentCulture, "Cannot update entity {0} since it does not exist in the global storage", entity.RecordId));
                    }
                    else
                    {
                        updatedEntities.Add(storedEntity.Index, entity);
                    }
                }

                /* Then, check if all the deleted entities still exist in the global storage */
                Dictionary <int, TEntity> deletedEntities = new Dictionary <int, TEntity>();
                foreach (TEntity entity in this.deletionCache)
                {
                    var storedEntity = memoryStore.Storage.Select((item, index) => new { Entity = item, Index = index })
                                       .FirstOrDefault(a => a.Entity.RecordId == entity.RecordId);
                    if (storedEntity == null)
                    {
                        throw new InvalidOperationException(
                                  string.Format(CultureInfo.CurrentCulture, "Cannot delete entity {0} since it does not exist in the global storage", entity.RecordId));
                    }
                    else
                    {
                        deletedEntities.Add(storedEntity.Index, entity);
                    }
                }

                /* All the pre-checks have been completed, start the saving */
                /* First, perform the updates */
                foreach (KeyValuePair <int, TEntity> updatedEntity in updatedEntities)
                {
                    if (this.SelectCloneDataSourceItems(dataSourceInfo))
                    {
                        memoryStore.Storage[updatedEntity.Key] = ((ICloneable)updatedEntity.Value).Clone() as TEntity;
                    }
                    else
                    {
                        memoryStore.Storage[updatedEntity.Key] = updatedEntity.Value;
                    }
                }

                /* Then perform the deletions */
                int iteration = 0;
                foreach (KeyValuePair <int, TEntity> deletedEntity in deletedEntities.OrderBy(kvp => kvp.Key))
                {
                    memoryStore.Storage.RemoveAt(deletedEntity.Key - iteration);
                    ++iteration;
                }

                /* Then apply new identifiers to the new entities */
                int startId = memoryStore.Storage.DefaultIfEmpty(new TEntity {
                    RecordId = 0
                }).Max(t => t.RecordId) + 1;
                this.ApplyIdentifiers(this.additionCache, startId);

                /* Then add the added entities to the global storage */
                foreach (TEntity addedEntity in this.additionCache)
                {
                    if (this.SelectCloneDataSourceItems(dataSourceInfo))
                    {
                        memoryStore.Storage.Add(((ICloneable)addedEntity).Clone() as TEntity);
                    }
                    else
                    {
                        memoryStore.Storage.Add(addedEntity);
                    }
                }

                /* Finally, clear the local storage */
                this.additionCache.Clear();
                this.updateCache.Clear();
                this.deletionCache.Clear();
            }
            finally {
                memoryStore.ExitWriteLock();
                this.temporaryStorageLock.ExitWriteLock();
            }
        }
Exemple #2
0
        /// <summary>Updates a collection of entities in the repository. Depending on the status of each entity, it is updated in the addition-cache or
        /// it is added to the update-cache.</summary>
        /// <param name="entities">The entities that contain the updated values.</param>
        /// <param name="dataSourceInfo">Information about the data source that may not have been set at an earlier stage. This parameter is not used.
        /// </param>
        /// <returns>The entities as they are stored in the repository.</returns>
        protected override IEnumerable <TEntity> UpdateEntitiesCore(IEnumerable <TEntity> entities, DataSourceInfo dataSourceInfo)
        {
            if (entities.Any(e => e.RecordId == 0))
            {
                throw new InvalidOperationException("Cannot update an entity whose identifier is zero.");
            }

            EntityEqualityComparer <TEntity> entityComparer = new EntityEqualityComparer <TEntity>();
            MemoryStore <TEntity>            memoryStore    = this.SelectMemoryStore(dataSourceInfo);

            IEnumerable <TEntity> addedEntities   = entities.Where(e => e.RecordId < 0);
            IEnumerable <TEntity> updatedEntities = entities.Where(e => e.RecordId > 0);

            this.temporaryStorageLock.EnterWriteLock();
            memoryStore.EnterReadLock();

            /* Make a copy of the caches. That way, if any thing goes wrong all the changes can be made undone */
            List <TEntity> tempAdditionCache = this.additionCache.ToList();
            List <TEntity> tempUpdateCache   = this.updateCache.ToList();
            List <TEntity> tempDeletionCache = this.deletionCache.ToList();

            List <TEntity> newlyAddedEntities     = new List <TEntity>();
            List <TEntity> currentUpdatedEntities = new List <TEntity>();

            try {
                foreach (TEntity addedEntity in addedEntities)
                {
                    /* If the entity is marked for addition, update the entity in the addition cache */
                    if (tempAdditionCache.Contains(addedEntity, entityComparer))
                    {
                        int     indexOfEntity    = tempAdditionCache.IndexOf(addedEntity, entityComparer);
                        TEntity repositoryEntity = tempAdditionCache[indexOfEntity];

                        /* Copy the  values of the updated entity into the cached entity */
                        repositoryEntity.CopyFrom(addedEntity);
                        newlyAddedEntities.Add(repositoryEntity);
                    }
                    else
                    {
                        throw new InvalidOperationException("Could not find the entity in the addition-cache.");
                    }
                }

                foreach (TEntity updatedEntity in updatedEntities)
                {
                    if (memoryStore.Storage.Contains(updatedEntity, entityComparer))
                    {
                        /* If the entity is already marked for deletion, it can no longer be updated. */
                        if (tempDeletionCache.Contains(updatedEntity, entityComparer))
                        {
                            throw new InvalidOperationException("Cannot update the entity since it already marked for deletion.");
                        }

                        TEntity repositoryEntity;
                        if (tempUpdateCache.Contains(updatedEntity, entityComparer))
                        {
                            /* If the entity was already marked for update, replace it in the update cache */
                            int indexOfEntity = tempUpdateCache.IndexOf(updatedEntity, entityComparer);
                            repositoryEntity = tempUpdateCache[indexOfEntity];

                            /* Copy the  values of the updated entity into the cached entity */
                            repositoryEntity.CopyFrom(updatedEntity);
                        }
                        else
                        {
                            /* Retrieve the original version of the entity from the storage */
                            TEntity originalEntity = memoryStore.Storage[memoryStore.Storage.IndexOf(updatedEntity, entityComparer)];
                            /* Create a copy of the original entity to avoid any unwanted updates of the original entity */
                            repositoryEntity = originalEntity.CreateCopyOrClone();

                            /* Copy the  values of the updated entity into the (copy of) original entity */
                            repositoryEntity.CopyFrom(updatedEntity);
                            /* Store the updated entity in the update cache */
                            tempUpdateCache.Add(repositoryEntity);
                        }

                        currentUpdatedEntities.Add(repositoryEntity);
                    }
                    else
                    {
                        throw new InvalidOperationException("Could not find the entity in the internal cache.");
                    }
                }

                /* Replace the original caches to complete the 'transaction' */
                this.additionCache = tempAdditionCache;
                this.updateCache   = tempUpdateCache;
                this.deletionCache = tempDeletionCache;

                if (this.SelectCloneDataSourceItems(dataSourceInfo))
                {
                    return(newlyAddedEntities.Concat(currentUpdatedEntities).Select(entity => ((ICloneable)entity).Clone() as TEntity).ToList());
                }
                else
                {
                    IEnumerable <TEntity> resultList = newlyAddedEntities.Concat(currentUpdatedEntities);
                    resultList.ForEach(entity => entities.Single(e => e.RecordId == entity.RecordId).CopyFrom(entity));
                    return(entities);
                }
            }
            finally {
                memoryStore.ExitReadLock();
                if (this.temporaryStorageLock.IsWriteLockHeld)
                {
                    this.temporaryStorageLock.ExitWriteLock();
                }
            }
        }
Exemple #3
0
        /// <summary>Removes a collection of entities from the repository. Depending on the status of each entity, it is removed from the addition-cache
        /// or it is added to the deletion-cache until it is saved using the <see cref="Repository{T}.SaveChanges()"/> method.</summary>
        /// <param name="entities">The entities that must be removed.</param>
        /// <param name="dataSourceInfo">Information about the data source that may not have been set at an earlier stage. This parameter is not used.
        /// </param>
        protected override void DeleteEntitiesCore(IEnumerable <TEntity> entities, DataSourceInfo dataSourceInfo)
        {
            if (entities.Any(e => e.RecordId == 0))
            {
                throw new InvalidOperationException("Cannot delete an entity whose identifier is zero.");
            }

            IEqualityComparer <TEntity> entityComparer = new EntityEqualityComparer <TEntity>();
            MemoryStore <TEntity>       memoryStore    = this.SelectMemoryStore(dataSourceInfo);

            this.temporaryStorageLock.EnterWriteLock();
            memoryStore.EnterReadLock();

            IEnumerable <TEntity> addedEntities    = entities.Where(e => e.RecordId < 0);
            IEnumerable <TEntity> existingEntities = entities.Where(e => e.RecordId > 0);

            /* Make a copy of the caches. That way, if any thing goes wrong all the changes can be made undone */
            List <TEntity> tempAdditionCache = this.additionCache.ToList();
            List <TEntity> tempUpdateCache   = this.updateCache.ToList();
            List <TEntity> tempDeletionCache = this.deletionCache.ToList();

            try {
                foreach (TEntity addedEntity in addedEntities)
                {
                    /* If the ID is negative, it should be marked for addition */
                    if (tempAdditionCache.Contains(addedEntity, entityComparer))
                    {
                        tempAdditionCache.Remove(addedEntity, entityComparer);
                    }
                    else
                    {
                        throw new InvalidOperationException("Could not find the entity in the memory.");
                    }
                }

                foreach (TEntity existingEntity in existingEntities)
                {
                    /* If the entity was marked for update, remove that mark */
                    if (tempUpdateCache.Contains(existingEntity, entityComparer))
                    {
                        tempUpdateCache.Remove(existingEntity, entityComparer);
                    }

                    /* If the entity exists in the original data source and has not yet been marked for deletion, mark it now */
                    if (memoryStore.Storage.Contains(existingEntity, entityComparer))
                    {
                        if (!tempDeletionCache.Contains(existingEntity, entityComparer))
                        {
                            tempDeletionCache.Add(existingEntity);
                        }
                        else
                        {
                            throw new InvalidOperationException("Cannot delete the same entity more then once.");
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException("Could not find the entity in the internal cache.");
                    }
                }

                /* Replace the original caches to complete the 'transaction' */
                this.additionCache = tempAdditionCache;
                this.updateCache   = tempUpdateCache;
                this.deletionCache = tempDeletionCache;
            }
            finally {
                memoryStore.ExitReadLock();
                if (this.temporaryStorageLock.IsWriteLockHeld)
                {
                    this.temporaryStorageLock.ExitWriteLock();
                }
            }
        }
Exemple #4
0
        /// <summary>Updates an entity in the storage.</summary>
        /// <param name="entity">The entity that must be updated.</param>
        /// <param name="dataSourceInfo">The parameter is not used.</param>
        /// <returns>The updated entity.</returns>
        protected override TEntity UpdateEntityCore(TEntity entity, DataSourceInfo dataSourceInfo)
        {
            if (entity.RecordId == 0)
            {
                throw new InvalidOperationException("Cannot update an entity whose identifier is zero.");
            }

            EntityEqualityComparer <TEntity> entityComparer = new EntityEqualityComparer <TEntity>();
            MemoryStore <TEntity>            memoryStore    = this.SelectMemoryStore(dataSourceInfo);

            try {
                /* First search the temporary storage */
                this.temporaryStorageLock.EnterWriteLock();
                memoryStore.EnterReadLock();

                if (entity.RecordId < 0)
                {
                    if (this.additionCache.Contains(entity, entityComparer))
                    {
                        int     indexOfEntity    = this.additionCache.IndexOf(entity, entityComparer);
                        TEntity repositoryEntity = this.additionCache[indexOfEntity];

                        /* Copy the  values of the updated entity into the cached entity */
                        repositoryEntity.CopyFrom(entity);

                        if (this.SelectCloneDataSourceItems(dataSourceInfo))
                        {
                            return(((ICloneable)repositoryEntity).Clone() as TEntity);
                        }
                        else
                        {
                            entity.CopyFrom(repositoryEntity);
                            return(entity);
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException("Could not find the entity in the addition-cache.");
                    }
                }
                else
                {
                    if (memoryStore.Storage.Contains(entity, entityComparer))
                    {
                        if (this.deletionCache.Contains(entity, entityComparer))
                        {
                            throw new InvalidOperationException("Cannot update the entity since it already marked for deletion.");
                        }

                        TEntity repositoryEntity;
                        if (this.updateCache.Contains(entity, entityComparer))
                        {
                            /* Retrieve the previous updated version of the entity from the cache */
                            int indexOfEntity = this.updateCache.IndexOf(entity, entityComparer);
                            repositoryEntity = this.updateCache[indexOfEntity];

                            /* Copy the  values of the updated entity into the cached entity */
                            repositoryEntity.CopyFrom(entity);
                        }
                        else
                        {
                            /* Retrieve the original version of the entity from the storage */
                            TEntity originalEntity = memoryStore.Storage[memoryStore.Storage.IndexOf(entity, entityComparer)];
                            /* Create a copy of the original entity to avoid any unwanted updates of the original entity */
                            repositoryEntity = originalEntity.CreateCopyOrClone();

                            /* Copy the  values of the updated entity into the (copy of) original entity */
                            repositoryEntity.CopyFrom(entity);
                            /* Store the updated entity in the update cache */
                            this.updateCache.Add(repositoryEntity);
                        }

                        /* Return an instance of TEntity that reflects the changes, but is disconnected from the instance that is stored in the cache
                         * (to avoid unwanted updates from outside the repository) */
                        if (this.SelectCloneDataSourceItems(dataSourceInfo))
                        {
                            return(((ICloneable)repositoryEntity).Clone() as TEntity);
                        }
                        else
                        {
                            entity.CopyFrom(repositoryEntity);
                            return(entity);
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException("Could not find the entity in the internal cache.");
                    }
                }
            }
            finally {
                memoryStore.ExitReadLock();
                if (this.temporaryStorageLock.IsWriteLockHeld)
                {
                    this.temporaryStorageLock.ExitWriteLock();
                }
            }
        }
Exemple #5
0
        /// <summary>Deletes the entity from the storage.</summary>
        /// <param name="entity">The entity that must be removed.</param>
        /// <param name="dataSourceInfo">The parameter is not used.</param>
        protected override void DeleteEntityCore(TEntity entity, DataSourceInfo dataSourceInfo)
        {
            if (entity.RecordId == 0)
            {
                throw new InvalidOperationException("Cannot delete an entity whose identifier is zero.");
            }

            IEqualityComparer <TEntity> entityComparer = new EntityEqualityComparer <TEntity>();

            MemoryStore <TEntity> memoryStore = this.SelectMemoryStore(dataSourceInfo);

            try {
                /* First search the temporary storage */
                this.temporaryStorageLock.EnterWriteLock();
                memoryStore.EnterReadLock();

                if (entity.RecordId < 0)
                {
                    /* If the ID is negative, it should be marked for addition */
                    if (this.additionCache.Contains(entity, entityComparer))
                    {
                        this.additionCache.Remove(entity, entityComparer);
                    }
                    else
                    {
                        throw new InvalidOperationException("Could not find the entity in the memory.");
                    }
                }
                else
                {
                    /* If the entity was marked for update, remove that mark */
                    if (this.updateCache.Contains(entity, entityComparer))
                    {
                        this.updateCache.Remove(entity, entityComparer);
                    }

                    /* If the entity exists in the original data source and has not yet been marked for deletion, mark it now */
                    if (memoryStore.Storage.Contains(entity, entityComparer))
                    {
                        if (!this.deletionCache.Contains(entity, entityComparer))
                        {
                            this.deletionCache.Add(entity);
                        }
                        else
                        {
                            throw new InvalidOperationException("Cannot delete the same entity more then once.");
                        }
                    }
                    else
                    {
                        throw new InvalidOperationException("Could not find the entity in the memory.");
                    }
                }
            }
            finally {
                memoryStore.ExitReadLock();

                if (this.temporaryStorageLock.IsWriteLockHeld)
                {
                    this.temporaryStorageLock.ExitWriteLock();
                }
            }
        }