public virtual string CreateTextReport(ImportResult result)
		{
			var sb = new StringBuilder();

			using (var writer = new StringWriter(sb))
			{
				writer.WriteLine("SUMMARY");
				writer.WriteLine("==================================================================================");
				writer.WriteLine("Started: {0}".FormatCurrent(result.StartDateUtc.ToLocalTime()));
				writer.WriteLine("Finished: {0}{1}".FormatCurrent(result.EndDateUtc.ToLocalTime(), result.Cancelled ? " (cancelled by user)" : ""));
				writer.WriteLine("Duration: {0}".FormatCurrent((result.EndDateUtc - result.StartDateUtc).ToString("g")));

				writer.WriteLine("");
				writer.WriteLine("Total rows in source: {0}".FormatCurrent(result.TotalRecords));
				writer.WriteLine("Rows processed: {0}".FormatCurrent(result.AffectedRecords));
				writer.WriteLine("Products imported: {0}".FormatCurrent(result.NewRecords));
				writer.WriteLine("Products updated: {0}".FormatCurrent(result.ModifiedRecords));

				writer.WriteLine("");
				writer.WriteLine("Warnings: {0}".FormatCurrent(result.Messages.Count(x => x.MessageType == ImportMessageType.Warning)));
				writer.WriteLine("Errors: {0}".FormatCurrent(result.Messages.Count(x => x.MessageType == ImportMessageType.Error)));

				writer.WriteLine("");
				writer.WriteLine("");
				writer.WriteLine("MESSAGES");
				writer.WriteLine("==================================================================================");

				foreach (var message in result.Messages)
				{
					string msg = string.Empty;
					var prefix = new List<string>();
					if (message.AffectedItem != null)
					{
						prefix.Add("Pos: " + message.AffectedItem.Position + 1);
					}
					if (message.AffectedField.HasValue())
					{
						prefix.Add("Field: " + message.AffectedField);
					}

					if (prefix.Any())
					{
						msg = "[{0}] ".FormatCurrent(String.Join(", ", prefix));
					}

					msg += message.Message;

					writer.WriteLine("{0}: {1}".FormatCurrent(message.MessageType.ToString().ToUpper(), msg));
				}
			}

			return sb.ToString();
		}
Beispiel #2
0
        private int ProcessSlugs(ICollection<ImportRow<Product>> batch, ImportResult result)
        {
            var slugMap = new Dictionary<string, UrlRecord>(100);
            Func<string, UrlRecord> slugLookup = ((s) => {
                if (slugMap.ContainsKey(s))
                {
                    return slugMap[s];
                }
                return (UrlRecord)null;
            });

            var entityName = typeof(Product).Name;

            foreach (var row in batch)
            {
                if (row.IsNew || row.NameChanged || row.ContainsKey("SeName"))
                {
                    try
                    {
                        string seName = row.GetValue<string>("SeName");
                        seName = row.Entity.ValidateSeName(seName, row.Entity.Name, true, _urlRecordService, _seoSettings, extraSlugLookup: slugLookup);

                        UrlRecord urlRecord = null;

                        if (row.IsNew)
                        {
                            // dont't bother validating SeName for new entities.
                            urlRecord = new UrlRecord
                            {
                                EntityId = row.Entity.Id,
                                EntityName = entityName,
                                Slug = seName,
                                LanguageId = 0,
                                IsActive = true,
                            };
                            _rsUrlRecord.Insert(urlRecord);
                        }
                        else
                        {
                            urlRecord = _urlRecordService.SaveSlug(row.Entity, seName, 0);
                        }

                        if (urlRecord != null)
                        {
                            // a new record was inserted to the store: keep track of it for this batch.
                            slugMap[seName] = urlRecord;
                        }
                    }
                    catch (Exception ex)
                    {
                        result.AddWarning(ex.Message, row.GetRowInfo(), "SeName");
                    }
                }
            }

            // commit whole batch at once
            return _rsUrlRecord.Context.SaveChanges();
        }
