예제 #1
0
        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;

            _pictureRepository.AutoCommitEnabled = false;

            var failed = false;

            int i = 0;

            using (var scope = new DbContextScope(ctx: ctx, autoDetectChanges: false, proxyCreation: false, validateOnSave: 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
                                pictures.Each(x => ctx.Detach(x));

                                // 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
                                _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);
                    }
                }
            }

            _pictureRepository.AutoCommitEnabled = true;

            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 && DataSettings.Current.IsSqlServer)
                {
                    try
                    {
                        ctx.ExecuteSqlCommand("DBCC SHRINKDATABASE(0)", true);
                    }
                    catch { }
                }
            }

            return(i);
        }