diff --git a/AsbCloudInfrastructure/EfCache/EfCacheDictionaryExtensions.cs b/AsbCloudInfrastructure/EfCache/EfCacheDictionaryExtensions.cs deleted file mode 100644 index 033a3d47..00000000 --- a/AsbCloudInfrastructure/EfCache/EfCacheDictionaryExtensions.cs +++ /dev/null @@ -1,439 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudInfrastructure.EfCache -{ -#nullable enable - /// - /// Кеширование запросов EF. - /// Кеш не отслеживается ChangeTracker. - /// - public static class EfCacheDictionaryExtensions - { - private static readonly Dictionary caches = new(16); - private static readonly TimeSpan semaphoreTimeout = TimeSpan.FromSeconds(25); - private static readonly SemaphoreSlim semaphore = new(1); - private static readonly TimeSpan minCacheTime = TimeSpan.FromSeconds(2); - private static readonly TimeSpan defaultObsolescence = TimeSpan.FromMinutes(4); - - private class CacheItem - { - internal IDictionary? Data; - internal DateTime DateObsolete; - internal DateTime DateObsoleteTotal; - internal readonly SemaphoreSlim semaphore = new(1); - - - internal Dictionary GetData() - where TKey : notnull - { - if (Data is Dictionary typedData) - return typedData; - throw new TypeAccessException("Cache data has wrong type. Possible 'tag' is not unique."); - } - - internal Dictionary GetData(Func convert, int attempt = 1) - where TKey : notnull - { - if (Data is Dictionary typedData) - return typedData; - if (Data is Dictionary typedEntityData) - { - if (semaphore.Wait(0)) - { - try - { - var convertedData = typedEntityData.ToDictionary(i => i.Key, i => convert(i.Value)); - Data = convertedData; - return convertedData; - } - catch - { - throw; - } - finally - { - semaphore.Release(); - } - } - else - { - if (semaphore.Wait(semaphoreTimeout)) - { - semaphore.Release(); - } - else - { - semaphore.Release(); - throw new TimeoutException("EfCacheL2.GetOrAddCache. Can't wait too long while converting cache data"); - } - } - } - if (attempt > 0) - return GetData(convert, --attempt); - throw new TypeAccessException("Cache data has wrong type. Possible 'tag' is not unique."); - } - } - - private static CacheItem GetOrAddCache(string tag, Func valueFactory, TimeSpan obsolete) - { - CacheItem cache; - while (!caches.ContainsKey(tag)) - { - if (semaphore.Wait(0)) - { - try - { - cache = new CacheItem(); - caches.Add(tag, cache); - } - catch - { - throw; - } - finally - { - semaphore.Release(); - } - break; - } - else - { - if (semaphore.Wait(semaphoreTimeout)) - { - semaphore.Release(); - } - else - { - semaphore.Release(); - throw new TimeoutException("EfCacheL2.GetOrAddCache. Can't wait too long while getting cache"); - } - } - } - - cache = caches[tag]; - - if (cache.DateObsolete < DateTime.Now) - { - if (cache.semaphore.Wait(0)) - { - try - { - var dateObsolete = DateTime.Now + obsolete; - var dateQueryStart = DateTime.Now; - var data = valueFactory(); - var queryTime = DateTime.Now - dateQueryStart; - - if (dateObsolete - DateTime.Now < minCacheTime) - dateObsolete = DateTime.Now + minCacheTime; - - cache.Data = data; - cache.DateObsolete = dateObsolete; - cache.DateObsoleteTotal = dateObsolete + queryTime + minCacheTime; - } - catch - { - throw; - } - finally - { - cache.semaphore.Release(); - } - } - else if (cache.DateObsoleteTotal < DateTime.Now) - { - if (cache.semaphore.Wait(semaphoreTimeout)) - { - cache.semaphore.Release(); - } - else - { - cache.semaphore.Release(); - throw new TimeoutException("EfCacheL2.GetOrAddCache. Can't wait too long while getting cache"); - } - } - } - return cache; - } - - private static async Task GetOrAddCacheAsync(string tag, Func> valueFactoryAsync, TimeSpan obsolete, CancellationToken token) - { - CacheItem cache; - while (!caches.ContainsKey(tag)) - { - if (semaphore.Wait(0)) - { - try - { - cache = new CacheItem(); - caches.Add(tag, cache); - } - catch - { - throw; - } - finally - { - semaphore.Release(); - } - break; - } - else - { - if (await semaphore.WaitAsync(semaphoreTimeout, token)) - { - semaphore.Release(); - } - else - { - semaphore.Release(); - throw new TimeoutException("EfCacheL2.GetOrAddCache. Can't wait too long while getting cache"); - } - } - } - - cache = caches[tag]; - - if (cache.DateObsolete < DateTime.Now) - { - if (cache.semaphore.Wait(0)) - { - try - { - var dateObsolete = DateTime.Now + obsolete; - var dateQueryStart = DateTime.Now; - var data = await valueFactoryAsync(token); - var queryTime = DateTime.Now - dateQueryStart; - - if (dateObsolete - DateTime.Now < minCacheTime) - dateObsolete = DateTime.Now + minCacheTime; - - cache.Data = data; - cache.DateObsolete = dateObsolete; - cache.DateObsoleteTotal = dateObsolete + queryTime + minCacheTime; - } - catch - { - throw; - } - finally - { - cache.semaphore.Release(); - } - } - else if (cache.DateObsoleteTotal < DateTime.Now) - { - if (await cache.semaphore.WaitAsync(semaphoreTimeout, token)) - { - cache.semaphore.Release(); - } - else - { - cache.semaphore.Release(); - throw new TimeoutException("EfCacheL2.GetOrAddCache. Can't wait too long while getting updated cache"); - } - } - } - return cache; - } - - /// - /// Кешировать запрос в Dictionary<, >. С тегом typeof(TEntity).Name и ключом int Id - /// - /// - /// - /// - public static Dictionary FromCacheDictionary( - this IQueryable query) - where TEntity : class, AsbCloudDb.Model.IId - { - var tag = typeof(TEntity).Name; - return FromCacheDictionary(query, tag, defaultObsolescence, e => e.Id); - } - - /// - /// Кешировать запрос в Dictionary<, >. С тегом typeof(TEntity).Name - /// - /// - /// - /// - /// Делегат получения ключа из записи - /// - public static Dictionary FromCacheDictionary( - this IQueryable query, - Func keySelector) - where TEntity : class - where TKey : notnull - { - var tag = typeof(TEntity).Name; - return FromCacheDictionary(query, tag, defaultObsolescence, keySelector); - } - - /// - /// Кешировать запрос в Dictionary<, >. - /// - /// тип ключа - /// тип значения - /// - /// Метка кеша - /// Период устаревания данных - /// Делегат получения ключа из записи - /// - public static Dictionary FromCacheDictionary( - this IQueryable query, - string tag, - TimeSpan obsolescence, - Func keySelector) - where TEntity : class - where TKey : notnull - { - IDictionary factory() - => query.AsNoTracking().ToDictionary(keySelector); - var cache = GetOrAddCache(tag, factory, obsolescence); - return cache.GetData(); - } - - /// - /// Кешировать запрос с последующим преобразованием из в .
- /// Преобразование выполняется после получения из БД, результат кешируется в Dictionary<, >. - ///
- /// тип ключа - /// тип значения - /// - /// - /// Метка кеша - /// Период устаревания данных - /// Делегат получения ключа из записи - /// Преобразование данных БД в DTO - /// - public static Dictionary FromCacheDictionary( - this IQueryable query, - string tag, - TimeSpan obsolescence, - Func keySelector, - Func convert) - where TEntity : class - where TKey : notnull - { - IDictionary factory() - => query.AsNoTracking().ToDictionary(keySelector); - var cache = GetOrAddCache(tag, factory, obsolescence); - return cache.GetData(convert); - } - - /// - /// Асинхронно кешировать запрос в Dictionary<, >. С тегом typeof(TEntity).Name и ключом int Id - /// - /// - /// - /// - /// - public static Task> FromCacheDictionaryAsync( - this IQueryable query, - CancellationToken token = default) - where TEntity : class, AsbCloudDb.Model.IId - { - var tag = typeof(TEntity).Name; - return FromCacheDictionaryAsync(query, tag, defaultObsolescence, e => e.Id, token); - } - - /// - /// Асинхронно кешировать запрос в Dictionary<, >. С тегом typeof(TEntity).Name - /// - /// - /// - /// - /// Делегат получения ключа из записи - /// - /// - public static Task> FromCacheDictionaryAsync( - this IQueryable query, - Func keySelector, - CancellationToken token = default) - where TEntity : class - where TKey : notnull - { - var tag = typeof(TEntity).Name; - return FromCacheDictionaryAsync(query, tag, defaultObsolescence, keySelector, token); - } - - - /// - /// Асинхронно кешировать запрос в Dictionary<, >. - /// - /// тип ключа - /// тип значения - /// - /// Метка кеша - /// Период устаревания данных - /// Делегат получения ключа из записи - /// - /// - public static async Task> FromCacheDictionaryAsync( - this IQueryable query, - string tag, - TimeSpan obsolescence, - Func keySelector, - CancellationToken token = default) - where TEntity : class - where TKey : notnull - { - async Task factory(CancellationToken token) - => await query.AsNoTracking().ToDictionaryAsync(keySelector, token); - var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token); - return cache.GetData(); - } - - /// - /// Асинхронно кешировать запрос с последующим преобразованием из в .
- /// Преобразование выполняется после получения из БД, результат кешируется в Dictionary<, >. - ///
- /// тип ключа - /// тип значения - /// - /// - /// Метка кеша - /// Период устаревания данных - /// Делегат получения ключа из записи - /// Преобразование данных БД в DTO - /// - /// - public static async Task> FromCacheDictionaryAsync(this IQueryable query, string tag, TimeSpan obsolescence, Func keySelector, Func convert, CancellationToken token = default) - where TEntity : class - where TKey : notnull - { - async Task factory(CancellationToken token) - => await query.AsNoTracking().ToDictionaryAsync(keySelector, token); - var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token); - return cache.GetData(convert); - } - - /// - /// drops cache with tag = typeof(T).Name - /// - /// - /// - public static void DropCacheDictionary(this IQueryable query) - { - var tag = typeof(T).Name; - DropCacheDictionary(query, tag); - } - - /// - /// Очистить кеш - /// - /// - /// - /// Метка кеша - public static void DropCacheDictionary(this IQueryable query, string tag) - { - caches.Remove(tag, out var _); - } - } -#nullable disable -} diff --git a/AsbCloudInfrastructure/EfCache/EfCacheExtensions.cs b/AsbCloudInfrastructure/EfCache/EfCacheExtensions.cs index 0a93c70c..c618534d 100644 --- a/AsbCloudInfrastructure/EfCache/EfCacheExtensions.cs +++ b/AsbCloudInfrastructure/EfCache/EfCacheExtensions.cs @@ -349,7 +349,7 @@ namespace AsbCloudInfrastructure.EfCache return cache.GetData(convert); } - public static Task> FromCacheAsync(this IQueryable query, CancellationToken token = default) + public static Task> FromCacheAsync(this IQueryable query, CancellationToken token) where TEntity : class { var tag = typeof(TEntity).Name; @@ -365,7 +365,7 @@ namespace AsbCloudInfrastructure.EfCache /// Период устаревания данных /// /// - public static async Task> FromCacheAsync(this IQueryable query, string tag, TimeSpan obsolescence, CancellationToken token = default) + public static async Task> FromCacheAsync(this IQueryable query, string tag, TimeSpan obsolescence, CancellationToken token) where TEntity : class { async Task factory(CancellationToken token) @@ -386,7 +386,7 @@ namespace AsbCloudInfrastructure.EfCache /// Преобразование данных БД в DTO /// /// - public static async Task> FromCacheAsync(this IQueryable query, string tag, TimeSpan obsolescence, Func convert, CancellationToken token = default) + public static async Task> FromCacheAsync(this IQueryable query, string tag, TimeSpan obsolescence, Func convert, CancellationToken token) where TEntity : class { async Task factory(CancellationToken token) diff --git a/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs index 94142e9d..ae9c1fed 100644 --- a/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs @@ -54,7 +54,7 @@ namespace AsbCloudInfrastructure.Repository public override async Task> GetAllAsync(CancellationToken token) { var cache = await GetCacheAsync(token); - return cache.Values; + return cache; } /// @@ -65,14 +65,14 @@ namespace AsbCloudInfrastructure.Repository public override TDto? GetOrDefault(int id) { var cache = GetCache(); - return cache.GetValueOrDefault(id); + return cache.FirstOrDefault(d => d.Id == id); } /// public override async Task GetOrDefaultAsync(int id, CancellationToken token) { var cache = await GetCacheAsync(token); - return cache.GetValueOrDefault(id); + return cache.FirstOrDefault(d => d.Id == id); } /// @@ -102,17 +102,17 @@ namespace AsbCloudInfrastructure.Repository return result; } - protected virtual Task> GetCacheAsync(CancellationToken token) + protected virtual Task> GetCacheAsync(CancellationToken token) => GetQuery() - .FromCacheDictionaryAsync(CacheTag, CacheOlescence, KeySelector, Convert, token); + .FromCacheAsync(CacheTag, CacheOlescence, Convert, token); - protected virtual Dictionary GetCache() + protected virtual IEnumerable GetCache() => GetQuery() - .FromCacheDictionary(CacheTag, CacheOlescence, KeySelector, Convert); + .FromCache(CacheTag, CacheOlescence, Convert); protected virtual void DropCache() - => dbSet.DropCacheDictionary(CacheTag); + => dbSet.DropCache(CacheTag); } #nullable disable } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs index f4e60abd..77729a13 100644 --- a/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs @@ -23,25 +23,25 @@ namespace AsbCloudInfrastructure.Repository public CrudWellRelatedCacheServiceBase(IAsbCloudDbContext context, Func, IQueryable> makeQuery) : base(context, makeQuery) { } - public async Task> GetByIdWellAsync(int idWell, CancellationToken token) + public async Task?> GetByIdWellAsync(int idWell, CancellationToken token) { var cache = await GetCacheAsync(token); - var dtos = cache.Values + var dtos = cache .Where(e => e.IdWell == idWell) .ToList(); return dtos; } - public async Task> GetByIdWellAsync(IEnumerable idsWells, CancellationToken token) + public async Task?> GetByIdWellAsync(IEnumerable idsWells, CancellationToken token) { if (!idsWells.Any()) return Enumerable.Empty(); var cache = await GetCacheAsync(token); - var dtos = cache.Values + var dtos = cache .Where(e => idsWells.Contains(e.IdWell)) .ToList(); return dtos; diff --git a/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs index 49bab949..0e9a3ad8 100644 --- a/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs @@ -23,7 +23,7 @@ namespace AsbCloudInfrastructure.Repository public CrudWellRelatedServiceBase(IAsbCloudDbContext context, Func, IQueryable> makeQuery) : base(context, makeQuery) { } - public async Task> GetByIdWellAsync(int idWell, CancellationToken token) + public async Task?> GetByIdWellAsync(int idWell, CancellationToken token) { var entities = await GetQuery() .Where(e => e.IdWell == idWell) @@ -32,7 +32,7 @@ namespace AsbCloudInfrastructure.Repository return dtos; } - public async Task> GetByIdWellAsync(IEnumerable idsWells, CancellationToken token) + public async Task?> GetByIdWellAsync(IEnumerable idsWells, CancellationToken token) { if (!idsWells.Any()) return Enumerable.Empty(); diff --git a/AsbCloudInfrastructure/Services/FileCategoryService.cs b/AsbCloudInfrastructure/Services/FileCategoryService.cs index 2b30583e..b1976e64 100644 --- a/AsbCloudInfrastructure/Services/FileCategoryService.cs +++ b/AsbCloudInfrastructure/Services/FileCategoryService.cs @@ -15,15 +15,15 @@ namespace AsbCloudInfrastructure.Services : base(context) { } + //TODO: Перенести в сервис "дело скважины" public async Task> GetWellCaseCategoriesAsync(CancellationToken token) { var cache = await GetCacheAsync(token) .ConfigureAwait(false); var dtos = cache - .Where(kv => kv.Key >= 10000) - .Where(kv => kv.Key <= 20000) - .Select(kv => kv.Value); + .Where(f => f.Id >= 10000) + .Where(f => f.Id <= 20000); return dtos; } diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs index 81362ca6..af2d471a 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataCache.cs @@ -123,18 +123,12 @@ namespace AsbCloudInfrastructure.Services.SAUB isLoading = true; Well[] wells = Array.Empty(); - try - { wells = db.Set() .Include(well => well.Telemetry) .Include(well => well.Cluster) .Where(well => well.IdTelemetry != null) .ToArray(); - } - catch(Exception ex) - { - ; - } + foreach (Well well in wells) { var capacity = well.IdState == 1 diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs index 293df995..f8574152 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs @@ -47,7 +47,7 @@ namespace AsbCloudInfrastructure.Services.SAUB private void DropTelemetryCache() { - db.Telemetries.DropCacheDictionary(telemetryCacheTag); + db.Telemetries.DropCache(telemetryCacheTag); } public DateTime GetLastTelemetryDate(int idTelemetry) diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index 7107bda5..2b33d7bf 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -81,8 +81,7 @@ namespace AsbCloudInfrastructure.Services .Select(r => r.IdWell); var wellsDtos = (await GetCacheAsync(token)) - .Where(kv => wellsIds.Contains(kv.Key)) - .Select(kv => kv.Value); + .Where(w => wellsIds.Contains(w.Id)); return wellsDtos.ToList(); } @@ -95,7 +94,7 @@ namespace AsbCloudInfrastructure.Services if (dto.IdState is < 0 or > 2) throw new ArgumentInvalidException("Текущее состояние работы скважины указано неправильно.", nameof(dto)); - if (dto.Id != 0 && (await GetCacheAsync(token)).ContainsKey(dto.Id)) + if (dto.Id != 0 && (await GetCacheAsync(token)).Any(w => w.Id == dto.Id)) throw new ArgumentInvalidException($"Нельзя повторно добавить скважину с id: {dto.Id}", nameof(dto)); var entity = Convert(dto); @@ -194,7 +193,7 @@ namespace AsbCloudInfrastructure.Services var cache = await GetCacheAsync(token); - var clusterWellsIds = cache.Values + var clusterWellsIds = cache .Where((w) => w.IdCluster == well.IdCluster) .Select(w => w.Id); @@ -247,7 +246,7 @@ namespace AsbCloudInfrastructure.Services public async Task EnshureTimezonesIsSetAsync(CancellationToken token) { var cache = await GetCacheAsync(token); - if (!cache.Values.Any(w => w.Timezone is null)) + if (!cache.Any(w => w.Timezone is null)) return; var defaultTimeZone = new SimpleTimezone