private void PageEntities <TEntity>( SmartObjectContext context, DbSet <MediaStorage> mediaStorages, IOrderedQueryable <TEntity> query, Action <TEntity> moveEntity) where TEntity : BaseEntity, IHasMedia { var pageIndex = 0; IPagedList <TEntity> entities = null; do { if (entities != null) { // detach all entities from previous page to save memory context.DetachAll(false); entities.Clear(); entities = null; } // load max 1000 entities at once entities = new PagedList <TEntity>(query, pageIndex++, PAGE_SIZE); entities.Each(x => moveEntity(x)); // save the current batch to database context.SaveChanges(); }while (entities.HasNextPage); }
protected virtual void PageEntities <TEntity>(IOrderedQueryable <TEntity> query, Action <TEntity> moveEntity) where TEntity : BaseEntity, IHasMedia { var pageIndex = 0; IPagedList <TEntity> entities = null; do { if (entities != null) { // detach all entities from previous page to save memory _services.DbContext.DetachEntities(entities); entities.Clear(); entities = null; } // load max 100 entities at once entities = new PagedList <TEntity>(query, pageIndex++, PAGE_SIZE); entities.Each(x => moveEntity(x)); // save the current batch to database _services.DbContext.SaveChanges(); }while (entities.HasNextPage); }
protected int MovePictures(bool toDb) { // long running operation, therefore some code chunks are redundant here in order to boost performance // a list of ALL file paths that were either deleted or created var affectedFiles = new List <string>(1000); var ctx = _pictureRepository.Context; var failed = false; int i = 0; using (var scope = new DbContextScope(ctx: ctx, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false)) { using (var tx = ctx.BeginTransaction()) { // we are about to process data in chunks but want to commit ALL at once when ALL chunks have been processed successfully. try { int pageIndex = 0; IPagedList <Picture> pictures = null; do { if (pictures != null) { // detach all entities from previous page to save memory ctx.DetachEntities(pictures); // breathe pictures.Clear(); pictures = null; } // load max 500 picture entities at once pictures = this.GetPictures(pageIndex, 500); pageIndex++; foreach (var picture in pictures) { string filePath = null; if (!toDb) { if (picture.PictureBinary != null && picture.PictureBinary.Length > 0) { // save picture as file SavePictureInFile(picture.Id, picture.PictureBinary, picture.MimeType, out filePath); } // remove picture binary from DB picture.PictureBinary = new byte[0]; } else { // load picture binary from file and set in DB var picBinary = LoadPictureFromFile(picture.Id, picture.MimeType, out filePath); if (picBinary.Length > 0) { picture.PictureBinary = picBinary; } } // remember file path: we must be able to rollback IO operations on transaction failure if (filePath.HasValue()) { affectedFiles.Add(filePath); //picture.IsNew = true; } // explicitly attach modified entity to context, because we disabled AutoCommit picture.UpdatedOnUtc = DateTime.UtcNow; _pictureRepository.Update(picture); i++; } // save the current batch to DB ctx.SaveChanges(); } while (pictures.HasNextPage); // FIRE! tx.Commit(); } catch (Exception ex) { failed = true; tx.Rollback(); _settingService.SetSetting <bool>("Media.Images.StoreInDB", !toDb); _notifier.Error(ex.Message); _logger.Error(ex); } } } if (affectedFiles.Count > 0) { if ((toDb && !failed) || (!toDb && failed)) { // FS > DB sucessful OR DB > FS failed: delete all physical files // run a background task for the deletion of files (fire & forget) Task.Factory.StartNew(state => { var files = state as string[]; foreach (var path in files) { if (File.Exists(path)) { File.Delete(path); } } }, affectedFiles.ToArray()).ConfigureAwait(false); } // shrink database (only when DB > FS and success) if (!toDb && !failed) { ctx.ShrinkDatabase(); } } return(i); }