context: using System;
  using System.Collections.Generic;
  using System.Linq;
  using Nop.Core;
  using Nop.Core.Caching;
  using Nop.Core.Data;
  using Nop.Core.Domain.Catalog;
  using Nop.Core.Domain.Security;
  using Nop.Core.Domain.Stores;
  using Nop.Services.Customers;
  using Nop.Services.Events;
  using Nop.Services.Security;
  using Nop.Services.Stores;
  
  namespace Nop.Services.Catalog
  {
      /// <summary>
      /// Category service
      /// </summary>
      public partial class CategoryService : ICategoryService
      {
          #region Constants
          /// <summary>
          /// Key for caching
          /// </summary>
          /// <remarks>
          /// {0} : category ID
          /// </remarks>
          private const string CATEGORIES_BY_ID_KEY = "Nop.category.id-{0}";
          /// <summary>
          /// Key for caching
          /// </summary>
          /// <remarks>
          /// {0} : parent category ID
          /// {1} : show hidden records?
          /// {2} : current customer ID
          /// {3} : store ID
          /// {4} : include all levels (child)
          /// </remarks>
          private const string CATEGORIES_BY_PARENT_CATEGORY_ID_KEY = "Nop.category.byparent-{0}-{1}-{2}-{3}-{4}";
          /// <summary>
          /// Key for caching
          /// </summary>
          /// <remarks>
          /// {0} : show hidden records?
          /// {1} : category ID
          /// {2} : page index
          /// {3} : page size
          /// {4} : current customer ID
          /// {5} : store ID
          /// </remarks>
          private const string PRODUCTCATEGORIES_ALLBYCATEGORYID_KEY = "Nop.productcategory.allbycategoryid-{0}-{1}-{2}-{3}-{4}-{5}";
          /// <summary>
          /// Key for caching
          /// </summary>
          /// <remarks>
          /// {0} : show hidden records?
          /// {1} : product ID
          /// {2} : current customer ID
          /// {3} : store ID
          /// </remarks>
          private const string PRODUCTCATEGORIES_ALLBYPRODUCTID_KEY = "Nop.productcategory.allbyproductid-{0}-{1}-{2}-{3}";
          /// <summary>
          /// Key pattern to clear cache
          /// </summary>
          private const string CATEGORIES_PATTERN_KEY = "Nop.category.";
          /// <summary>
          /// Key pattern to clear cache
          /// </summary>
          private const string PRODUCTCATEGORIES_PATTERN_KEY = "Nop.productcategory.";
  
          #endregion
  
          #region Fields
  
          private readonly IRepository<Category> _categoryRepository;
          private readonly IRepository<ProductCategory> _productCategoryRepository;
          private readonly IRepository<Product> _productRepository;
          private readonly IRepository<AclRecord> _aclRepository;
          private readonly IRepository<StoreMapping> _storeMappingRepository;
          private readonly IWorkContext _workContext;
          private readonly IStoreContext _storeContext;
          private readonly IEventPublisher _eventPublisher;
          private readonly ICacheManager _cacheManager;
          private readonly IStoreMappingService _storeMappingService;
          private readonly IAclService _aclService;
          private readonly CatalogSettings _catalogSettings;
  
          #endregion
          
          #region Ctor
  
          /// <summary>
          /// Ctor
          /// </summary>
          /// <param name="cacheManager">Cache manager</param>
          /// <param name="categoryRepository">Category repository</param>
          /// <param name="productCategoryRepository">ProductCategory repository</param>
          /// <param name="productRepository">Product repository</param>
          /// <param name="aclRepository">ACL record repository</param>
          /// <param name="storeMappingRepository">Store mapping repository</param>
          /// <param name="workContext">Work context</param>
          /// <param name="storeContext">Store context</param>
          /// <param name="eventPublisher">Event publisher</param>
          /// <param name="storeMappingService">Store mapping service</param>
          /// <param name="aclService">ACL service</param>
          /// <param name="catalogSettings">Catalog settings</param>
          public CategoryService(ICacheManager cacheManager,
              IRepository<Category> categoryRepository,
              IRepository<ProductCategory> productCategoryRepository,
              IRepository<Product> productRepository,
              IRepository<AclRecord> aclRepository,
              IRepository<StoreMapping> storeMappingRepository,
              IWorkContext workContext,
              IStoreContext storeContext,
              IEventPublisher eventPublisher,
              IStoreMappingService storeMappingService,
              IAclService aclService,
              CatalogSettings catalogSettings)
          {
              this._cacheManager = cacheManager;
              this._categoryRepository = categoryRepository;
              this._productCategoryRepository = productCategoryRepository;
              this._productRepository = productRepository;
              this._aclRepository = aclRepository;
              this._storeMappingRepository = storeMappingRepository;
              this._workContext = workContext;
              this._storeContext = storeContext;
              this._eventPublisher = eventPublisher;
              this._storeMappingService = storeMappingService;
              this._aclService = aclService;
              this._catalogSettings = catalogSettings;
          }
  
          #endregion
  
          #region Methods
  
          /// <summary>
          /// Delete category
          /// </summary>
          /// <param name="category">Category</param>
          public virtual void DeleteCategory(Category category)
          {
              if (category == null)
                  throw new ArgumentNullException("category");
  
              category.Deleted = true;
              UpdateCategory(category);
  
              //reset a "Parent category" property of all child subcategories
              var subcategories = GetAllCategoriesByParentCategoryId(category.Id, true);
              foreach (var subcategory in subcategories)
              {
                  subcategory.ParentCategoryId = 0;
                  UpdateCategory(subcategory);
              }
          }
          
          /// <summary>
          /// Gets all categories
          /// </summary>
          /// <param name="categoryName">Category name</param>
          /// <param name="storeId">Store identifier; 0 if you want to get all records</param>
          /// <param name="pageIndex">Page index</param>
          /// <param name="pageSize">Page size</param>
          /// <param name="showHidden">A value indicating whether to show hidden records</param>
          /// <returns>Categories</returns>
          public virtual IPagedList<Category> GetAllCategories(string categoryName = "", int storeId = 0, 
              int pageIndex = 0, int pageSize = int.MaxValue, bool showHidden = false)
          {
              var query = _categoryRepository.Table;
              if (!showHidden)
                  query = query.Where(c => c.Published);
              if (!String.IsNullOrWhiteSpace(categoryName))
                  query = query.Where(c => c.Name.Contains(categoryName));
              query = query.Where(c => !c.Deleted);
              query = query.OrderBy(c => c.ParentCategoryId).ThenBy(c => c.DisplayOrder);
  
              if ((storeId > 0 && !_catalogSettings.IgnoreStoreLimitations) || (!showHidden && !_catalogSettings.IgnoreAcl))
              {
                  if (!showHidden && !_catalogSettings.IgnoreAcl)
                  {
                      //ACL (access control list)
                      var allowedCustomerRolesIds = _workContext.CurrentCustomer.GetCustomerRoleIds();
                      query = from c in query
                              join acl in _aclRepository.Table
                              on new { c1 = c.Id, c2 = "Category" } equals new { c1 = acl.EntityId, c2 = acl.EntityName } into c_acl
                              from acl in c_acl.DefaultIfEmpty()
                              where !c.SubjectToAcl || allowedCustomerRolesIds.Contains(acl.CustomerRoleId)
                              select c;
                  }
                  if (storeId > 0 && !_catalogSettings.IgnoreStoreLimitations)
                  {
                      //Store mapping
                      query = from c in query
                              join sm in _storeMappingRepository.Table
                              on new { c1 = c.Id, c2 = "Category" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into c_sm
                              from sm in c_sm.DefaultIfEmpty()
                              where !c.LimitedToStores || storeId == sm.StoreId
                              select c;
                  }
  
                  //only distinct categories (group by ID)
                  query = from c in query
                          group c by c.Id
                          into cGroup
                          orderby cGroup.Key
                          select cGroup.FirstOrDefault();
                  query = query.OrderBy(c => c.ParentCategoryId).ThenBy(c => c.DisplayOrder);
              }
              
              var unsortedCategories = query.ToList();
  
              //sort categories
              var sortedCategories = unsortedCategories.SortCategoriesForTree();
  
              //paging
              return new PagedList<Category>(sortedCategories, pageIndex, pageSize);
  
          }
  
          /// <summary>
          /// Gets all categories filtered by parent category identifier
          /// </summary>
          /// <param name="parentCategoryId">Parent category identifier</param>
          /// <param name="showHidden">A value indicating whether to show hidden records</param>
          /// <param name="includeAllLevels">A value indicating whether we should load all child levels</param>
          /// <returns>Categories</returns>
          public virtual IList<Category> GetAllCategoriesByParentCategoryId(int parentCategoryId,
              bool showHidden = false, bool includeAllLevels = false)
          {
              string key = string.Format(CATEGORIES_BY_PARENT_CATEGORY_ID_KEY, parentCategoryId, showHidden, _workContext.CurrentCustomer.Id, _storeContext.CurrentStore.Id, includeAllLevels);
              return _cacheManager.Get(key, () =>
              {
                  var query = _categoryRepository.Table;
                  if (!showHidden)
                      query = query.Where(c => c.Published);
                  query = query.Where(c => c.ParentCategoryId == parentCategoryId);
                  query = query.Where(c => !c.Deleted);
                  query = query.OrderBy(c => c.DisplayOrder);
  
                  if (!showHidden && (!_catalogSettings.IgnoreAcl || !_catalogSettings.IgnoreStoreLimitations))
                  {
                      if (!_catalogSettings.IgnoreAcl)
                      {
                          //ACL (access control list)
                          var allowedCustomerRolesIds = _workContext.CurrentCustomer.GetCustomerRoleIds();
                          query = from c in query
                                  join acl in _aclRepository.Table
                                  on new { c1 = c.Id, c2 = "Category" } equals new { c1 = acl.EntityId, c2 = acl.EntityName } into c_acl
                                  from acl in c_acl.DefaultIfEmpty()
                                  where !c.SubjectToAcl || allowedCustomerRolesIds.Contains(acl.CustomerRoleId)
                                  select c;
                      }
                      if (!_catalogSettings.IgnoreStoreLimitations)
                      {
                          //Store mapping
                          var currentStoreId = _storeContext.CurrentStore.Id;
                          query = from c in query
                                  join sm in _storeMappingRepository.Table
                                  on new { c1 = c.Id, c2 = "Category" } equals new { c1 = sm.EntityId, c2 = sm.EntityName } into c_sm
                                  from sm in c_sm.DefaultIfEmpty()
                                  where !c.LimitedToStores || currentStoreId == sm.StoreId
                                  select c;
                      }
                      //only distinct categories (group by ID)
                      query = from c in query
                              group c by c.Id
                              into cGroup
                              orderby cGroup.Key
                              select cGroup.FirstOrDefault();
                      query = query.OrderBy(c => c.DisplayOrder);
                  }
  
  
gt:                 var categories = query.ToList();