public virtual async Task <int> SendNotificationsToSubscribersAsync(Product product) { Guard.NotNull(product, nameof(product)); var numberOfMessages = 0; var subscriptionQuery = _db.BackInStockSubscriptions.ApplyStandardFilter(product.Id, null, null); var pager = new FastPager <BackInStockSubscription>(subscriptionQuery); while ((await pager.ReadNextPageAsync <BackInStockSubscription>()).Out(out var subscriptions)) { foreach (var subscription in subscriptions) { // Ensure that the customer is registered (simple and fast way). if (subscription?.Customer?.Email?.IsEmail() ?? false) { await _messageFactory.SendBackInStockNotificationAsync(subscription); ++numberOfMessages; } } _db.BackInStockSubscriptions.RemoveRange(subscriptions); await _db.SaveChangesAsync(); } return(numberOfMessages); }
public virtual CombinationAvailabilityInfo IsCombinationAvailable( Product product, IEnumerable <ProductVariantAttribute> attributes, IEnumerable <ProductVariantAttributeValue> selectedValues, ProductVariantAttributeValue currentValue) { if (product == null || _performanceSettings.MaxUnavailableAttributeCombinations <= 0 || !(selectedValues?.Any() ?? false)) { return(null); } // Get unavailable combinations. var unavailableCombinations = _cache.Get(UNAVAILABLE_COMBINATIONS_KEY.FormatInvariant(product.Id), () => { var data = new Dictionary <string, CombinationAvailabilityInfo>(); var query = _pvacRepository.TableUntracked.Where(x => x.ProductId == product.Id); if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) { query = query.Where(x => !x.IsActive || (x.StockQuantity <= 0 && !x.AllowOutOfStockOrders)); } else { query = query.Where(x => !x.IsActive); } // Do not proceed if there are too many unavailable combinations. var unavailableCombinationsCount = query.Count(); if (unavailableCombinationsCount <= _performanceSettings.MaxUnavailableAttributeCombinations) { var pager = new FastPager <ProductVariantAttributeCombination>(query); while (pager.ReadNextPage(out var combinations)) { foreach (var combination in combinations) { var map = DeserializeProductVariantAttributes(combination.AttributesXml); if (map.Any()) { // <ProductVariantAttribute.Id>:<ProductVariantAttributeValue.Id>[,...] var valuesKeys = map .OrderBy(x => x.Key) .Select(x => $"{x.Key}:{string.Join(",", x.Value.OrderBy(y => y))}"); data[string.Join("-", valuesKeys)] = new CombinationAvailabilityInfo { IsActive = combination.IsActive, IsOutOfStock = combination.StockQuantity <= 0 && !combination.AllowOutOfStockOrders }; } } } }
public void MigrateMediaFiles_Old(SmartObjectContext ctx) { var query = ctx.Set <MediaFile>(); //.Where(x => x.Version == 0) //.Include(x => x.MediaStorage); var pager = new FastPager <MediaFile>(query, 1000); using (var scope = new DbContextScope(ctx, hooksEnabled: false, autoCommit: false, proxyCreation: false, validateOnSave: false, lazyLoading: false)) { while (pager.ReadNextPage(out var files)) { foreach (var file in files) { if (file.Version > 0) { continue; } if (file.Extension.IsEmpty()) { file.Extension = MimeTypes.MapMimeTypeToExtension(file.MimeType); } var name = file.Name; if (name.IsEmpty()) { name = file.Id.ToString(System.Globalization.CultureInfo.InvariantCulture); } else { name = MediaHelper.NormalizeFileName(file.Name.Truncate(292)); } file.Name = name + "." + file.Extension; file.CreatedOnUtc = file.UpdatedOnUtc; file.Version = 1; ProcessMediaFile(file); } // Save to DB int num = scope.Commit(); // Breathe ctx.DetachEntities <MediaFile>(deep: true); } } }
public virtual void DeleteDeliveryTime(DeliveryTime deliveryTime) { if (deliveryTime == null) { return; } // Remove associations to deleted products. using (var scope = new DbContextScope(_productRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false)) { var productsQuery = _productRepository.Table.Where(x => x.Deleted && x.DeliveryTimeId == deliveryTime.Id); var productsPager = new FastPager <Product>(productsQuery, 500); while (productsPager.ReadNextPage(out var products)) { if (products.Any()) { products.Each(x => x.DeliveryTimeId = null); scope.Commit(); } } var attributeCombinationQuery = from ac in _attributeCombinationRepository.Table join p in _productRepository.Table on ac.ProductId equals p.Id where p.Deleted && ac.DeliveryTimeId == deliveryTime.Id select ac; var attributeCombinationPager = new FastPager <ProductVariantAttributeCombination>(attributeCombinationQuery, 1000); while (attributeCombinationPager.ReadNextPage(out var attributeCombinations)) { if (attributeCombinations.Any()) { attributeCombinations.Each(x => x.DeliveryTimeId = null); scope.Commit(); } } } // Warn if there are associations to active products. if (IsAssociated(deliveryTime.Id)) { throw new SmartException(T("Admin.Configuration.DeliveryTimes.CannotDeleteAssignedProducts")); } _deliveryTimeRepository.Delete(deliveryTime); }
/// <summary> /// Prevents saving of delivery time if it's referenced in products or attribute combinations /// and removes associations to deleted products and attribute combinations. /// </summary> protected override async Task <HookResult> OnDeletingAsync(DeliveryTime entity, IHookedEntity entry, CancellationToken cancelToken) { // Remove associations to deleted products. var productsQuery = _db.Products.Where(x => x.Deleted && x.DeliveryTimeId == entity.Id); var productsPager = new FastPager <Product>(productsQuery, 500); while ((await productsPager.ReadNextPageAsync <Product>()).Out(out var products)) { if (products.Any()) { products.Each(x => x.DeliveryTimeId = null); } } var attributeCombinationQuery = from ac in _db.ProductVariantAttributeCombinations join p in _db.Products.AsNoTracking() on ac.ProductId equals p.Id where p.Deleted && ac.DeliveryTimeId == entity.Id select ac; var attributeCombinationPager = new FastPager <ProductVariantAttributeCombination>(attributeCombinationQuery, 1000); while ((await attributeCombinationPager.ReadNextPageAsync <ProductVariantAttributeCombination>()).Out(out var attributeCombinations)) { if (attributeCombinations.Any()) { attributeCombinations.Each(x => x.DeliveryTimeId = null); } } // Warn and throw if there are associations to active products or attribute combinations. var query = from x in _db.Products where x.DeliveryTimeId == entity.Id || x.ProductVariantAttributeCombinations.Any(c => c.DeliveryTimeId == entity.Id) select x.Id; if (await query.AnyAsync(cancellationToken: cancelToken)) { // Prohibit saving of associated entities. entry.State = Smartstore.Data.EntityState.Detached; throw new SmartException("The delivery time cannot be deleted. It has associated products or product variants."); } return(HookResult.Ok); }
// AJAX. public ActionResult AllProductAttributes(string label, int selectedId) { var query = _productAttributeService.GetAllProductAttributes(0, int.MaxValue).SourceQuery; var pager = new FastPager <ProductAttribute>(query, 500); var allAttributes = new List <dynamic>(); while (pager.ReadNextPage(out var attributes)) { foreach (var attribute in attributes) { dynamic obj = new { attribute.Id, attribute.DisplayOrder, Name = attribute.GetLocalized(x => x.Name).Value }; allAttributes.Add(obj); } } var data = allAttributes .OrderBy(x => x.DisplayOrder) .Select(x => new ChoiceListItem { Id = x.Id.ToString(), Text = x.Name, Selected = x.Id == selectedId }) .ToList(); if (label.HasValue()) { data.Insert(0, new ChoiceListItem { Id = "0", Text = label, Selected = false }); } return(new JsonResult { Data = data, JsonRequestBehavior = JsonRequestBehavior.AllowGet }); }
public void MigrateMediaFiles(SmartObjectContext ctx) { var query = ctx.Set <MediaFile>() //.Where(x => x.Version == 0) .Include(x => x.MediaStorage); var pager = new FastPager <MediaFile>(query, 1000); using (var scope = new DbContextScope(ctx, hooksEnabled: false, autoCommit: false, proxyCreation: false, validateOnSave: false, lazyLoading: false)) { while (pager.ReadNextPage(out var files)) { foreach (var file in files) { if (file.Version > 0) { continue; } if (file.Extension.IsEmpty()) { file.Extension = MimeTypes.MapMimeTypeToExtension(file.MimeType); } file.Name = file.Name + "." + file.Extension; file.CreatedOnUtc = file.UpdatedOnUtc; file.Version = 1; ProcessMediaFile(file); } // Save to DB int num = scope.Commit(); // Breathe ctx.DetachEntities <MediaFile>(deep: true); } } }
public override IEnumerable <NamedEntity> Enlist() { var pager = new FastPager <Product>(Query.AsNoTracking(), Context.MaximumNodeCount); while (pager.ReadNextPage(x => new { x.Id, x.UpdatedOnUtc }, x => x.Id, out var products)) { if (Context.CancellationToken.IsCancellationRequested) { break; } foreach (var x in products) { yield return(new NamedEntity { EntityName = "Product", Id = x.Id, LastMod = x.UpdatedOnUtc }); } } }
// AJAX. public ActionResult AllCustomerRoles(string label, string selectedIds, bool?includeSystemRoles) { var rolesQuery = _customerService.GetAllCustomerRoles(true).SourceQuery; if (!(includeSystemRoles ?? true)) { rolesQuery = rolesQuery.Where(x => !x.IsSystemRole); } var rolesPager = new FastPager <CustomerRole>(rolesQuery, 500); var customerRoles = new List <CustomerRole>(); var ids = selectedIds.ToIntArray(); while (rolesPager.ReadNextPage(out var roles)) { customerRoles.AddRange(roles); } var list = customerRoles .OrderBy(x => x.Name) .Select(x => new ChoiceListItem { Id = x.Id.ToString(), Text = x.Name, Selected = ids.Contains(x.Id) }) .ToList(); if (label.HasValue()) { list.Insert(0, new ChoiceListItem { Id = "0", Text = label, Selected = false }); } return(new JsonResult { Data = list, JsonRequestBehavior = JsonRequestBehavior.AllowGet }); }
/// <summary> /// Gets a list of all available customer roles. /// </summary> /// <param name="label">Text for optional entry. If not null an entry with the specified label text and the Id 0 will be added to the list.</param> /// <param name="selectedIds">Ids of selected entities.</param> /// <param name="includeSystemRoles">Specifies whether to include system roles.</param> /// <returns>List of all customer roles as JSON.</returns> public async Task <IActionResult> AllCustomerRoles(string label, string selectedIds, bool?includeSystemRoles) { var rolesQuery = _db.CustomerRoles .AsNoTracking() .ApplyStandardFilter(true).AsQueryable(); if (!(includeSystemRoles ?? true)) { rolesQuery = rolesQuery.Where(x => x.IsSystemRole); } var rolesPager = new FastPager <CustomerRole>(rolesQuery, 500); var customerRoles = new List <CustomerRole>(); var ids = selectedIds.ToIntArray(); while ((await rolesPager.ReadNextPageAsync <CustomerRole>()).Out(out var roles)) { customerRoles.AddRange(roles); } var list = customerRoles .OrderBy(x => x.Name) .Select(x => new ChoiceListItem { Id = x.Id.ToString(), Text = x.Name, Selected = ids.Contains(x.Id) }) .ToList(); if (label.HasValue()) { list.Insert(0, new ChoiceListItem { Id = "0", Text = label, Selected = false }); } return(new JsonResult(list)); }
public override IEnumerable <NamedEntity> Enlist() { // Enlist forum groups foreach (var group in _groups) { yield return(new NamedEntity { EntityName = nameof(ForumGroup), Id = group.Id, LastMod = group.UpdatedOnUtc }); } // Enlist forums foreach (var forum in _forums) { yield return(new NamedEntity { EntityName = nameof(Forum), Id = forum.Id, LastMod = forum.UpdatedOnUtc }); } // Enlist topics var pager = new FastPager <ForumTopic>(_topicsQuery.AsNoTracking(), _context.MaximumNodeCount); while (pager.ReadNextPage(x => new { x.Id, x.UpdatedOnUtc, x.Subject }, x => x.Id, out var topics)) { if (_context.CancellationToken.IsCancellationRequested) { break; } foreach (var x in topics) { yield return(new NamedEntity { EntityName = nameof(ForumTopic), Slug = (new ForumTopic { Subject = x.Subject }).GetSeName(), Id = x.Id, LastMod = x.UpdatedOnUtc }); } } }
// AJAX. public ActionResult AllNews(string selectedIds) { var query = _newsService.GetAllNews(0, 0, int.MaxValue, 0, true).SourceQuery; var pager = new FastPager <NewsItem>(query, 500); var allNewsItems = new List <dynamic>(); var ids = selectedIds.ToIntArray().ToList(); while (pager.ReadNextPage(out var newsItems)) { foreach (var newsItem in newsItems) { dynamic obj = new { newsItem.Id, newsItem.CreatedOnUtc, Title = newsItem.GetLocalized(x => x.Title).Value }; allNewsItems.Add(obj); } } var data = allNewsItems .OrderByDescending(x => x.CreatedOnUtc) .Select(x => new ChoiceListItem { Id = x.Id.ToString(), Text = x.Title, Selected = ids.Contains(x.Id) }) .ToList(); return(new JsonResult { Data = data, JsonRequestBehavior = JsonRequestBehavior.AllowGet }); }
public virtual bool Move(Provider <IMediaStorageProvider> sourceProvider, Provider <IMediaStorageProvider> targetProvider) { Guard.NotNull(sourceProvider, nameof(sourceProvider)); Guard.NotNull(targetProvider, nameof(targetProvider)); var success = false; var utcNow = DateTime.UtcNow; var context = new MediaMoverContext(sourceProvider.Metadata.SystemName, targetProvider.Metadata.SystemName); var source = sourceProvider.Value as ISupportsMediaMoving; var target = targetProvider.Value as ISupportsMediaMoving; // Source and target must support media storage moving if (source == null) { throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", sourceProvider.Metadata.SystemName)); } if (target == null) { throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", targetProvider.Metadata.SystemName)); } // Source and target provider must not be equal if (sourceProvider.Metadata.SystemName.IsCaseInsensitiveEqual(targetProvider.Metadata.SystemName)) { throw new ArgumentException(T("Admin.Media.CannotMoveToSameProvider")); } // We are about to process data in chunks but want to commit ALL at once after ALL chunks have been processed successfully. // AutoDetectChanges true required for newly inserted binary data. using (var scope = new DbContextScope(ctx: _services.DbContext, autoDetectChanges: true, proxyCreation: false, validateOnSave: false, autoCommit: false)) { using (var transaction = scope.DbContext.BeginTransaction()) { try { var pager = new FastPager <MediaFile>(_mediaFileRepo.Table, PAGE_SIZE); while (pager.ReadNextPage(out var files)) { foreach (var file in files) { // Move item from source to target source.MoveTo(target, context, file); file.UpdatedOnUtc = utcNow; ++context.MovedItems; } scope.DbContext.SaveChanges(); // Detach all entities from previous page to save memory scope.DbContext.DetachEntities(files, deep: true); } transaction.Commit(); success = true; } catch (Exception exception) { success = false; transaction.Rollback(); _services.Notifier.Error(exception); _logger.Error(exception); } } } if (success) { _services.Settings.SetSetting("Media.Storage.Provider", targetProvider.Metadata.SystemName); } // inform both provider about ending source.OnCompleted(context, success); target.OnCompleted(context, success); if (success && context.ShrinkDatabase) { _services.DbContext.ShrinkDatabase(); } return(success); }
public override async Task ExecuteAsync(TaskExecutionContext ctx) { var count = 0; var numDeleted = 0; var numAdded = 0; var roleQuery = _customerRoleRepository.TableUntracked.Expand(x => x.RuleSets); if (ctx.Parameters.ContainsKey("CustomerRoleIds")) { var roleIds = ctx.Parameters["CustomerRoleIds"].ToIntArray(); roleQuery = roleQuery.Where(x => roleIds.Contains(x.Id)); numDeleted = _customerRoleMappingRepository.Context.ExecuteSqlCommand( "Delete From [dbo].[CustomerRoleMapping] Where [CustomerRoleId] In ({0}) And [IsSystemMapping] = 1", false, null, string.Join(",", roleIds)); } else { numDeleted = _customerRoleMappingRepository.Context.ExecuteSqlCommand("Delete From [dbo].[CustomerRoleMapping] Where [IsSystemMapping] = 1"); } var roles = await roleQuery .Where(x => x.Active && x.RuleSets.Any(y => y.IsActive)) .ToListAsync(); using (var scope = new DbContextScope(ctx: _customerRoleMappingRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false)) { foreach (var role in roles) { var ruleSetCustomerIds = new HashSet <int>(); ctx.SetProgress(++count, roles.Count, $"Add customer assignments for role \"{role.SystemName.NaIfEmpty()}\"."); // Execute active rule sets and collect customer ids. foreach (var ruleSet in role.RuleSets.Where(x => x.IsActive)) { if (ctx.CancellationToken.IsCancellationRequested) { return; } if (_ruleFactory.CreateExpressionGroup(ruleSet, _targetGroupService) is FilterExpression expression) { var filterResult = _targetGroupService.ProcessFilter(expression, 0, 500); var resultPager = new FastPager <Customer>(filterResult.SourceQuery, 500); while (true) { var customerIds = await resultPager.ReadNextPageAsync(x => x.Id, x => x); if (!(customerIds?.Any() ?? false)) { break; } ruleSetCustomerIds.AddRange(customerIds); } } } // Add mappings. if (ruleSetCustomerIds.Any()) { foreach (var chunk in ruleSetCustomerIds.Slice(500)) { if (ctx.CancellationToken.IsCancellationRequested) { return; } foreach (var customerId in chunk) { _customerRoleMappingRepository.Insert(new CustomerRoleMapping { CustomerId = customerId, CustomerRoleId = role.Id, IsSystemMapping = true }); ++numAdded; } await scope.CommitAsync(); } try { _customerRoleMappingRepository.Context.DetachEntities <CustomerRoleMapping>(); } catch { } } } } if (numAdded > 0 || numDeleted > 0) { _cacheManager.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN); } Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} customer assignments for {roles.Count} roles."); }
public void MigrateDownloads(SmartObjectContext ctx) { var sql = "SELECT * FROM [Download] WHERE [MediaFileId] IS NULL AND [UseDownloadUrl] = 0"; var downloadStubs = ctx.SqlQuery <DownloadStub>(sql).ToDictionary(x => x.Id); var downloadsFolderId = _albumRegistry.GetAlbumByName(SystemAlbumProvider.Downloads)?.Id; var messagesFolderId = _albumRegistry.GetAlbumByName(SystemAlbumProvider.Messages)?.Id; var newFiles = new List <MediaFile>(); using (var scope = new DbContextScope(ctx, validateOnSave: false, hooksEnabled: false, autoCommit: false, autoDetectChanges: false)) { var messageTemplates = ctx.Set <MessageTemplate>() .Where(x => x.Attachment1FileId.HasValue || x.Attachment2FileId.HasValue || x.Attachment3FileId.HasValue) .ToList(); // Key = Download.Id var messageTemplatesDict = new Dictionary <int, MessageTemplate>(); foreach (var mt in messageTemplates) { if (mt.Attachment1FileId.HasValue) { messageTemplatesDict[mt.Attachment1FileId.Value] = mt; } if (mt.Attachment2FileId.HasValue) { messageTemplatesDict[mt.Attachment2FileId.Value] = mt; } if (mt.Attachment3FileId.HasValue) { messageTemplatesDict[mt.Attachment3FileId.Value] = mt; } } var hasPostProcessor = _isFsProvider || messageTemplatesDict.Count > 0; var query = ctx.Set <Download>().Where(x => x.MediaFileId == null); var pager = new FastPager <Download>(query, 1000); while (pager.ReadNextPage(out var downloads)) { foreach (var d in downloads) { var stub = downloadStubs.Get(d.Id); if (stub == null) { continue; } if (stub.UseDownloadUrl || string.IsNullOrEmpty(stub.Extension)) { // Something weird has happened in the past continue; } if (stub.Filename == "undefined" || string.IsNullOrEmpty(stub.Filename)) { stub.Filename = stub.Id.ToString(CultureInfo.InvariantCulture); } var isMailAttachment = false; if (messageTemplatesDict.TryGetValue(stub.Id, out var mt)) { isMailAttachment = true; } // Create and insert new MediaFile entity for the download var file = new MediaFile { CreatedOnUtc = stub.UpdatedOnUtc, UpdatedOnUtc = stub.UpdatedOnUtc, Extension = stub.Extension.TrimStart('.'), Name = stub.Filename.Truncate(292), // Extension appended later in MigrateFiles() MimeType = stub.ContentType, MediaType = MediaType.Image, // Resolved later in MigrateFiles() FolderId = isMailAttachment ? messagesFolderId : downloadsFolderId, IsTransient = stub.IsTransient, MediaStorageId = stub.MediaStorageId, Version = 0 // Ensure that this record gets processed by MigrateFiles() }; // Assign new file to download d.MediaFile = file; // To be able to move files later if (hasPostProcessor) { newFiles.Add(file); } } // Save to DB int num = scope.Commit(); if (hasPostProcessor) { var downloadsDict = downloads.ToDictionary(x => x.Id); if (_isFsProvider) { // Copy files from "Media/Downloads" to "Media/Storage" folder MoveDownloadFiles(newFiles.ToDictionary(x => x.Id), downloadsDict, downloadStubs); } // MessageTemplate attachments (Download > MediaFile) if (messageTemplatesDict.Count > 0) { ReRefMessageTemplateAttachments(ctx, messageTemplatesDict, downloadsDict); } newFiles.Clear(); } // Breathe ctx.DetachEntities <MessageTemplate>(); ctx.DetachEntities <Download>(deep: true); } } }
public virtual async Task <bool> MoveAsync( Provider <IMediaStorageProvider> sourceProvider, Provider <IMediaStorageProvider> targetProvider, CancellationToken cancelToken = default) { Guard.NotNull(sourceProvider, nameof(sourceProvider)); Guard.NotNull(targetProvider, nameof(targetProvider)); // Source must support sending if (sourceProvider.Value is not IMediaSender sender) { throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", sourceProvider.Metadata.SystemName)); } // Target must support receiving if (targetProvider.Value is not IMediaReceiver receiver) { throw new ArgumentException(T("Admin.Media.StorageMovingNotSupported", targetProvider.Metadata.SystemName)); } // Source and target provider must not be equal if (sender == receiver) { throw new ArgumentException(T("Admin.Media.CannotMoveToSameProvider")); } var success = false; var utcNow = DateTime.UtcNow; var context = new MediaMoverContext(sender, receiver); // We are about to process data in chunks but want to commit ALL at once after ALL chunks have been processed successfully. // AutoDetectChanges true required for newly inserted binary data. using (var scope = new DbContextScope(ctx: _db, autoDetectChanges: true, retainConnection: true)) { using (var transaction = await _db.Database.BeginTransactionAsync(cancelToken)) { try { var pager = new FastPager <MediaFile>(_db.MediaFiles, PAGE_SIZE); while ((await pager.ReadNextPageAsync <MediaFile>()).Out(out var files)) { if (cancelToken.IsCancellationRequested) { break; } foreach (var file in files) { // Move item from source to target await sender.MoveToAsync(receiver, context, file); file.UpdatedOnUtc = utcNow; ++context.MovedItems; } if (!cancelToken.IsCancellationRequested) { await _db.SaveChangesAsync(cancelToken); // Detach all entities from previous page to save memory scope.DbContext.DetachEntities(files, deep: true); } } if (!cancelToken.IsCancellationRequested) { await transaction.CommitAsync(cancelToken); success = true; } else { success = false; await transaction.RollbackAsync(CancellationToken.None); } } catch (Exception exception) { success = false; await transaction.RollbackAsync(cancelToken); _notifier.Error(exception); Logger.Error(exception); } } } if (success) { await _settingService.ApplySettingAsync("Media.Storage.Provider", targetProvider.Metadata.SystemName); await _db.SaveChangesAsync(cancelToken); } // Inform both provider about ending await sender.OnCompletedAsync(context, success, cancelToken); await receiver.OnCompletedAsync(context, success, cancelToken); return(success); }
public override async Task ExecuteAsync(TaskExecutionContext ctx) { var count = 0; var clearCache = false; var roleQuery = _customerRoleRepository.TableUntracked.Expand(x => x.RuleSets); if (ctx.Parameters.ContainsKey("CustomerRoleIds")) { var rolesIds = ctx.Parameters["CustomerRoleIds"].ToIntArray(); roleQuery = roleQuery.Where(x => rolesIds.Contains(x.Id)); } var roles = await roleQuery.ToListAsync(); if (!roles.Any()) { return; } using (var scope = new DbContextScope(ctx: _customerRoleMappingRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false)) foreach (var role in roles) { try { ctx.SetProgress(++count, roles.Count, $"Sync customer assignments for role {role.SystemName.NaIfEmpty()}."); _customerRoleMappingRepository.Context.DetachEntities(x => x is CustomerRoleMapping); } catch { } var ruleSetCustomerIds = new HashSet <int>(); var existingCustomerIds = new HashSet <int>(); var numDeleted = 0; var numAdded = 0; // Execute active rule sets and collect customer ids. // Delete old mappings if the role is inactive or has no assigned rule sets. if (role.Active) { foreach (var ruleSet in role.RuleSets.Where(x => x.IsActive)) { if (ctx.CancellationToken.IsCancellationRequested) { return; } var expression = _ruleFactory.CreateExpressionGroup(ruleSet, _targetGroupService) as FilterExpression; if (expression != null) { var filterResult = _targetGroupService.ProcessFilter(expression, 0, 500); var resultPager = new FastPager <Customer>(filterResult.SourceQuery, 500); for (var i = 0; i < 9999999; ++i) { var customerIds = await resultPager.ReadNextPageAsync(x => x.Id, x => x); if (!(customerIds?.Any() ?? false)) { break; } ruleSetCustomerIds.AddRange(customerIds); } } } } // Sync mappings. var query = _customerRoleMappingRepository.Table.Where(x => x.CustomerRoleId == role.Id && x.IsSystemMapping); var pager = new FastPager <CustomerRoleMapping>(query, 500); // Mappings to delete. for (var i = 0; i < 9999999; ++i) { if (ctx.CancellationToken.IsCancellationRequested) { return; } var mappings = await pager.ReadNextPageAsync <CustomerRoleMapping>(); if (!(mappings?.Any() ?? false)) { break; } foreach (var mapping in mappings) { if (!role.Active || !ruleSetCustomerIds.Contains(mapping.CustomerId)) { _customerRoleMappingRepository.Delete(mapping); ++numDeleted; clearCache = true; } else { existingCustomerIds.Add(mapping.CustomerId); } } await scope.CommitAsync(); } // Mappings to add. if (role.Active) { var toAdd = ruleSetCustomerIds.Except(existingCustomerIds).ToList(); if (toAdd.Any()) { foreach (var chunk in toAdd.Slice(500)) { if (ctx.CancellationToken.IsCancellationRequested) { return; } foreach (var customerId in chunk) { _customerRoleMappingRepository.Insert(new CustomerRoleMapping { CustomerId = customerId, CustomerRoleId = role.Id, IsSystemMapping = true }); ++numAdded; clearCache = true; } await scope.CommitAsync(); } } } Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Customer assignments for {role.SystemName.NaIfEmpty()}: deleted {numDeleted}, added {numAdded}."); } if (clearCache) { _cacheManager.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN); } }
public async Task Run(TaskExecutionContext ctx, CancellationToken cancelToken = default) { //var count = 0; var numDeleted = 0; var numAdded = 0; var rolesCount = 0; using (var scope = new DbContextScope(_db, autoDetectChanges: false, hooksEnabled: false, deferCommit: true)) { // Delete existing system mappings. var deleteQuery = _db.CustomerRoleMappings.Where(x => x.IsSystemMapping); if (ctx.Parameters.ContainsKey("CustomerRoleIds")) { var roleIds = ctx.Parameters["CustomerRoleIds"].ToIntArray(); deleteQuery = deleteQuery.Where(x => roleIds.Contains(x.CustomerRoleId)); } numDeleted = await deleteQuery.BatchDeleteAsync(cancelToken); // Insert new customer role mappings. var roles = await _db.CustomerRoles .Include(x => x.RuleSets) .AsNoTracking() .Where(x => x.Active && x.RuleSets.Any(y => y.IsActive)) .ToListAsync(cancelToken); rolesCount = roles.Count; foreach (var role in roles) { var ruleSetCustomerIds = new HashSet <int>(); // TODO: (mg) (core) Complete TargetGroupEvaluatorTask (TaskExecutionContext required). //ctx.SetProgress(++count, roles.Count, $"Add customer assignments for role \"{role.SystemName.NaIfEmpty()}\"."); // Execute active rule sets and collect customer ids. foreach (var ruleSet in role.RuleSets.Where(x => x.IsActive)) { if (cancelToken.IsCancellationRequested) { return; } var expressionGroup = await _ruleService.CreateExpressionGroupAsync(ruleSet, _targetGroupService); if (expressionGroup is FilterExpression expression) { var filterResult = _targetGroupService.ProcessFilter(expression, 0, 500); var resultPager = new FastPager <Customer>(filterResult.SourceQuery, 500); while ((await resultPager.ReadNextPageAsync(x => x.Id, x => x)).Out(out var customerIds)) { ruleSetCustomerIds.AddRange(customerIds); } } } // Add mappings. if (ruleSetCustomerIds.Any()) { foreach (var chunk in ruleSetCustomerIds.Slice(500)) { if (cancelToken.IsCancellationRequested) { return; } foreach (var customerId in chunk) { _db.CustomerRoleMappings.Add(new CustomerRoleMapping { CustomerId = customerId, CustomerRoleId = role.Id, IsSystemMapping = true }); ++numAdded; } await scope.CommitAsync(cancelToken); } try { scope.DbContext.DetachEntities <CustomerRoleMapping>(); } catch { } } } } if (numAdded > 0 || numDeleted > 0) { await _cache.RemoveByPatternAsync(AclService.ACL_SEGMENT_PATTERN); } Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} customer assignments for {rolesCount} roles."); }
public virtual void InstallPermissions(IPermissionProvider[] permissionProviders, bool removeUnusedPermissions = false) { if (!(permissionProviders?.Any() ?? false)) { return; } Dictionary <string, CustomerRole> existingRoles = null; var allPermissionNames = _permissionRepository.TableUntracked.Select(x => x.SystemName).ToList(); var existing = new HashSet <string>(allPermissionNames, StringComparer.InvariantCultureIgnoreCase); var added = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase); var providerPermissions = new HashSet <string>(StringComparer.InvariantCultureIgnoreCase); var log = existing.Any(); var clearCache = false; if (existing.Any()) { var permissionsMigrated = existing.Contains(Permissions.System.AccessShop) && !existing.Contains("PublicStoreAllowNavigation"); if (!permissionsMigrated) { // Migrations must have been completed before permissions can be added or deleted. return; } } try { using (var scope = new DbContextScope(_permissionRepository.Context, validateOnSave: false, hooksEnabled: false, autoCommit: false)) { // Add new permissions. foreach (var provider in permissionProviders) { try { var systemNames = provider.GetPermissions().Select(x => x.SystemName); var missingSystemNames = systemNames.Except(existing); if (removeUnusedPermissions) { providerPermissions.AddRange(systemNames); } if (missingSystemNames.Any()) { var defaultPermissions = provider.GetDefaultPermissions(); foreach (var systemName in missingSystemNames) { var roleNames = defaultPermissions .Where(x => x.PermissionRecords.Any(y => y.SystemName == systemName)) .Select(x => x.CustomerRoleSystemName); var newPermission = new PermissionRecord { SystemName = systemName }; foreach (var roleName in new HashSet <string>(roleNames, StringComparer.InvariantCultureIgnoreCase)) { if (existingRoles == null) { existingRoles = new Dictionary <string, CustomerRole>(); var rolesQuery = _customerService.Value.GetAllCustomerRoles(true).SourceQuery; rolesQuery = rolesQuery.Where(x => !string.IsNullOrEmpty(x.SystemName)); var rolesPager = new FastPager <CustomerRole>(rolesQuery, 500); while (rolesPager.ReadNextPage(out var roles)) { roles.Each(x => existingRoles[x.SystemName] = x); } } if (!existingRoles.TryGetValue(roleName, out var role)) { role = new CustomerRole { Active = true, Name = roleName, SystemName = roleName }; _customerService.Value.InsertCustomerRole(role); scope.Commit(); existingRoles[roleName] = role; } newPermission.PermissionRoleMappings.Add(new PermissionRoleMapping { Allow = true, CustomerRoleId = role.Id }); } _permissionRepository.Insert(newPermission); clearCache = true; added.Add(newPermission.SystemName); existing.Add(newPermission.SystemName); } } } catch (Exception ex) { Logger.Error(ex); } } scope.Commit(); if (log && added.Any()) { Logger.Info(T("Admin.Permissions.AddedPermissions", string.Join(", ", added))); } // Remove permissions no longer supported by providers. if (removeUnusedPermissions) { var toDelete = existing.Except(providerPermissions).ToList(); if (toDelete.Any()) { clearCache = true; foreach (var chunk in toDelete.Slice(500)) { var entities = _permissionRepository.Table.Where(x => chunk.Contains(x.SystemName)).ToList(); entities.Each(x => _permissionRepository.Delete(x)); scope.Commit(); } if (log) { Logger.Info(T("Admin.Permissions.RemovedPermissions", string.Join(", ", toDelete))); } } } } } finally { if (clearCache) { _cacheManager.RemoveByPattern(PERMISSION_TREE_PATTERN_KEY); } } }
public virtual void InheritAclIntoChildren( int categoryId, bool touchProductsWithMultipleCategories = false, bool touchExistingAcls = false, bool categoriesOnly = false) { var allCustomerRoles = _customerService.GetAllCustomerRoles(true); var referenceCategory = GetCategoryById(categoryId); var referenceRoleIds = _aclService.GetCustomerRoleIdsWithAccessTo(referenceCategory); using (var scope = new DbContextScope(ctx: _aclRepository.Context, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false)) { ProcessCategory(scope, referenceCategory); } _cache.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN); void ProcessCategory(DbContextScope scope, Category c) { // Process sub-categories. var subCategories = GetAllCategoriesByParentCategoryId(c.Id, true); foreach (var subCategory in subCategories) { if (subCategory.SubjectToAcl != referenceCategory.SubjectToAcl) { subCategory.SubjectToAcl = referenceCategory.SubjectToAcl; _categoryRepository.Update(subCategory); } var existingAclRecords = _aclService.GetAclRecords(subCategory).ToDictionarySafe(x => x.CustomerRoleId); foreach (var role in allCustomerRoles) { if (referenceRoleIds.Contains(role.Id)) { if (!existingAclRecords.ContainsKey(role.Id)) { _aclRepository.Insert(new AclRecord { CustomerRoleId = role.Id, EntityId = subCategory.Id, EntityName = "Category" }); } } else if (existingAclRecords.TryGetValue(role.Id, out var aclRecordToDelete)) { _aclRepository.Delete(aclRecordToDelete); } } } scope.Commit(); // Process products. var categoryIds = new HashSet <int>(subCategories.Select(x => x.Id)); categoryIds.Add(c.Id); var searchQuery = new CatalogSearchQuery().WithCategoryIds(null, categoryIds.ToArray()); var productsQuery = _catalogSearchService.PrepareQuery(searchQuery); var productsPager = new FastPager <Product>(productsQuery, 500); while (productsPager.ReadNextPage(out var products)) { foreach (var product in products) { if (product.SubjectToAcl != referenceCategory.SubjectToAcl) { product.SubjectToAcl = referenceCategory.SubjectToAcl; _productRepository.Update(product); } var existingAclRecords = _aclService.GetAclRecords(product).ToDictionarySafe(x => x.CustomerRoleId); foreach (var role in allCustomerRoles) { if (referenceRoleIds.Contains(role.Id)) { if (!existingAclRecords.ContainsKey(role.Id)) { _aclRepository.Insert(new AclRecord { CustomerRoleId = role.Id, EntityId = product.Id, EntityName = "Product" }); } } else if (existingAclRecords.TryGetValue(role.Id, out var aclRecordToDelete)) { _aclRepository.Delete(aclRecordToDelete); } } } scope.Commit(); } scope.Commit(); try { scope.DbContext.DetachEntities(x => x is Product || x is Category || x is AclRecord, false); } catch { } foreach (var subCategory in subCategories) { ProcessCategory(scope, subCategory); } } }
public virtual void InheritStoresIntoChildren( int categoryId, bool touchProductsWithMultipleCategories = false, bool touchExistingAcls = false, bool categoriesOnly = false) { var allStores = _storeService.GetAllStores(); var referenceCategory = GetCategoryById(categoryId); var referenceStoreMappings = _storeMappingService.GetStoresIdsWithAccess(referenceCategory); using (var scope = new DbContextScope(ctx: _storeMappingRepository.Context, autoDetectChanges: false, proxyCreation: false, validateOnSave: false, autoCommit: false)) { ProcessCategory(scope, referenceCategory); } _cache.RemoveByPattern(StoreMappingService.STOREMAPPING_SEGMENT_PATTERN); void ProcessCategory(DbContextScope scope, Category c) { // Process sub-categories. var subCategories = GetAllCategoriesByParentCategoryId(c.Id, true); foreach (var subCategory in subCategories) { if (subCategory.LimitedToStores != referenceCategory.LimitedToStores) { subCategory.LimitedToStores = referenceCategory.LimitedToStores; _categoryRepository.Update(subCategory); } var existingStoreMappingsRecords = _storeMappingService.GetStoreMappings(subCategory).ToDictionary(x => x.StoreId); foreach (var store in allStores) { if (referenceStoreMappings.Contains(store.Id)) { if (!existingStoreMappingsRecords.ContainsKey(store.Id)) { _storeMappingRepository.Insert(new StoreMapping { StoreId = store.Id, EntityId = subCategory.Id, EntityName = "Category" }); } } else if (existingStoreMappingsRecords.TryGetValue(store.Id, out var storeMappingToDelete)) { _storeMappingRepository.Delete(storeMappingToDelete); } } } scope.Commit(); // Process products. var categoryIds = new HashSet <int>(subCategories.Select(x => x.Id)); categoryIds.Add(c.Id); var searchQuery = new CatalogSearchQuery().WithCategoryIds(null, categoryIds.ToArray()); var productsQuery = _catalogSearchService.PrepareQuery(searchQuery); var productsPager = new FastPager <Product>(productsQuery, 500); while (productsPager.ReadNextPage(out var products)) { foreach (var product in products) { if (product.LimitedToStores != referenceCategory.LimitedToStores) { product.LimitedToStores = referenceCategory.LimitedToStores; _productRepository.Update(product); } var existingStoreMappingsRecords = _storeMappingService.GetStoreMappings(product).ToDictionary(x => x.StoreId); foreach (var store in allStores) { if (referenceStoreMappings.Contains(store.Id)) { if (!existingStoreMappingsRecords.ContainsKey(store.Id)) { _storeMappingRepository.Insert(new StoreMapping { StoreId = store.Id, EntityId = product.Id, EntityName = "Product" }); } } else if (existingStoreMappingsRecords.TryGetValue(store.Id, out var storeMappingToDelete)) { _storeMappingRepository.Delete(storeMappingToDelete); } } } scope.Commit(); } scope.Commit(); try { scope.DbContext.DetachEntities(x => x is Product || x is Category || x is StoreMapping, false); } catch { } foreach (var subCategory in subCategories) { ProcessCategory(scope, subCategory); } } }
public virtual async Task <(int AffectedCategories, int AffectedProducts)> InheritStoresIntoChildrenAsync( int categoryId, bool touchProductsWithMultipleCategories = false, bool touchExistingAcls = false, bool categoriesOnly = false) { var categoryEntityName = nameof(Category); var productEntityName = nameof(Product); var affectedCategories = 0; var affectedProducts = 0; var allStoreIds = await _db.Stores .AsQueryable() .Select(x => x.Id) .ToListAsync(); var referenceCategory = await _db.Categories.FindByIdAsync(categoryId); var referenceStoreMappingIds = await _storeMappingService.GetAuthorizedStoreIdsAsync(referenceCategory); using (var scope = new DbContextScope(_db, autoDetectChanges: false)) { await ProcessCategory(scope, referenceCategory); } _cache.RemoveByPattern(StoreMappingService.STOREMAPPING_SEGMENT_PATTERN); return(affectedCategories, affectedProducts); async Task ProcessCategory(DbContextScope scope, Category c) { // Process sub-categories. var subCategories = await _db.Categories .AsQueryable() .Where(x => x.ParentCategoryId == c.Id) .OrderBy(x => x.DisplayOrder) .ToListAsync(); foreach (var subCategory in subCategories) { if (subCategory.LimitedToStores != referenceCategory.LimitedToStores) { subCategory.LimitedToStores = referenceCategory.LimitedToStores; } var storeMappings = await _db.StoreMappings .ApplyEntityFilter(subCategory) .ToListAsync(); var storeMappingsDic = storeMappings.ToDictionarySafe(x => x.StoreId); foreach (var storeId in allStoreIds) { if (referenceStoreMappingIds.Contains(storeId)) { if (!storeMappingsDic.ContainsKey(storeId)) { _db.StoreMappings.Add(new StoreMapping { StoreId = storeId, EntityId = subCategory.Id, EntityName = categoryEntityName }); } } else if (storeMappingsDic.TryGetValue(storeId, out var storeMappingToDelete)) { _db.StoreMappings.Remove(storeMappingToDelete); } } } await scope.CommitAsync(); affectedCategories += subCategories.Count; // Process products. var categoryIds = new HashSet <int>(subCategories.Select(x => x.Id)) { c.Id }; var searchQuery = new CatalogSearchQuery().WithCategoryIds(null, categoryIds.ToArray()); var productsQuery = _catalogSearchService.PrepareQuery(searchQuery); var productsPager = new FastPager <Product>(productsQuery, 500); while ((await productsPager.ReadNextPageAsync <Product>()).Out(out var products)) { foreach (var product in products) { if (product.LimitedToStores != referenceCategory.LimitedToStores) { product.LimitedToStores = referenceCategory.LimitedToStores; } var storeMappings = await _db.StoreMappings .ApplyEntityFilter(product) .ToListAsync(); var storeMappingsDic = storeMappings.ToDictionarySafe(x => x.StoreId); foreach (var storeId in allStoreIds) { if (referenceStoreMappingIds.Contains(storeId)) { if (!storeMappingsDic.ContainsKey(storeId)) { _db.StoreMappings.Add(new StoreMapping { StoreId = storeId, EntityId = product.Id, EntityName = productEntityName }); } } else if (storeMappingsDic.TryGetValue(storeId, out var storeMappingToDelete)) { _db.StoreMappings.Remove(storeMappingToDelete); } } } await scope.CommitAsync(); affectedProducts += products.Count; } await scope.CommitAsync(); try { scope.DbContext.DetachEntities(x => x is Product || x is Category || x is StoreMapping, false); } catch { } foreach (var subCategory in subCategories) { await ProcessCategory(scope, subCategory); } } }
public IEnumerable <MediaTrackAction> DetectAllTracks(string albumName) { var ctx = _dbContext; var entityName = string.Empty; // TODO: Messages, Forums (?), Store (?) // Products if (albumName == Products) { // Products { var name = nameof(ProductMediaFile); var p = new FastPager <ProductMediaFile>(ctx.Set <ProductMediaFile>().AsNoTracking()); while (p.ReadNextPage(x => new { x.Id, x.ProductId, x.MediaFileId }, x => x.Id, out var list)) { foreach (var x in list) { yield return(new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId }); } } } // ProductAttributeOption { var name = nameof(ProductAttributeOption); var p = new FastPager <ProductAttributeOption>(ctx.Set <ProductAttributeOption>().AsNoTracking().Where(x => x.MediaFileId > 0)); while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) { foreach (var x in list) { yield return(new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId }); } } } // ProductVariantAttributeValue { var name = nameof(ProductVariantAttributeValue); var p = new FastPager <ProductVariantAttributeValue>(ctx.Set <ProductVariantAttributeValue>().AsNoTracking().Where(x => x.MediaFileId > 0)); while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) { foreach (var x in list) { yield return(new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId }); } } } // SpecificationAttributeOption { var name = nameof(SpecificationAttributeOption); var p = new FastPager <SpecificationAttributeOption>(ctx.Set <SpecificationAttributeOption>().AsNoTracking().Where(x => x.MediaFileId > 0)); while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) { foreach (var x in list) { yield return(new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId }); } } } yield break; } // Categories if (albumName == Categories) { var name = nameof(Category); var p = new FastPager <Category>(ctx.Set <Category>().AsNoTracking().Where(x => x.MediaFileId.HasValue)); while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) { foreach (var x in list) { yield return(new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value }); } } yield break; } // Brands if (albumName == Brands) { var name = nameof(Manufacturer); var p = new FastPager <Manufacturer>(ctx.Set <Manufacturer>().AsNoTracking().Where(x => x.MediaFileId.HasValue)); while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) { foreach (var x in list) { yield return(new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value }); } } yield break; } // BlogPost if (albumName == Blog) { var name = nameof(BlogPost); var p = new FastPager <BlogPost>(ctx.Set <BlogPost>().AsNoTracking().Where(x => x.MediaFileId.HasValue || x.PreviewMediaFileId.HasValue)); while (p.ReadNextPage(x => new { x.Id, x.MediaFileId, x.PreviewMediaFileId }, x => x.Id, out var list)) { foreach (var x in list) { if (x.MediaFileId.HasValue) { yield return new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value } } ; if (x.PreviewMediaFileId.HasValue) { yield return new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.PreviewMediaFileId.Value } } ; } } yield break; } // NewsItem if (albumName == News) { var name = nameof(NewsItem); var p = new FastPager <NewsItem>(ctx.Set <NewsItem>().AsNoTracking().Where(x => x.MediaFileId.HasValue || x.PreviewMediaFileId.HasValue)); while (p.ReadNextPage(x => new { x.Id, x.MediaFileId, x.PreviewMediaFileId }, x => x.Id, out var list)) { foreach (var x in list) { if (x.MediaFileId.HasValue) { yield return new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value } } ; if (x.PreviewMediaFileId.HasValue) { yield return new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.PreviewMediaFileId.Value } } ; } } yield break; } // Customer if (albumName == Customers) { var name = nameof(Customers); // Avatars var p = new FastPager <GenericAttribute>(ctx.Set <GenericAttribute>().AsNoTracking() .Where(x => x.KeyGroup == nameof(Customer) && x.Key == SystemCustomerAttributeNames.AvatarPictureId)); while (p.ReadNextPage(x => new { x.Id, x.EntityId, x.Value }, x => x.Id, out var list)) { foreach (var x in list) { var id = x.Value.ToInt(); if (id > 0) { yield return(new MediaTrackAction { EntityId = x.EntityId, EntityName = name, MediaFileId = id }); } } } yield break; } // Downloads if (albumName == Downloads) { var name = nameof(Download); var p = new FastPager <Download>(ctx.Set <Download>().AsNoTracking().Where(x => x.MediaFileId.HasValue)); while (p.ReadNextPage(x => new { x.Id, x.MediaFileId }, x => x.Id, out var list)) { foreach (var x in list) { yield return(new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.MediaFileId.Value }); } } yield break; } // Messages if (albumName == Messages) { var name = nameof(MessageTemplate); var p = new FastPager <MessageTemplate>(ctx.Set <MessageTemplate>().AsNoTracking()); while (p.ReadNextPage(x => new { x.Id, x.Attachment1FileId, x.Attachment2FileId, x.Attachment3FileId }, x => x.Id, out var list)) { foreach (var x in list) { if (x.Attachment1FileId.HasValue) { yield return new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.Attachment1FileId.Value } } ; if (x.Attachment2FileId.HasValue) { yield return new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.Attachment2FileId.Value } } ; if (x.Attachment3FileId.HasValue) { yield return new MediaTrackAction { EntityId = x.Id, EntityName = name, MediaFileId = x.Attachment3FileId.Value } } ; } } yield break; } } #endregion } }
public override async Task ExecuteAsync(TaskExecutionContext ctx) { var count = 0; var numDeleted = 0; var numAdded = 0; var numCategories = 0; var pageSize = 500; var pageIndex = -1; var categoryIds = ctx.Parameters.ContainsKey("CategoryIds") ? ctx.Parameters["CategoryIds"].ToIntArray() : null; // Hooks are enabled because search index needs to be updated. using (var scope = new DbContextScope(ctx: _productCategoryRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: true, autoCommit: false)) { // Delete existing system mappings. var deleteQuery = _productCategoryRepository.Table.Where(x => x.IsSystemMapping); if (categoryIds != null) { deleteQuery = deleteQuery.Where(x => categoryIds.Contains(x.CategoryId)); } var pager = new FastPager <ProductCategory>(deleteQuery, pageSize); while (pager.ReadNextPage(out var mappings)) { if (ctx.CancellationToken.IsCancellationRequested) { return; } if (mappings.Any()) { _productCategoryRepository.DeleteRange(mappings); numDeleted += await scope.CommitAsync(); } } try { _productCategoryRepository.Context.DetachEntities <ProductCategory>(); } catch { } // Insert new product category mappings. var categoryQuery = _categoryRepository.TableUntracked.Expand(x => x.RuleSets); if (categoryIds != null) { categoryQuery = categoryQuery.Where(x => categoryIds.Contains(x.Id)); } var categories = await categoryQuery .Where(x => x.Published && !x.Deleted && x.RuleSets.Any(y => y.IsActive)) .ToListAsync(); numCategories = categories.Count; foreach (var category in categories) { var ruleSetProductIds = new HashSet <int>(); ctx.SetProgress(++count, categories.Count, $"Add product mappings for category \"{category.Name.NaIfEmpty()}\"."); // Execute active rule sets and collect product ids. foreach (var ruleSet in category.RuleSets.Where(x => x.IsActive)) { if (ctx.CancellationToken.IsCancellationRequested) { return; } if (_ruleFactory.CreateExpressionGroup(ruleSet, _productRuleProvider) is SearchFilterExpression expression) { pageIndex = -1; while (true) { // Do not touch searchResult.Hits. We only need the product identifiers. var searchResult = _productRuleProvider.Search(new SearchFilterExpression[] { expression }, ++pageIndex, pageSize); ruleSetProductIds.AddRange(searchResult.HitsEntityIds); if (pageIndex >= (searchResult.TotalHitsCount / pageSize)) { break; } } } } // Add mappings. if (ruleSetProductIds.Any()) { foreach (var chunk in ruleSetProductIds.Slice(500)) { if (ctx.CancellationToken.IsCancellationRequested) { return; } foreach (var productId in chunk) { _productCategoryRepository.Insert(new ProductCategory { ProductId = productId, CategoryId = category.Id, IsSystemMapping = true }); ++numAdded; } await scope.CommitAsync(); } try { _productCategoryRepository.Context.DetachEntities <ProductCategory>(); } catch { } } } } Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} product mappings for {numCategories} categories."); }