Webiant Logo Webiant Logo
  1. No results found.

    Try your search with a different keyword or use * as a wildcard.

EntityRepository.cs

using System.Linq.Expressions;
using System.Transactions;
using Nop.Core;
using Nop.Core.Caching;
using Nop.Core.Configuration;
using Nop.Core.Domain.Common;
using Nop.Core.Events;

namespace Nop.Data;

/// 
/// Represents the entity repository implementation
/// 
/// Entity type
public partial class EntityRepository : IRepository where TEntity : BaseEntity
{
    #region Fields

    protected readonly IEventPublisher _eventPublisher;
    protected readonly INopDataProvider _dataProvider;
    protected readonly IShortTermCacheManager _shortTermCacheManager;
    protected readonly IStaticCacheManager _staticCacheManager;
    protected readonly bool _usingDistributedCache;

    #endregion

    #region Ctor

    public EntityRepository(IEventPublisher eventPublisher,
        INopDataProvider dataProvider,
        IShortTermCacheManager shortTermCacheManager,
        IStaticCacheManager staticCacheManager,
        AppSettings appSettings)
    {
        _eventPublisher = eventPublisher;
        _dataProvider = dataProvider;
        _shortTermCacheManager = shortTermCacheManager;
        _staticCacheManager = staticCacheManager;
        _usingDistributedCache = appSettings.Get().DistributedCacheType switch
        {
            DistributedCacheType.Redis => true,
            DistributedCacheType.SqlServer => true,
            _ => false
        };
    }

    #endregion

    #region Utilities

    /// 
    /// Get all entity entries
    /// 
    /// Function to select entries
    /// Function to get a cache key; pass null to don't cache; return null from this function to use the default key
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the entity entries
    /// 
    protected virtual async Task> GetEntitiesAsync(Func>> getAllAsync, Func getCacheKey)
    {
        if (getCacheKey == null)
            return await getAllAsync();

        //caching
        var cacheKey = getCacheKey(_staticCacheManager)
                       ?? _staticCacheManager.PrepareKeyForDefaultCache(NopEntityCacheDefaults.AllCacheKey);
        return await _staticCacheManager.GetAsync(cacheKey, getAllAsync);
    }

    /// 
    /// Get all entity entries
    /// 
    /// Function to select entries
    /// Function to get a cache key; pass null to don't cache; return null from this function to use the default key
    /// Entity entries
    protected virtual IList GetEntities(Func> getAll, Func getCacheKey)
    {
        if (getCacheKey == null)
            return getAll();

        //caching
        var cacheKey = getCacheKey(_staticCacheManager)
                       ?? _staticCacheManager.PrepareKeyForDefaultCache(NopEntityCacheDefaults.AllCacheKey);

        return _staticCacheManager.Get(cacheKey, getAll);
    }

    /// 
    /// Get all entity entries
    /// 
    /// Function to select entries
    /// Function to get a cache key; pass null to don't cache; return null from this function to use the default key
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the entity entries
    /// 
    protected virtual async Task> GetEntitiesAsync(Func>> getAllAsync, Func> getCacheKey)
    {
        if (getCacheKey == null)
            return await getAllAsync();

        //caching
        var cacheKey = await getCacheKey(_staticCacheManager)
                       ?? _staticCacheManager.PrepareKeyForDefaultCache(NopEntityCacheDefaults.AllCacheKey);
        return await _staticCacheManager.GetAsync(cacheKey, getAllAsync);
    }

    /// 
    /// Adds "deleted" filter to query which contains  entries, if its need
    /// 
    /// Entity entries
    /// Whether to include deleted items
    /// Entity entries
    protected virtual IQueryable AddDeletedFilter(IQueryable query, in bool includeDeleted)
    {
        if (includeDeleted)
            return query;

        if (typeof(TEntity).GetInterface(nameof(ISoftDeletedEntity)) == null)
            return query;

        return query.OfType().Where(entry => !entry.Deleted).OfType();
    }