Beispiel #3
0
        private int ProcessProducts(ICollection<ImportRow<Product>> batch, ImportResult result)
        {
            _rsProduct.AutoCommitEnabled = true;

            Product lastInserted = null;
            Product lastUpdated = null;

            foreach (var row in batch)
            {
                if (row.Count == 0)
                    continue;

                Product product = null;

                object key;

                // try get by int ID
                if (row.TryGetValue("Id", out key) && key.ToString().ToInt() > 0)
                {
                    product = _productService.GetProductById(key.ToString().ToInt());
                }

                // try get by SKU
                if (product == null && row.TryGetValue("SKU", out key))
                {
                    product = _productService.GetProductBySku(key.ToString());
                }

                // try get by GTIN
                if (product == null && row.TryGetValue("Gtin", out key))
                {
                    product = _productService.GetProductByGtin(key.ToString());
                }

                if (product == null)
                {
                    // a Name is required with new products.
                    if (!row.ContainsKey("Name"))
                    {
                        result.AddError("The 'Name' field is required for new products. Skipping row.", row.GetRowInfo(), "Name");
                        continue;
                    }
                    product = new Product();
                }

                row.Initialize(product, row["Name"].ToString());

                if (!row.IsNew)
                {
                    if (!product.Name.Equals(row["Name"].ToString(), StringComparison.OrdinalIgnoreCase))
                    {
                        // Perf: use this later for SeName updates.
                        row.NameChanged = true;
                    }
                }

                row.SetProperty(result, product, (x) => x.Sku);
                row.SetProperty(result, product, (x) => x.Gtin);
                row.SetProperty(result, product, (x) => x.ManufacturerPartNumber);
                row.SetProperty(result, product, (x) => x.ProductTypeId, (int)ProductType.SimpleProduct);
                row.SetProperty(result, product, (x) => x.ParentGroupedProductId);
                row.SetProperty(result, product, (x) => x.VisibleIndividually, true);
                row.SetProperty(result, product, (x) => x.Name);
                row.SetProperty(result, product, (x) => x.ShortDescription);
                row.SetProperty(result, product, (x) => x.FullDescription);
                row.SetProperty(result, product, (x) => x.ProductTemplateId);
                row.SetProperty(result, product, (x) => x.ShowOnHomePage);
                row.SetProperty(result, product, (x) => x.HomePageDisplayOrder);
                row.SetProperty(result, product, (x) => x.MetaKeywords);
                row.SetProperty(result, product, (x) => x.MetaDescription);
                row.SetProperty(result, product, (x) => x.MetaTitle);
                row.SetProperty(result, product, (x) => x.AllowCustomerReviews, true);
                row.SetProperty(result, product, (x) => x.Published, true);
                row.SetProperty(result, product, (x) => x.IsGiftCard);
                row.SetProperty(result, product, (x) => x.GiftCardTypeId);
                row.SetProperty(result, product, (x) => x.RequireOtherProducts);
                row.SetProperty(result, product, (x) => x.RequiredProductIds);
                row.SetProperty(result, product, (x) => x.AutomaticallyAddRequiredProducts);
                row.SetProperty(result, product, (x) => x.IsDownload);
                row.SetProperty(result, product, (x) => x.DownloadId);
                row.SetProperty(result, product, (x) => x.UnlimitedDownloads, true);
                row.SetProperty(result, product, (x) => x.MaxNumberOfDownloads, 10);
                row.SetProperty(result, product, (x) => x.DownloadActivationTypeId, 1);
                row.SetProperty(result, product, (x) => x.HasSampleDownload);
                row.SetProperty(result, product, (x) => x.SampleDownloadId, (int?)null, ZeroToNull);
                row.SetProperty(result, product, (x) => x.HasUserAgreement);
                row.SetProperty(result, product, (x) => x.UserAgreementText);
                row.SetProperty(result, product, (x) => x.IsRecurring);
                row.SetProperty(result, product, (x) => x.RecurringCycleLength, 100);
                row.SetProperty(result, product, (x) => x.RecurringCyclePeriodId);
                row.SetProperty(result, product, (x) => x.RecurringTotalCycles, 10);
                row.SetProperty(result, product, (x) => x.IsShipEnabled, true);
                row.SetProperty(result, product, (x) => x.IsFreeShipping);
                row.SetProperty(result, product, (x) => x.AdditionalShippingCharge);
                row.SetProperty(result, product, (x) => x.IsEsd);
                row.SetProperty(result, product, (x) => x.IsTaxExempt);
                row.SetProperty(result, product, (x) => x.TaxCategoryId, 1);
                row.SetProperty(result, product, (x) => x.ManageInventoryMethodId);
                row.SetProperty(result, product, (x) => x.StockQuantity, 10000);
                row.SetProperty(result, product, (x) => x.DisplayStockAvailability);
                row.SetProperty(result, product, (x) => x.DisplayStockQuantity);
                row.SetProperty(result, product, (x) => x.MinStockQuantity);
                row.SetProperty(result, product, (x) => x.LowStockActivityId);
                row.SetProperty(result, product, (x) => x.NotifyAdminForQuantityBelow, 1);
                row.SetProperty(result, product, (x) => x.BackorderModeId);
                row.SetProperty(result, product, (x) => x.AllowBackInStockSubscriptions);
                row.SetProperty(result, product, (x) => x.OrderMinimumQuantity, 1);
                row.SetProperty(result, product, (x) => x.OrderMaximumQuantity, 10000);
                row.SetProperty(result, product, (x) => x.AllowedQuantities);
                row.SetProperty(result, product, (x) => x.DisableBuyButton);
                row.SetProperty(result, product, (x) => x.DisableWishlistButton);
                row.SetProperty(result, product, (x) => x.AvailableForPreOrder);
                row.SetProperty(result, product, (x) => x.CallForPrice);
                row.SetProperty(result, product, (x) => x.Price);
                row.SetProperty(result, product, (x) => x.OldPrice);
                row.SetProperty(result, product, (x) => x.ProductCost);
                row.SetProperty(result, product, (x) => x.SpecialPrice);
                row.SetProperty(result, product, (x) => x.SpecialPriceStartDateTimeUtc, null, OADateToUtcDate);
                row.SetProperty(result, product, (x) => x.SpecialPriceEndDateTimeUtc, null, OADateToUtcDate);
                row.SetProperty(result, product, (x) => x.CustomerEntersPrice);
                row.SetProperty(result, product, (x) => x.MinimumCustomerEnteredPrice);
                row.SetProperty(result, product, (x) => x.MaximumCustomerEnteredPrice, 1000);
                row.SetProperty(result, product, (x) => x.Weight);
                row.SetProperty(result, product, (x) => x.Length);
                row.SetProperty(result, product, (x) => x.Width);
                row.SetProperty(result, product, (x) => x.Height);
                row.SetProperty(result, product, (x) => x.DeliveryTimeId);
                row.SetProperty(result, product, (x) => x.QuantityUnitId);
                row.SetProperty(result, product, (x) => x.BasePriceEnabled);
                row.SetProperty(result, product, (x) => x.BasePriceMeasureUnit);
                row.SetProperty(result, product, (x) => x.BasePriceAmount);
                row.SetProperty(result, product, (x) => x.BasePriceBaseAmount);
                row.SetProperty(result, product, (x) => x.BundlePerItemPricing);
                row.SetProperty(result, product, (x) => x.BundlePerItemShipping);
                row.SetProperty(result, product, (x) => x.BundlePerItemShoppingCart);
                row.SetProperty(result, product, (x) => x.BundleTitleText);
                row.SetProperty(result, product, (x) => x.AvailableStartDateTimeUtc, null, OADateToUtcDate);
                row.SetProperty(result, product, (x) => x.AvailableEndDateTimeUtc, null, OADateToUtcDate);
                row.SetProperty(result, product, (x) => x.LimitedToStores);

                string storeIds = row.GetValue<string>("StoreIds");
                if (storeIds.HasValue())
                {
                    _storeMappingService.SaveStoreMappings(product,
                        row["StoreIds"].ToString()
                        .Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(x => Convert.ToInt32(x.Trim())).ToArray());
                }

                row.SetProperty(result, product, (x) => x.CreatedOnUtc, DateTime.UtcNow, OADateToUtcDate);

                product.UpdatedOnUtc = DateTime.UtcNow;

                if (row.IsTransient)
                {
                    _rsProduct.Insert(product);
                    lastInserted = product;
                }
                else
                {
                    _rsProduct.Update(product);
                    lastUpdated = product;
                }
            }

            // commit whole batch at once
            var num = _rsProduct.Context.SaveChanges();

            // Perf: notify only about LAST insertion and update
            if (lastInserted != null)
                _eventPublisher.EntityInserted(lastInserted);
            if (lastUpdated != null)
                _eventPublisher.EntityUpdated(lastUpdated);

            return num;
        }
