/// <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; }
public void Setup() { dataSegmenter = new DataSegmenter(); }