    /// 
    /// Transactionally deletes a list of entities
    /// 
    /// Entities to delete
    protected virtual async Task DeleteAsync(IList entities)
    {
        using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
        await _dataProvider.BulkDeleteEntitiesAsync(entities);
        transaction.Complete();
    }

    /// 
    /// Soft-deletes  entities
    /// 
    /// Entities to delete
    protected virtual async Task DeleteAsync(IList entities) where T : ISoftDeletedEntity, TEntity
    {
        foreach (var entity in entities)
            entity.Deleted = true;
        await _dataProvider.UpdateEntitiesAsync(entities);
    }

    #endregion

    #region Methods

    /// 
    /// Get the entity entry
    /// 
    /// Entity entry identifier
    /// Function to get a cache key; pass null to don't cache; return null from this function to use the default key
    /// Whether to include deleted items (applies only to  entities)
    /// Whether to use short term cache instead of static cache
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the entity entry
    /// 
    public virtual async Task GetByIdAsync(int? id, Func getCacheKey = null, bool includeDeleted = true, bool useShortTermCache = false)
    {
        if (!id.HasValue || id == 0)
            return null;

        async Task getEntityAsync()
        {
            return await AddDeletedFilter(Table, includeDeleted).FirstOrDefaultAsync(entity => entity.Id == Convert.ToInt32(id));
        }

        if (getCacheKey == null)
            return await getEntityAsync();

        ICacheKeyService cacheKeyService = useShortTermCache ? _shortTermCacheManager : _staticCacheManager;

        //caching
        var cacheKey = getCacheKey(cacheKeyService)
                       ?? cacheKeyService.PrepareKeyForDefaultCache(NopEntityCacheDefaults.ByIdCacheKey, id);

        if (useShortTermCache)
            return await _shortTermCacheManager.GetAsync(getEntityAsync, cacheKey);

        return await _staticCacheManager.GetAsync(cacheKey, getEntityAsync);
    }

    /// 
    /// Get the entity entry
    /// 
    /// Entity entry identifier
    /// Function to get a cache key; pass null to don't cache; return null from this function to use the default key
    /// Whether to include deleted items (applies only to  entities)
    /// 
    /// The entity entry
    /// 
    public virtual TEntity GetById(int? id, Func getCacheKey = null, bool includeDeleted = true)
    {
        if (!id.HasValue || id == 0)
            return null;

        TEntity getEntity()
        {
            return AddDeletedFilter(Table, includeDeleted).FirstOrDefault(entity => entity.Id == Convert.ToInt32(id));
        }

        if (getCacheKey == null)
            return getEntity();

        //caching
        var cacheKey = getCacheKey(_staticCacheManager)
                       ?? _staticCacheManager.PrepareKeyForDefaultCache(NopEntityCacheDefaults.ByIdCacheKey, id);

        return _staticCacheManager.Get(cacheKey, getEntity);
    }

