using Nop.Core; using Nop.Core.Caching; using Nop.Core.Domain.Common; using Nop.Data; namespace Nop.Services.Common; /// /// Generic attribute service /// public partial class GenericAttributeService : IGenericAttributeService { #region Fields protected readonly IRepository _genericAttributeRepository; protected readonly IShortTermCacheManager _shortTermCacheManager; protected readonly IStaticCacheManager _staticCacheManager; #endregion #region Ctor public GenericAttributeService(IRepository genericAttributeRepository, IShortTermCacheManager shortTermCacheManager, IStaticCacheManager staticCacheManager) { _genericAttributeRepository = genericAttributeRepository; _shortTermCacheManager = shortTermCacheManager; _staticCacheManager = staticCacheManager; } #endregion #region Methods /// /// Deletes an attribute /// /// Attribute /// A task that represents the asynchronous operation public virtual async Task DeleteAttributeAsync(GenericAttribute attribute) { await _genericAttributeRepository.DeleteAsync(attribute); } /// /// Deletes an attributes /// /// Attributes /// A task that represents the asynchronous operation public virtual async Task DeleteAttributesAsync(IList attributes) { await _genericAttributeRepository.DeleteAsync(attributes); } /// /// Inserts an attribute /// /// attribute /// A task that represents the asynchronous operation public virtual async Task InsertAttributeAsync(GenericAttribute attribute) { ArgumentNullException.ThrowIfNull(attribute); attribute.CreatedOrUpdatedDateUTC = DateTime.UtcNow; await _genericAttributeRepository.InsertAsync(attribute); } /// /// Updates the attribute /// /// Attribute /// A task that represents the asynchronous operation public virtual async Task UpdateAttributeAsync(GenericAttribute attribute) { ArgumentNullException.ThrowIfNull(attribute); attribute.CreatedOrUpdatedDateUTC = DateTime.UtcNow; await _genericAttributeRepository.UpdateAsync(attribute); } /// /// Get attributes /// /// Entity identifier /// Key group /// /// A task that represents the asynchronous operation /// The task result contains the get attributes /// public virtual async Task> GetAttributesForEntityAsync(int entityId, string keyGroup) { var query = from ga in _genericAttributeRepository.Table where ga.EntityId == entityId && ga.KeyGroup == keyGroup select ga; var attributes = await _shortTermCacheManager.GetAsync(async () => await query.ToListAsync(), NopCommonDefaults.GenericAttributeCacheKey, entityId, keyGroup); return attributes; } /// /// Save attribute value /// /// Property type /// Entity /// Key /// Value /// Store identifier; pass 0 if this attribute will be available for all stores /// A task that represents the asynchronous operation public virtual async Task SaveAttributeAsync(BaseEntity entity, string key, TPropType value, int storeId = 0) { ArgumentNullException.ThrowIfNull(entity); ArgumentNullException.ThrowIfNull(key); var keyGroup = entity.GetType().Name; var props = (await GetAttributesForEntityAsync(entity.Id, keyGroup)) .Where(x => x.StoreId == storeId) .ToList(); var prop = props.FirstOrDefault(ga => ga.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)); //should be culture invariant var valueStr = CommonHelper.To(value); if (prop != null) { if (string.IsNullOrWhiteSpace(valueStr)) //delete await DeleteAttributeAsync(prop); else { //update prop.Value = valueStr; await UpdateAttributeAsync(prop); } } else { if (string.IsNullOrWhiteSpace(valueStr)) return; //insert prop = new GenericAttribute { EntityId = entity.Id, Key = key, KeyGroup = keyGroup, Value = valueStr, StoreId = storeId }; await InsertAttributeAsync(prop); } } /// /// Get an attribute of an entity /// /// Property type /// Entity /// Key /// Load a value specific for a certain store; pass 0 to load a value shared for all stores /// Default value /// /// A task that represents the asynchronous operation /// The task result contains the attribute /// public virtual async Task GetAttributeAsync(BaseEntity entity, string key, int storeId = 0, TPropType defaultValue = default) { ArgumentNullException.ThrowIfNull(entity); var keyGroup = entity.GetType().Name; var props = await GetAttributesForEntityAsync(entity.Id, keyGroup); //little hack here (only for unit testing). we should write expect-return rules in unit tests for such cases if (props == null) return defaultValue; props = props.Where(x => x.StoreId == storeId).ToList(); if (!props.Any()) return defaultValue; var prop = props.FirstOrDefault(ga => ga.Key.Equals(key, StringComparison.InvariantCultureIgnoreCase)); //should be culture invariant if (prop == null || string.IsNullOrEmpty(prop.Value)) return defaultValue; return CommonHelper.To(prop.Value); } /// /// Get an attribute of an entity /// /// Property type /// Entity type /// Entity identifier /// Key /// Load a value specific for a certain store; pass 0 to load a value shared for all stores /// Default value /// /// A task that represents the asynchronous operation /// The task result contains the attribute /// public virtual async Task GetAttributeAsync(int entityId, string key, int storeId = 0, TPropType defaultValue = default) where TEntity : BaseEntity { var entity = (TEntity)Activator.CreateInstance(typeof(TEntity)); entity.Id = entityId; return await GetAttributeAsync(entity, key, storeId, defaultValue); } #endregion }