/// <summary>Finds all the entities that match the expression.</summary> /// <param name="expression">The search-specification.</param> /// <param name="sortRules">The specification of the sort rules that must be applied. Use <see langword="null"/> to ignore the ordering.</param> /// <param name="maximumResults">The maximum number of results that must be retrieved. Use '-1' to retrieve all results.</param> /// <param name="includePaths">The dot-separated lists of related objects to return in the query results.</param> /// <param name="dataSourceInfo">The parameter is not used.</param> /// <returns>The items that match the expression.</returns> protected override IEnumerable <TEntity> FindAllCore(Func <TEntity, bool> expression, SortSpecifications <TEntity> sortRules, int maximumResults, string[] includePaths, DataSourceInfo dataSourceInfo) { MemoryStore <TEntity> memoryStore = this.SelectMemoryStore(dataSourceInfo); try { this.temporaryStorageLock.EnterReadLock(); memoryStore.EnterReadLock(); IEnumerable <TEntity> results = this.ConcatStorage(dataSourceInfo); results = results.Where(item => expression(item)).OrderBy(sortRules); if (maximumResults == -1) { return(results.ToList()); } else { return(results.Take(maximumResults).ToList()); } } finally { memoryStore.ExitReadLock(); this.temporaryStorageLock.ExitReadLock(); } }
/// <summary>Finds the single entity that matches the expression or returns the default value if there were no matches.</summary> /// <param name="expression">The search-specification.</param> /// <param name="includePaths">The dot-separated lists of related objects to return in the query results.</param> /// <param name="dataSourceInfo">The parameter is not used.</param> /// <param name="defaultValue">The value that must be returned if there were no matches.</param> /// <returns>The single result or the default value.</returns> protected override TEntity FindSingleCore(Func <TEntity, bool> expression, string[] includePaths, DataSourceInfo dataSourceInfo, TEntity defaultValue) { MemoryStore <TEntity> memoryStore = this.SelectMemoryStore(dataSourceInfo); try { this.temporaryStorageLock.EnterReadLock(); memoryStore.EnterReadLock(); return(this.ConcatStorage(dataSourceInfo).SingleOrDefault(expression, defaultValue)); } finally { memoryStore.ExitReadLock(); if (this.temporaryStorageLock.IsReadLockHeld) { this.temporaryStorageLock.ExitReadLock(); } } }
/// <summary>Finds the first entity that matches the expression or returns the default value if there were no matches.</summary> /// <param name="expression">The search-specification.</param> /// <param name="sortRules">The specification of the sort rules that must be applied. Use <see langword="null"/> to ignore the ordering.</param> /// <param name="includePaths">The dot-separated lists of related objects to return in the query results.</param> /// <param name="dataSourceInfo">The parameter is not used.</param> /// <param name="defaultValue">The value that must be returned if there were no matches.</param> /// <returns>The first result or the default value.</returns> protected override TEntity FindFirstCore(Func <TEntity, bool> expression, SortSpecifications <TEntity> sortRules, string[] includePaths, DataSourceInfo dataSourceInfo, TEntity defaultValue) { MemoryStore <TEntity> memoryStore = this.SelectMemoryStore(dataSourceInfo); try { this.temporaryStorageLock.EnterReadLock(); memoryStore.EnterReadLock(); return(this.ConcatStorage(dataSourceInfo).OrderBy(sortRules).FirstOrDefault(expression, defaultValue)); } finally { memoryStore.ExitReadLock(); if (this.temporaryStorageLock.IsReadLockHeld) { this.temporaryStorageLock.ExitReadLock(); } } }
/// <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(); } } }
/// <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(); } } }
/// <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(); } } }
/// <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(); } } }