    /// 
    /// Get entity entries by identifiers
    /// 
    /// Entity entry identifiers
    /// Function to get a cache key; pass null to don't cache; return null from this function to use the default key
    /// Whether to include deleted items (applies only to  entities)
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the entity entries
    /// 
    public virtual async Task> GetByIdsAsync(IList ids, Func getCacheKey = null, bool includeDeleted = true)
    {
        if (ids?.Any() != true)
            return new List();

        static IList sortByIdList(IList listOfId, IDictionary entitiesById)
        {
            var sortedEntities = new List(listOfId.Count);

            foreach (var id in listOfId)
                if (entitiesById.TryGetValue(id, out var entry))
                    sortedEntities.Add(entry);

            return sortedEntities;
        }

        async Task> getByIdsAsync(IList listOfId, bool sort = true)
        {
            var query = AddDeletedFilter(Table, includeDeleted)
                .Where(entry => listOfId.Contains(entry.Id));

            return sort
                ? sortByIdList(listOfId, await query.ToDictionaryAsync(entry => entry.Id))
                : await query.ToListAsync();
        }

        if (getCacheKey == null)
            return await getByIdsAsync(ids);

        //caching
        var cacheKey = getCacheKey(_staticCacheManager);
        if (cacheKey == null && _usingDistributedCache)
            cacheKey = _staticCacheManager.PrepareKeyForDefaultCache(NopEntityCacheDefaults.ByIdsCacheKey, ids);
        if (cacheKey != null)
            return await _staticCacheManager.GetAsync(cacheKey, async () => await getByIdsAsync(ids));

        //if we are using an in-memory cache, we can optimize by caching each entity individually for maximum reusability.
        //with a distributed cache, the overhead would be too high.
        var cachedById = await ids
            .Distinct()
            .SelectAwait(async id => await _staticCacheManager.GetAsync(
                _staticCacheManager.PrepareKeyForDefaultCache(NopEntityCacheDefaults.ByIdCacheKey, id),
                default(TEntity)))
            .Where(entity => entity != default)
            .ToDictionaryAsync(entity => entity.Id, entity => entity);
        var missingIds = ids.Except(cachedById.Keys).ToList();
        var missingEntities = missingIds.Count > 0 ? await getByIdsAsync(missingIds, false) : new List();

        foreach (var entity in missingEntities)
        {
            await _staticCacheManager.SetAsync(_staticCacheManager.PrepareKeyForDefaultCache(NopEntityCacheDefaults.ByIdCacheKey, entity.Id), entity);
            cachedById[entity.Id] = entity;
        }

        return sortByIdList(ids, cachedById);
    }

    /// 
    /// Get all entity entries
    /// 
    /// Function to select entries
    /// Function to get a cache key; pass null to don't cache; return null from this function to use the default key
    /// Whether to include deleted items (applies only to  entities)
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the entity entries
    /// 
    public virtual async Task> GetAllAsync(Func, IQueryable> func = null,
        Func getCacheKey = null, bool includeDeleted = true)
    {
        async Task> getAllAsync()
        {
            var query = AddDeletedFilter(Table, includeDeleted);
            query = func != null ? func(query) : query;

            return await query.ToListAsync();
        }

        return await GetEntitiesAsync(getAllAsync, getCacheKey);
    }

    /// 
    /// Get all entity entries
    /// 
    /// Function to select entries
    /// Function to get a cache key; pass null to don't cache; return null from this function to use the default key
    /// Whether to include deleted items (applies only to  entities)
    /// Entity entries
    public virtual IList GetAll(Func, IQueryable> func = null,
        Func getCacheKey = null, bool includeDeleted = true)
    {
        IList getAll()
        {
            var query = AddDeletedFilter(Table, includeDeleted);
            query = func != null ? func(query) : query;

            return query.ToList();
        }

        return GetEntities(getAll, getCacheKey);
    }

    /// 
    /// Get all entity entries
    /// 
    /// Function to select entries
    /// Function to get a cache key; pass null to don't cache; return null from this function to use the default key
    /// Whether to include deleted items (applies only to  entities)
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the entity entries
    /// 
    public virtual async Task> GetAllAsync(
        Func, Task>> func = null,
        Func getCacheKey = null, bool includeDeleted = true)
    {
        async Task> getAllAsync()
        {
            var query = AddDeletedFilter(Table, includeDeleted);
            query = func != null ? await func(query) : query;

            return await query.ToListAsync();
        }

        return await GetEntitiesAsync(getAllAsync, getCacheKey);
    }

    /// 
    /// Get all entity entries
    /// 
    /// Function to select entries
    /// Function to get a cache key; pass null to don't cache; return null from this function to use the default key
    /// Whether to include deleted items (applies only to  entities)
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the entity entries
    /// 
    public virtual async Task> GetAllAsync(
        Func, Task>> func = null,
        Func> getCacheKey = null, bool includeDeleted = true)
    {
        async Task> getAllAsync()
        {
            var query = AddDeletedFilter(Table, includeDeleted);
            query = func != null ? await func(query) : query;

            return await query.ToListAsync();
        }

        return await GetEntitiesAsync(getAllAsync, getCacheKey);
    }

