Beispiel #1
0
        /// <summary>Adds the entity to the storage.</summary>
        /// <param name="entity">The entity that must be added.</param>
        /// <param name="dataSourceInfo">The parameter is not used.</param>
        /// <returns>The entity with the updated recordID.</returns>
        protected override TEntity AddEntityCore(TEntity entity, DataSourceInfo dataSourceInfo)
        {
            EntityEqualityComparer <TEntity> entityComparer = new EntityEqualityComparer <TEntity>();

            try {
                this.temporaryStorageLock.EnterWriteLock();

                if (entity.RecordId > 0)
                {
                    /* The entity already has an ID which suggests that it came from the original datasource */
                    if (this.deletionCache.Contains(entity, entityComparer))
                    {
                        /* The entity has been marked for deletion, undelete it... */
                        this.deletionCache.Remove(entity, entityComparer);
                        /* ...and mark it as updated in case any of the fields have been altered. */
                        TEntity repositoryEntity = entity.CreateCopyOrClone();
                        this.updateCache.Add(repositoryEntity);
                        if (this.SelectCloneDataSourceItems(dataSourceInfo))
                        {
                            return(((ICloneable)repositoryEntity).Clone() as TEntity);
                        }
                        else
                        {
                            entity.CopyFrom(repositoryEntity);
                            return(entity);
                        }
                    }
                }

                /* The entity is either new or came from another data source */
                /* Determine the new temporary ID for the entity */
                int newRecordId = -1;
                if (this.additionCache.Count > 0)
                {
                    newRecordId = this.additionCache.Min(t => t.RecordId) - 1;
                }

                TEntity copyOfEntity = entity.CreateCopyOrClone();
                copyOfEntity.RecordId = newRecordId;

                /* Add it to the addition cache */
                this.additionCache.Add(copyOfEntity);

                if (this.SelectCloneDataSourceItems(dataSourceInfo))
                {
                    return(((ICloneable)copyOfEntity).Clone() as TEntity);
                }
                else
                {
                    entity.CopyFrom(copyOfEntity);
                    return(entity);
                }
            }
            finally {
                if (this.temporaryStorageLock.IsWriteLockHeld)
                {
                    this.temporaryStorageLock.ExitWriteLock();
                }
            }
        }
Beispiel #2
0
        /// <summary>Concatenates the global caches and local cache into one complete and up-to-date cache.</summary>
        /// <param name="dataSourceInfo">Any information regarding the data store that is used as data source.</param>
        /// <returns>The concatenated cache-values.</returns>
        private IEnumerable <TEntity> ConcatStorage(DataSourceInfo dataSourceInfo)
        {
            EntityEqualityComparer <TEntity> entityComparer = new EntityEqualityComparer <TEntity>();

            IEnumerable <TEntity> totalCache = this.SelectMemoryStore(dataSourceInfo).Storage;

            if (this.SelectCloneDataSourceItems(dataSourceInfo))
            {
                totalCache = totalCache.Select(t => ((ICloneable)t).Clone() as TEntity);
            }

            totalCache = totalCache.Concat(this.additionCache);
            /*...then remove the entities that were updated from the basic cache and replace them with the updated versions... */
            totalCache = totalCache.Except(this.updateCache, entityComparer).Concat(this.updateCache);
            /* ...finally, remove the deleted entities */
            totalCache = totalCache.Except(this.deletionCache, entityComparer);

            return(totalCache.OrderBy(t => t.RecordId));
        }
Beispiel #3
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();
                }
            }
        }
Beispiel #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();
                }
            }
        }
Beispiel #5
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();
                }
            }
        }
Beispiel #6
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();
                }
            }
        }
Beispiel #7
0
        /// <summary>Adds a collection of new entities to the repository. They are added to the addition cache until it is saved using the
        /// <see cref="Repository{T}.SaveChanges()"/> method. A temporary (negative) RecordID is assigned to the entities. This will be reset when the entity is
        /// saved.</summary>
        /// <param name="entities">The entities that must be added.</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 were added to the repository.</returns>
        protected override IEnumerable <TEntity> AddEntitiesCore(IEnumerable <TEntity> entities, DataSourceInfo dataSourceInfo)
        {
            EntityEqualityComparer <TEntity> entityComparer = new EntityEqualityComparer <TEntity>();

            /* Place the entities in a list to keep track of the entities that have been handled */
            List <TEntity> unhandledEntities = entities.ToList();

            this.temporaryStorageLock.EnterWriteLock();

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

            List <TEntity> updatedEntities = new List <TEntity>();
            List <TEntity> addedEntities   = new List <TEntity>();

            Dictionary <TEntity, TEntity> handledEntities = new Dictionary <TEntity, TEntity>();

            try {
                if (entities.Any(e => e.RecordId > 0))
                {
                    IEnumerable <TEntity> existingEntities = entities.Where(e => e.RecordId > 0);
                    ReferenceEqualityComparer <TEntity> referenceComparer = new ReferenceEqualityComparer <TEntity>();
                    /* At least some of the entities already have an ID which suggests that they came from the original datasource */
                    foreach (TEntity existingEntity in existingEntities)
                    {
                        if (tempDeletionCache.Contains(existingEntity, entityComparer))
                        {
                            /* The entity has been marked for deletion, undelete it... */
                            tempDeletionCache.Remove(existingEntity, entityComparer);
                            /* ...and mark it as updated in case any of the fields have been altered. */
                            TEntity repositoryEntity = existingEntity.CreateCopyOrClone();
                            tempUpdateCache.Add(repositoryEntity);
                            updatedEntities.Add(repositoryEntity);

                            handledEntities.Add(existingEntity, repositoryEntity);
                            bool removeResult = unhandledEntities.Remove(existingEntity, referenceComparer);
                            Debug.Assert(removeResult, "Somehow the result could not be removed from the collection of handled entities.");
                        }
                    }
                }

                if (unhandledEntities.Count > 0)
                {
                    /* At least some of the entities are either new or came from another data source */
                    /* Determine the new temporary ID for the entities */
                    int newRecordId = -1;
                    if (tempAdditionCache.Count > 0)
                    {
                        newRecordId = tempAdditionCache.Min(t => t.RecordId) - 1;
                    }

                    foreach (TEntity unhandledEntity in unhandledEntities)
                    {
                        TEntity copyOfEntity = unhandledEntity.CreateCopyOrClone();

                        copyOfEntity.RecordId = newRecordId;
                        --newRecordId;
                        /* Add it to the addition cache */
                        tempAdditionCache.Add(copyOfEntity);
                        addedEntities.Add(copyOfEntity);
                        handledEntities.Add(unhandledEntity, copyOfEntity);
                    }
                }

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

                if (this.SelectCloneDataSourceItems(dataSourceInfo))
                {
                    return(addedEntities.Concat(updatedEntities).Select(entity => ((ICloneable)entity).Clone() as TEntity).ToList());
                }
                else
                {
                    handledEntities.ForEach(kvp => kvp.Key.CopyFrom(kvp.Value));
                    return(entities);
                }
            }
            finally {
                this.temporaryStorageLock.ExitWriteLock();
            }
        }