diff --git a/src/VirtoCommerce.Platform.Core/Extensions/CrudServiceExtensions.cs b/src/VirtoCommerce.Platform.Core/Extensions/CrudServiceExtensions.cs index 10d2c95faa4..15604f8153b 100644 --- a/src/VirtoCommerce.Platform.Core/Extensions/CrudServiceExtensions.cs +++ b/src/VirtoCommerce.Platform.Core/Extensions/CrudServiceExtensions.cs @@ -45,4 +45,20 @@ public static Task> GetByIdsAsync(this ICrudService + /// Returns data from the database without using cache. + /// + public static async Task GetNoCacheAsync(this ICrudService crudService, string id, string responseGroup = null) + where TModel : Entity + { + if (id is null) + { + return null; + } + + var entities = await crudService.GetNoCacheAsync([id], responseGroup); + + return entities?.FirstOrDefault(); + } } diff --git a/src/VirtoCommerce.Platform.Core/Extensions/SearchServiceExtensions.cs b/src/VirtoCommerce.Platform.Core/Extensions/SearchServiceExtensions.cs index c97347b6bec..8d5f3f7706b 100644 --- a/src/VirtoCommerce.Platform.Core/Extensions/SearchServiceExtensions.cs +++ b/src/VirtoCommerce.Platform.Core/Extensions/SearchServiceExtensions.cs @@ -34,6 +34,24 @@ public static async Task> SearchAllAsync + /// Returns data from the database without using cache. + /// + public static async Task> SearchAllNoCacheAsync(this ISearchService searchService, TCriteria searchCriteria) + where TCriteria : SearchCriteriaBase + where TResult : GenericSearchResult + where TModel : IEntity + { + var result = new List(); + + await foreach (var searchResult in searchService.SearchBatchesNoCacheAsync(searchCriteria)) + { + result.AddRange(searchResult.Results); + } + + return result; + } + /// /// Returns data from the cache without cloning. This consumes less memory, but the returned data must not be modified. /// @@ -71,10 +89,26 @@ public static IAsyncEnumerable SearchBatchesNoCloneAsync where TModel : IEntity { - return searchService.SearchBatchesAsync(searchCriteria, clone: false); + return SearchBatchesInternalAsync(searchService, searchCriteria, false); + } + + public static IAsyncEnumerable SearchBatchesAsync(this ISearchService searchService, TCriteria searchCriteria, bool clone = true) + where TCriteria : SearchCriteriaBase + where TResult : GenericSearchResult + where TModel : IEntity + { + return SearchBatchesInternalAsync(searchService, searchCriteria, clone); + } + + public static IAsyncEnumerable SearchBatchesNoCacheAsync(this ISearchService searchService, TCriteria searchCriteria) + where TCriteria : SearchCriteriaBase + where TResult : GenericSearchResult + where TModel : IEntity + { + return SearchBatchesInternalAsync(searchService, searchCriteria, noCache: true); } - public static async IAsyncEnumerable SearchBatchesAsync(this ISearchService searchService, TCriteria searchCriteria, bool clone = true) + private static async IAsyncEnumerable SearchBatchesInternalAsync(this ISearchService searchService, TCriteria searchCriteria, bool clone = true, bool noCache = false) where TCriteria : SearchCriteriaBase where TResult : GenericSearchResult where TModel : IEntity @@ -84,10 +118,12 @@ public static async IAsyncEnumerable SearchBatchesAsync 0) { yield return searchResult; } diff --git a/src/VirtoCommerce.Platform.Core/GenericCrud/ICrudService.cs b/src/VirtoCommerce.Platform.Core/GenericCrud/ICrudService.cs index d5eff83ee82..19e62658d92 100644 --- a/src/VirtoCommerce.Platform.Core/GenericCrud/ICrudService.cs +++ b/src/VirtoCommerce.Platform.Core/GenericCrud/ICrudService.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Threading.Tasks; using VirtoCommerce.Platform.Core.Common; @@ -20,7 +21,19 @@ public interface ICrudService /// Task> GetAsync(IList ids, string responseGroup = null, bool clone = true); + /// + /// Returns a list of model instances for specified IDs without using cache. + /// + /// + /// + /// + Task> GetNoCacheAsync(IList ids, string responseGroup = null) + { + throw new NotSupportedException("Underlying service does not support no cache search."); + } + Task SaveChangesAsync(IList models); + Task DeleteAsync(IList ids, bool softDelete = false); } } diff --git a/src/VirtoCommerce.Platform.Core/GenericCrud/ISearchService.cs b/src/VirtoCommerce.Platform.Core/GenericCrud/ISearchService.cs index e24605ca4b3..b4d6189ac5c 100644 --- a/src/VirtoCommerce.Platform.Core/GenericCrud/ISearchService.cs +++ b/src/VirtoCommerce.Platform.Core/GenericCrud/ISearchService.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using VirtoCommerce.Platform.Core.Common; @@ -21,5 +22,15 @@ public interface ISearchService /// If false, returns data from the cache without cloning. This consumes less memory, but the returned data must not be modified. /// Task SearchAsync(TCriteria criteria, bool clone = true); + + /// + /// Returns model instances that meet specified criteria without using cache. + /// + /// + /// + Task SearchNoCacheAsync(TCriteria criteria) + { + throw new NotSupportedException("Underlying service does not support no cache search."); + } } } diff --git a/src/VirtoCommerce.Platform.Data/GenericCrud/CrudService.cs b/src/VirtoCommerce.Platform.Data/GenericCrud/CrudService.cs index 0e890f765f2..e78f73b6f38 100644 --- a/src/VirtoCommerce.Platform.Data/GenericCrud/CrudService.cs +++ b/src/VirtoCommerce.Platform.Data/GenericCrud/CrudService.cs @@ -60,21 +60,25 @@ public virtual async Task> GetAsync(IList ids, string resp missingIds => GetByIdsNoCache(missingIds, responseGroup), ConfigureCache); - if (!clone) - { - return models; - } + return !clone ? models : models.Select(x => x.CloneTyped()).ToList(); + } - return models - .Select(x => x.CloneTyped()) - .ToList(); + /// + /// Returns a list of model instances for specified IDs without using cache. + /// + /// + /// + /// + public virtual Task> GetNoCacheAsync(IList ids, string responseGroup = null) + { + return GetByIdsNoCache(ids, responseGroup); } protected virtual async Task> GetByIdsNoCache(IList ids, string responseGroup) { using var repository = _repositoryFactory(); - // Disable DBContext change tracking for better performance + // Disable DBContext change tracking for better performance repository.DisableChangesTracking(); var entities = await LoadEntities(repository, ids, responseGroup); @@ -341,7 +345,7 @@ protected virtual TEntity FromModel(TModel model, PrimaryKeyResolvingMap keyMap) protected virtual GenericChangedEntryEvent EventFactory(IList> changedEntries) { - return (GenericChangedEntryEvent)typeof(TEvent).GetConstructor(new[] { typeof(IEnumerable>) }).Invoke(new object[] { changedEntries }); + return (GenericChangedEntryEvent)typeof(TEvent).GetConstructor([typeof(IEnumerable>)])?.Invoke([changedEntries]); } } } diff --git a/src/VirtoCommerce.Platform.Data/GenericCrud/SearchService.cs b/src/VirtoCommerce.Platform.Data/GenericCrud/SearchService.cs index 77d39d043ff..aa1f041e391 100644 --- a/src/VirtoCommerce.Platform.Data/GenericCrud/SearchService.cs +++ b/src/VirtoCommerce.Platform.Data/GenericCrud/SearchService.cs @@ -83,6 +83,29 @@ public virtual async Task SearchAsync(TCriteria criteria, bool clone = return await ProcessSearchResultAsync(result, criteria); } + /// + /// Returns model instances that meet specified criteria without using cache. + /// + /// + /// + public virtual async Task SearchNoCacheAsync(TCriteria criteria) + { + ValidateSearchCriteria(criteria); + + var idsResult = await SearchIdsNoCacheAsync(criteria); + + var result = AbstractTypeFactory.TryCreateInstance(); + result.TotalCount = idsResult.TotalCount; + + if (idsResult.Results?.Count > 0) + { + var models = await _crudService.GetNoCacheAsync(idsResult.Results, criteria.ResponseGroup); + result.Results.AddRange(models.OrderBy(x => idsResult.Results.IndexOf(x.Id))); + } + + return await ProcessSearchResultAsync(result, criteria); + } + protected virtual void ValidateSearchCriteria(TCriteria criteria) {