    /// 
    /// Get paged list of all entity entries
    /// 
    /// Function to select entries
    /// Page index
    /// Page size
    /// Whether to get only the total number of entries without actually loading data
    /// Whether to include deleted items (applies only to  entities)
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the paged list of entity entries
    /// 
    public virtual async Task> GetAllPagedAsync(Func, IQueryable> func = null,
        int pageIndex = 0, int pageSize = int.MaxValue, bool getOnlyTotalCount = false, bool includeDeleted = true)
    {
        var query = AddDeletedFilter(Table, includeDeleted);

        query = func != null ? func(query) : query;

        return await query.ToPagedListAsync(pageIndex, pageSize, getOnlyTotalCount);
    }

    /// 
    /// Get paged list of all entity entries
    /// 
    /// Function to select entries
    /// Page index
    /// Page size
    /// Whether to get only the total number of entries without actually loading data
    /// Whether to include deleted items (applies only to  entities)
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the paged list of entity entries
    /// 
    public virtual async Task> GetAllPagedAsync(Func, Task>> func = null,
        int pageIndex = 0, int pageSize = int.MaxValue, bool getOnlyTotalCount = false, bool includeDeleted = true)
    {
        var query = AddDeletedFilter(Table, includeDeleted);

        query = func != null ? await func(query) : query;

        return await query.ToPagedListAsync(pageIndex, pageSize, getOnlyTotalCount);
    }

    /// 
    /// Insert the entity entry
    /// 
    /// Entity entry
    /// Whether to publish event notification
    /// A task that represents the asynchronous operation
    public virtual async Task InsertAsync(TEntity entity, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entity);

        await _dataProvider.InsertEntityAsync(entity);

