Пример #1
0
        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);
        }
Пример #2
0
        /// <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);
        }
        /// <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 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.");
        }
Пример #5
0
        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);
        }
Пример #6
0
        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 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 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.");
        }