private void ProcessProductPictures(ICollection<ImportRow<Product>> batch, ImportResult result) { // true, cause pictures must be saved and assigned an id // prior adding a mapping. _rsProductPicture.AutoCommitEnabled = true; ProductPicture lastInserted = null; int equalPictureId = 0; foreach (var row in batch) { var pictures = new string[] { row.GetValue<string>("Picture1"), row.GetValue<string>("Picture2"), row.GetValue<string>("Picture3") }; int i = 0; try { for (i = 0; i < pictures.Length; i++) { var picture = pictures[i]; if (picture.IsEmpty() || !File.Exists(picture)) continue; var currentPictures = _rsProductPicture.TableUntracked.Expand(x => x.Picture).Where(x => x.ProductId == row.Entity.Id).Select(x => x.Picture).ToList(); var pictureBinary = _pictureService.FindEqualPicture(picture, currentPictures, out equalPictureId); if (pictureBinary != null && pictureBinary.Length > 0) { // no equal picture found in sequence var newPicture = _pictureService.InsertPicture(pictureBinary, "image/jpeg", _pictureService.GetPictureSeName(row.EntityDisplayName), true, false, false); if (newPicture != null) { var mapping = new ProductPicture { ProductId = row.Entity.Id, PictureId = newPicture.Id, DisplayOrder = 1, }; _rsProductPicture.Insert(mapping); lastInserted = mapping; } } else { result.AddInfo("Found equal picture in data store. Skipping field.", row.GetRowInfo(), "Picture" + (i + 1).ToString()); } } } catch (Exception ex) { result.AddWarning(ex.Message, row.GetRowInfo(), "Picture" + (i + 1).ToString()); } } // Perf: notify only about LAST insertion and update if (lastInserted != null) _eventPublisher.EntityInserted(lastInserted); }
/// <summary> /// Import products from XLSX file /// </summary> /// <param name="stream">Stream</param> public virtual ImportResult ImportProductsFromExcel( Stream stream, CancellationToken cancellationToken, IProgress<ImportProgressInfo> progress = null) { Guard.ArgumentNotNull(() => stream); var result = new ImportResult(); int saved = 0; if (progress != null) progress.Report(new ImportProgressInfo { ElapsedTime = TimeSpan.Zero }); using (var scope = new DbContextScope(ctx: _rsProduct.Context, autoDetectChanges: false, proxyCreation: false, validateOnSave: false)) { try { using (var segmenter = new DataSegmenter<Product>(stream)) { result.TotalRecords = segmenter.TotalRows; while (segmenter.ReadNextBatch() && !cancellationToken.IsCancellationRequested) { var batch = segmenter.CurrentBatch; // Perf: detach all entities _rsProduct.Context.DetachAll(false); // Update progress for calling thread if (progress != null) { progress.Report(new ImportProgressInfo { TotalRecords = result.TotalRecords, TotalProcessed = segmenter.CurrentSegmentFirstRowIndex - 1, NewRecords = result.NewRecords, ModifiedRecords = result.ModifiedRecords, ElapsedTime = DateTime.UtcNow - result.StartDateUtc, TotalWarnings = result.Messages.Count(x => x.MessageType == ImportMessageType.Warning), TotalErrors = result.Messages.Count(x => x.MessageType == ImportMessageType.Error), }); } // =========================================================================== // 1.) Import products // =========================================================================== try { saved = ProcessProducts(batch, result); } catch (Exception ex) { result.AddError(ex, segmenter.CurrentSegment, "ProcessProducts"); } // reduce batch to saved (valid) products. // No need to perform import operations on errored products. batch = batch.Where(x => x.Entity != null && !x.IsTransient).AsReadOnly(); // update result object result.NewRecords += batch.Count(x => x.IsNew && !x.IsTransient); result.ModifiedRecords += batch.Count(x => !x.IsNew && !x.IsTransient); // =========================================================================== // 2.) Import SEO Slugs // IMPORTANT: Unlike with Products AutoCommitEnabled must be TRUE, // as Slugs are going to be validated against existing ones in DB. // =========================================================================== if (batch.Any(x => x.IsNew || (x.ContainsKey("SeName") || x.NameChanged))) { try { _rsProduct.Context.AutoDetectChangesEnabled = true; ProcessSlugs(batch, result); } catch (Exception ex) { result.AddError(ex, segmenter.CurrentSegment, "ProcessSeoSlugs"); } finally { _rsProduct.Context.AutoDetectChangesEnabled = false; } } // =========================================================================== // 3.) Import Localizations // =========================================================================== try { ProcessLocalizations(batch, result); } catch (Exception ex) { result.AddError(ex, segmenter.CurrentSegment, "ProcessLocalizations"); } // =========================================================================== // 4.) Import product category mappings // =========================================================================== if (batch.Any(x => x.ContainsKey("CategoryIds"))) { try { ProcessProductCategories(batch, result); } catch (Exception ex) { result.AddError(ex, segmenter.CurrentSegment, "ProcessProductCategories"); } } // =========================================================================== // 5.) Import product manufacturer mappings // =========================================================================== if (batch.Any(x => x.ContainsKey("ManufacturerIds"))) { try { ProcessProductManufacturers(batch, result); } catch (Exception ex) { result.AddError(ex, segmenter.CurrentSegment, "ProcessProductManufacturers"); } } // =========================================================================== // 6.) Import product picture mappings // =========================================================================== if (batch.Any(x => x.ContainsKey("Picture1") || x.ContainsKey("Picture2") || x.ContainsKey("Picture3"))) { try { ProcessProductPictures(batch, result); } catch (Exception ex) { result.AddError(ex, segmenter.CurrentSegment, "ProcessProductPictures"); } } } } } catch (Exception ex) { result.AddError(ex, null, "ReadFile"); } } result.EndDateUtc = DateTime.UtcNow; if (cancellationToken.IsCancellationRequested) { result.Cancelled = true; result.AddInfo("Import task was cancelled by user"); } return result; }