        //event notification
        if (publishEvent)
            await _eventPublisher.EntityInsertedAsync(entity);
    }

    /// 
    /// Insert the entity entry
    /// 
    /// Entity entry
    /// Whether to publish event notification
    public virtual void Insert(TEntity entity, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entity);

        _dataProvider.InsertEntity(entity);

        //event notification
        if (publishEvent)
            _eventPublisher.EntityInserted(entity);
    }

    /// 
    /// Insert entity entries
    /// 
    /// Entity entries
    /// Whether to publish event notification
    /// A task that represents the asynchronous operation
    public virtual async Task InsertAsync(IList entities, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entities);

        using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
        await _dataProvider.BulkInsertEntitiesAsync(entities);
        transaction.Complete();

        if (!publishEvent)
            return;

        //event notification
        foreach (var entity in entities)
            await _eventPublisher.EntityInsertedAsync(entity);
    }

    /// 
    /// Insert entity entries
    /// 
    /// Entity entries
    /// Whether to publish event notification
    public virtual void Insert(IList entities, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entities);

        using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
        _dataProvider.BulkInsertEntities(entities);
        transaction.Complete();

        if (!publishEvent)
            return;

        //event notification
        foreach (var entity in entities)
            _eventPublisher.EntityInserted(entity);
    }

    /// 
    /// Loads the original copy of the entity
    /// 
    /// Entity
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the copy of the passed entity
    /// 
    public virtual async Task LoadOriginalCopyAsync(TEntity entity)
    {
        return await _dataProvider.GetTable()
            .FirstOrDefaultAsync(e => e.Id == Convert.ToInt32(entity.Id));
    }

    /// 
    /// Update the entity entry
    /// 
    /// Entity entry
    /// Whether to publish event notification
    /// A task that represents the asynchronous operation
    public virtual async Task UpdateAsync(TEntity entity, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entity);

        await _dataProvider.UpdateEntityAsync(entity);

        //event notification
        if (publishEvent)
            await _eventPublisher.EntityUpdatedAsync(entity);
    }

    /// 
    /// Update the entity entry
    /// 
    /// Entity entry
    /// Whether to publish event notification
    public virtual void Update(TEntity entity, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entity);

        _dataProvider.UpdateEntity(entity);

        //event notification
        if (publishEvent)
            _eventPublisher.EntityUpdated(entity);
    }

    /// 
    /// Update entity entries
    /// 
    /// Entity entries
    /// Whether to publish event notification
    /// A task that represents the asynchronous operation
    public virtual async Task UpdateAsync(IList entities, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entities);

        if (!entities.Any())
            return;

        await _dataProvider.UpdateEntitiesAsync(entities);

        //event notification
        if (!publishEvent)
            return;

        foreach (var entity in entities)
            await _eventPublisher.EntityUpdatedAsync(entity);
    }

    /// 
    /// Update entity entries
    /// 
    /// Entity entries
    /// Whether to publish event notification
    public virtual void Update(IList entities, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entities);

        if (!entities.Any())
            return;

        _dataProvider.UpdateEntities(entities);

        //event notification
        if (!publishEvent)
            return;

        foreach (var entity in entities)
            _eventPublisher.EntityUpdated(entity);
    }

    /// 
    /// Delete the entity entry
    /// 
    /// Entity entry
    /// Whether to publish event notification
    /// A task that represents the asynchronous operation
    public virtual async Task DeleteAsync(TEntity entity, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entity);

        switch (entity)
        {
            case ISoftDeletedEntity softDeletedEntity:
                softDeletedEntity.Deleted = true;
                await _dataProvider.UpdateEntityAsync(entity);
                break;

            default:
                await _dataProvider.DeleteEntityAsync(entity);
                break;
        }

        //event notification
        if (publishEvent)
            await _eventPublisher.EntityDeletedAsync(entity);
    }

    /// 
    /// Delete the entity entry
    /// 
    /// Entity entry
    /// Whether to publish event notification
    public virtual void Delete(TEntity entity, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entity);

        switch (entity)
        {
            case ISoftDeletedEntity softDeletedEntity:
                softDeletedEntity.Deleted = true;
                _dataProvider.UpdateEntity(entity);
                break;

            default:
                _dataProvider.DeleteEntity(entity);
                break;
        }

        //event notification
        if (publishEvent)
            _eventPublisher.EntityDeleted(entity);
    }

    /// 
    /// Delete entity entries
    /// 
    /// Entity entries
    /// Whether to publish event notification
    /// A task that represents the asynchronous operation
    public virtual async Task DeleteAsync(IList entities, bool publishEvent = true)
    {
        ArgumentNullException.ThrowIfNull(entities);

        if (!entities.Any())
            return;

        await DeleteAsync(entities);

        //event notification
        if (!publishEvent)
            return;

        foreach (var entity in entities)
            await _eventPublisher.EntityDeletedAsync(entity);
    }

    /// 
    /// Delete entity entries by the passed predicate
    /// 
    /// A function to test each element for a condition
    /// 
    /// A task that represents the asynchronous operation
    /// The task result contains the number of deleted records
    /// 
    public virtual async Task DeleteAsync(Expression> predicate)
    {
        ArgumentNullException.ThrowIfNull(predicate);

        using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
        var countDeletedRecords = await _dataProvider.BulkDeleteEntitiesAsync(predicate);
        transaction.Complete();

        return countDeletedRecords;
    }

    /// 
    /// Delete entity entries by the passed predicate
    /// 
    /// A function to test each element for a condition
    /// 
    /// The number of deleted records
    /// 
    public virtual int Delete(Expression> predicate)
    {
        ArgumentNullException.ThrowIfNull(predicate);

        using var transaction = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled);
        var countDeletedRecords = _dataProvider.BulkDeleteEntities(predicate);
        transaction.Complete();

        return countDeletedRecords;
    }

    /// 
    /// Truncates database table
    /// 
    /// Performs reset identity column
    /// A task that represents the asynchronous operation
    public virtual async Task TruncateAsync(bool resetIdentity = false)
    {
        await _dataProvider.TruncateAsync(resetIdentity);
    }

    #endregion

    #region Properties

    /// 
    /// Gets a table
    /// 
    public virtual IQueryable Table => _dataProvider.GetTable();

    #endregion
}