Beispiel #4
0
        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);
        }
Beispiel #5
0
        private int ProcessProductManufacturers(ICollection<ImportRow<Product>> batch, ImportResult result)
        {
            _rsProductManufacturer.AutoCommitEnabled = false;

            ProductManufacturer lastInserted = null;

            foreach (var row in batch)
            {
                string manufacturerIds = row.GetValue<string>("ManufacturerIds");
                if (manufacturerIds.HasValue())
                {
                    try
                    {
                        foreach (var id in manufacturerIds.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(x => Convert.ToInt32(x.Trim())))
                        {
                            if (_rsProductManufacturer.TableUntracked.Where(x => x.ProductId == row.Entity.Id && x.ManufacturerId == id).FirstOrDefault() == null)
                            {
                                // ensure that manufacturer exists
                                var manufacturer = _manufacturerService.GetManufacturerById(id);
                                if (manufacturer != null)
                                {
                                    var productManufacturer = new ProductManufacturer
                                    {
                                        ProductId = row.Entity.Id,
                                        ManufacturerId = manufacturer.Id,
                                        IsFeaturedProduct = false,
                                        DisplayOrder = 1
                                    };
                                    _rsProductManufacturer.Insert(productManufacturer);
                                    lastInserted = productManufacturer;
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        result.AddWarning(ex.Message, row.GetRowInfo(), "ManufacturerIds");
                    }
                }
            }

            // commit whole batch at once
            var num = _rsProductManufacturer.Context.SaveChanges();

            // Perf: notify only about LAST insertion and update
            if (lastInserted != null)
                _eventPublisher.EntityInserted(lastInserted);

            return num;
        }
Beispiel #6
0
        private int ProcessLocalizations(ICollection<ImportRow<Product>> batch, ImportResult result)
        {
            //_rsProductManufacturer.AutoCommitEnabled = false;

            //string lastInserted = null;

            var languages = _languageService.GetAllLanguages(true);

            foreach (var row in batch)
            {

                Product product = null;

                //get product
                try
                {
                    product = _productService.GetProductById(row.Entity.Id);
                }
                catch (Exception ex)
                {
                    result.AddWarning(ex.Message, row.GetRowInfo(), "ProcessLocalizations Product");
                }

                foreach (var lang in languages)
                {
                    string localizedName = row.GetValue<string>("Name[" + lang.UniqueSeoCode + "]");
                    string localizedShortDescription = row.GetValue<string>("ShortDescription[" + lang.UniqueSeoCode + "]");
                    string localizedFullDescription = row.GetValue<string>("FullDescription[" + lang.UniqueSeoCode + "]");

                    if (localizedName.HasValue())
                    {
                        _localizedEntityService.SaveLocalizedValue(product, x => x.Name, localizedName, lang.Id);
                    }
                    if (localizedShortDescription.HasValue())
                    {
                        _localizedEntityService.SaveLocalizedValue(product, x => x.ShortDescription, localizedShortDescription, lang.Id);
                    }
                    if (localizedFullDescription.HasValue())
                    {
                        _localizedEntityService.SaveLocalizedValue(product, x => x.FullDescription, localizedFullDescription, lang.Id);
                    }
                }
            }

            // commit whole batch at once
            var num = _rsProductManufacturer.Context.SaveChanges();

            // Perf: notify only about LAST insertion and update
            //if (lastInserted != null)
            //    _eventPublisher.EntityInserted(lastInserted);

            return num;
        }
Beispiel #7
0
        /// <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 ImportResult ImportSubscribers(Stream stream)
        {
            Guard.ArgumentNotNull(() => stream);

            var result = new ImportResult();
            var toAdd = new List<NewsLetterSubscription>();
            var toUpdate = new List<NewsLetterSubscription>();
            var autoCommit = _subscriptionRepository.AutoCommitEnabled;
            var validateOnSave = _subscriptionRepository.Context.ValidateOnSaveEnabled;
            var autoDetectChanges = _subscriptionRepository.Context.AutoDetectChangesEnabled;
            var proxyCreation = _subscriptionRepository.Context.ProxyCreationEnabled;

            try
            {
                using (var reader = new StreamReader(stream))
                {
                    _subscriptionRepository.Context.ValidateOnSaveEnabled = false;
                    _subscriptionRepository.Context.AutoDetectChangesEnabled = false;
                    _subscriptionRepository.Context.ProxyCreationEnabled = false;
                    
                    while (!reader.EndOfStream)
                    {
                        string line = reader.ReadLine();
                        if (line.IsEmpty())
                        {
                            continue;
                        }
                        string[] tmp = line.Split(',');

                        var email = "";
                        bool isActive = true;
						int storeId = 0;

                        // parse
                        if (tmp.Length == 1)
                        {
                            // "email" only
                            email = tmp[0].Trim();
                        }
                        else if (tmp.Length == 2)
                        {
                            // "email" and "active" fields specified
                            email = tmp[0].Trim();
                            isActive = Boolean.Parse(tmp[1].Trim());
                        }
						else if (tmp.Length == 3)
						{
							email = tmp[0].Trim();
							isActive = Boolean.Parse(tmp[1].Trim());
							storeId = int.Parse(tmp[2].Trim());
						}
						else
						{
							throw new SmartException("Wrong file format (expected comma separated entries 'Email' and optionally 'IsActive')");
						}

                        result.TotalRecords++;

                        if (email.Length > 255)
                        {
                            result.AddWarning("The emal address '{0}' exceeds the maximun allowed length of 255.".FormatInvariant(email));
                            continue;
                        }

                        if (!email.IsEmail())
                        {
							result.AddWarning("'{0}' is not a valid email address.".FormatInvariant(email));
                            continue;
                        }

						if (storeId == 0)
						{
							storeId = _storeService.GetAllStores().First().Id;
						}

                        // import
                        var subscription = (from nls in _subscriptionRepository.Table
                                            where nls.Email == email && nls.StoreId == storeId
                                            orderby nls.Id
                                            select nls).FirstOrDefault();

                        if (subscription != null)
                        {
                            subscription.Active = isActive;

                            toUpdate.Add(subscription);
                            result.ModifiedRecords++;
                        }
                        else
                        {
                            subscription = new NewsLetterSubscription()
                            {
                                Active = isActive,
                                CreatedOnUtc = DateTime.UtcNow,
                                Email = email,
                                NewsLetterSubscriptionGuid = Guid.NewGuid(),
								StoreId = storeId
                            };

                            toAdd.Add(subscription);
                            result.NewRecords++;
                        }
                    }
                }

                // insert new subscribers
                _subscriptionRepository.AutoCommitEnabled = true;
                _subscriptionRepository.InsertRange(toAdd, 500);
                toAdd.Clear();

                // update modified subscribers
                _subscriptionRepository.AutoCommitEnabled = false;
                toUpdate.Each(x => 
                {
                    _subscriptionRepository.Update(x);
                });
                _subscriptionRepository.Context.SaveChanges();
                toUpdate.Clear();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                _subscriptionRepository.AutoCommitEnabled = autoCommit;
                _subscriptionRepository.Context.ValidateOnSaveEnabled = validateOnSave;
                _subscriptionRepository.Context.AutoDetectChangesEnabled = autoDetectChanges;
                _subscriptionRepository.Context.ProxyCreationEnabled = proxyCreation;
            }

            return result;
        }
		private async Task<int> ProcessProductCategories(ICollection<ImportRow<Product>> batch, ImportResult result)
		{
			_rsProductCategory.AutoCommitEnabled = false;

			ProductCategory lastInserted = null;
			
			foreach (var row in batch)
			{
				string categoryIds = row.GetValue<string>("CategoryIds");
				if (categoryIds.HasValue())
				{
					try
					{
						foreach (var id in categoryIds.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(x => Convert.ToInt32(x.Trim())))
						{
							if (_rsProductCategory.TableUntracked.Where(x => x.ProductId == row.Entity.Id && x.CategoryId == id).FirstOrDefault() == null)
							{
								// ensure that category exists
								var category = _categoryService.GetCategoryById(id);
								if (category != null)
								{
									var productCategory = new ProductCategory()
									{
										ProductId = row.Entity.Id,
										CategoryId = category.Id,
										IsFeaturedProduct = false,
										DisplayOrder = 1
									};
									_rsProductCategory.Insert(productCategory);
									lastInserted = productCategory;
								}
							}
						}
					}
					catch (Exception ex)
					{
						result.AddWarning(ex.Message, row.GetRowInfo(), "CategoryIds");
					}
				}
			}

			// commit whole batch at once
			var t = await _rsProductCategory.Context.SaveChangesAsync();

			// Perf: notify only about LAST insertion and update
			if (lastInserted != null)
				_eventPublisher.EntityInserted(lastInserted);

			return t;
		}
		private void ProcessSlugs(ICollection<ImportRow<Product>> batch, ImportResult result)
		{
			foreach (var row in batch)
			{
				if (row.IsNew || row.NameChanged || row.ContainsKey("SeName"))
				{
					try
					{
						string seName = row.GetValue<string>("SeName");
						_urlRecordService.SaveSlug(row.Entity, row.Entity.ValidateSeName(seName, row.Entity.Name, true), 0);
					}
					catch (Exception ex)
					{
						result.AddWarning(ex.Message, row.GetRowInfo(), "SeName");
					}
				}
			}
		}