From fa0389c08fc742e61953c47454badb2c39f1bd05 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Thu, 17 Nov 2022 16:09:51 +0500 Subject: [PATCH 01/14] Trying to use microsoft in memeory cache --- AsbCloudInfrastructure/DependencyInjection.cs | 7 ++- .../Repository/CrudCacheServiceBase.cs | 48 ++++++++++++++----- .../Repository/CrudServiceBase.cs | 14 ------ .../CrudWellRelatedCacheServiceBase.cs | 12 ++--- .../Repository/CrudWellRelatedServiceBase.cs | 3 -- .../Repository/SetpointsRequestRepository.cs | 5 +- .../Services/FileCategoryService.cs | 7 ++- .../Services/SAUB/SetpointsService.cs | 5 +- .../Services/Subsystems/SubsystemService.cs | 3 +- .../Services/WellService.cs | 16 ++----- 10 files changed, 62 insertions(+), 58 deletions(-) diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index 0b9c0bc6..a7bf3469 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -16,10 +16,10 @@ using AsbCloudInfrastructure.Services.SAUB; using AsbCloudInfrastructure.Services.Subsystems; using AsbCloudInfrastructure.Services.WellOperationService; using AsbCloudInfrastructure.Validators; -using DocumentFormat.OpenXml.Spreadsheet; using FluentValidation.AspNetCore; using Mapster; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using System; @@ -93,6 +93,7 @@ namespace AsbCloudInfrastructure services.AddFluentValidation(); + services.AddMemoryCache(); services.AddScoped(provider => provider.GetService()); services.AddScoped(); @@ -140,21 +141,25 @@ namespace AsbCloudInfrastructure services.AddTransient, CrudServiceBase>(s => new CrudCacheServiceBase( s.GetService(), + s.GetService(), dbSet => dbSet.Include(t => t.Well))); // может быть включен в сервис TelemetryService services.AddTransient, DrillParamsService>(); services.AddTransient, CrudCacheServiceBase>(s => new CrudCacheServiceBase( s.GetService(), + s.GetService(), dbSet => dbSet.Include(d => d.Clusters))); services.AddTransient, CrudCacheServiceBase>(s => new CrudCacheServiceBase( s.GetService(), + s.GetService(), dbSet => dbSet.Include(c => c.CompanyType))); services.AddTransient, CrudCacheServiceBase>(); services.AddTransient, CrudCacheServiceBase>(s => new CrudCacheServiceBase( s.GetService(), + s.GetService(), dbSet => dbSet .Include(c => c.Wells) .Include(c => c.Deposit))); // может быть включен в сервис ClusterService diff --git a/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs index ae9c1fed..2624d39c 100644 --- a/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudCacheServiceBase.cs @@ -1,6 +1,6 @@ using AsbCloudDb.Model; -using AsbCloudInfrastructure.EfCache; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Linq; @@ -21,16 +21,21 @@ namespace AsbCloudInfrastructure.Repository { protected string CacheTag = typeof(TDto).Name; protected TimeSpan CacheOlescence = TimeSpan.FromMinutes(5); + private readonly IMemoryCache memoryCache; + protected int KeySelector(TEntity entity) => entity.Id; - public CrudCacheServiceBase(IAsbCloudDbContext dbContext) - : base(dbContext) { } + public CrudCacheServiceBase(IAsbCloudDbContext dbContext, IMemoryCache memoryCache) + : base(dbContext) + { + this.memoryCache = memoryCache; + } - public CrudCacheServiceBase(IAsbCloudDbContext dbContext, ISet includes) - : base(dbContext, includes) { } - - public CrudCacheServiceBase(IAsbCloudDbContext dbContext, Func, IQueryable> makeQuery) - : base(dbContext, makeQuery) { } + public CrudCacheServiceBase(IAsbCloudDbContext dbContext, IMemoryCache memoryCache, Func, IQueryable> makeQuery) + : base(dbContext, makeQuery) + { + this.memoryCache = memoryCache; + } /// public override async Task InsertAsync(TDto newItem, CancellationToken token) @@ -103,16 +108,33 @@ namespace AsbCloudInfrastructure.Repository } protected virtual Task> GetCacheAsync(CancellationToken token) - => GetQuery() - .FromCacheAsync(CacheTag, CacheOlescence, Convert, token); + { + var cache = memoryCache.GetOrCreateAsync(CacheTag, async (cacheEntry) => { + cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence; + cacheEntry.SlidingExpiration = CacheOlescence; + var entities = await GetQuery().ToArrayAsync(token); + var dtos = entities.Select(Convert); + return dtos.ToArray().AsEnumerable(); + }); + return cache; + } protected virtual IEnumerable GetCache() - => GetQuery() - .FromCache(CacheTag, CacheOlescence, Convert); + { + var cache = memoryCache.GetOrCreate(CacheTag, cacheEntry => { + cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence; + cacheEntry.SlidingExpiration= CacheOlescence; + + var entities = GetQuery().ToArray(); + var dtos = entities.Select(Convert); + return dtos.ToArray(); + }); + return cache; + } protected virtual void DropCache() - => dbSet.DropCache(CacheTag); + => memoryCache.Remove(CacheTag); } #nullable disable } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Repository/CrudServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudServiceBase.cs index c6e96d09..67b750cc 100644 --- a/AsbCloudInfrastructure/Repository/CrudServiceBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudServiceBase.cs @@ -31,20 +31,6 @@ namespace AsbCloudInfrastructure.Repository GetQuery = () => dbSet; } - public CrudServiceBase(IAsbCloudDbContext dbContext, ISet includes) - { - this.dbContext = dbContext; - dbSet = dbContext.Set(); - - GetQuery = () => - { - IQueryable query = dbSet; - foreach (var include in includes) - query = query.Include(include); - return query; - }; - } - public CrudServiceBase(IAsbCloudDbContext context, Func, IQueryable> makeQuery) { dbContext = context; diff --git a/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs index 77729a13..a7eb3305 100644 --- a/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudWellRelatedCacheServiceBase.cs @@ -1,6 +1,7 @@ using AsbCloudApp.Services; using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Linq; @@ -14,14 +15,11 @@ namespace AsbCloudInfrastructure.Repository where TDto : AsbCloudApp.Data.IId, AsbCloudApp.Data.IWellRelated where TEntity : class, IId, IWellRelated { - public CrudWellRelatedCacheServiceBase(IAsbCloudDbContext context) - : base(context) { } + public CrudWellRelatedCacheServiceBase(IAsbCloudDbContext context, IMemoryCache memoryCache) + : base(context, memoryCache) { } - public CrudWellRelatedCacheServiceBase(IAsbCloudDbContext dbContext, ISet includes) - : base(dbContext, includes) { } - - public CrudWellRelatedCacheServiceBase(IAsbCloudDbContext context, Func, IQueryable> makeQuery) - : base(context, makeQuery) { } + public CrudWellRelatedCacheServiceBase(IAsbCloudDbContext context, IMemoryCache memoryCache, Func, IQueryable> makeQuery) + : base(context, memoryCache, makeQuery) { } public async Task?> GetByIdWellAsync(int idWell, CancellationToken token) { diff --git a/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs b/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs index 0e9a3ad8..b79277cc 100644 --- a/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs +++ b/AsbCloudInfrastructure/Repository/CrudWellRelatedServiceBase.cs @@ -17,9 +17,6 @@ namespace AsbCloudInfrastructure.Repository public CrudWellRelatedServiceBase(IAsbCloudDbContext context) : base(context) { } - public CrudWellRelatedServiceBase(IAsbCloudDbContext dbContext, ISet includes) - : base(dbContext, includes) { } - public CrudWellRelatedServiceBase(IAsbCloudDbContext context, Func, IQueryable> makeQuery) : base(context, makeQuery) { } diff --git a/AsbCloudInfrastructure/Repository/SetpointsRequestRepository.cs b/AsbCloudInfrastructure/Repository/SetpointsRequestRepository.cs index 3519a8e0..c9379244 100644 --- a/AsbCloudInfrastructure/Repository/SetpointsRequestRepository.cs +++ b/AsbCloudInfrastructure/Repository/SetpointsRequestRepository.cs @@ -2,6 +2,7 @@ using AsbCloudApp.Services; using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using System; namespace AsbCloudInfrastructure.Repository @@ -10,8 +11,8 @@ namespace AsbCloudInfrastructure.Repository { private readonly IWellService wellService; - public SetpointsRequestRepository(IAsbCloudDbContext dbContext, IWellService wellService) - : base(dbContext, q => q.Include(s => s.Author) + public SetpointsRequestRepository(IAsbCloudDbContext dbContext, IMemoryCache memoryCache, IWellService wellService) + : base(dbContext, memoryCache, q => q.Include(s => s.Author) .Include(s => s.Well)) { this.wellService = wellService; diff --git a/AsbCloudInfrastructure/Services/FileCategoryService.cs b/AsbCloudInfrastructure/Services/FileCategoryService.cs index b1976e64..8721ef25 100644 --- a/AsbCloudInfrastructure/Services/FileCategoryService.cs +++ b/AsbCloudInfrastructure/Services/FileCategoryService.cs @@ -2,6 +2,7 @@ using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Repository; +using Microsoft.Extensions.Caching.Memory; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -11,10 +12,8 @@ namespace AsbCloudInfrastructure.Services { public class FileCategoryService : CrudCacheServiceBase, IFileCategoryService { - public FileCategoryService(IAsbCloudDbContext context) - : base(context) - { - } + public FileCategoryService(IAsbCloudDbContext context, IMemoryCache memoryCache) + : base(context, memoryCache) { } //TODO: Перенести в сервис "дело скважины" public async Task> GetWellCaseCategoriesAsync(CancellationToken token) diff --git a/AsbCloudInfrastructure/Services/SAUB/SetpointsService.cs b/AsbCloudInfrastructure/Services/SAUB/SetpointsService.cs index ce297d83..4d9c4769 100644 --- a/AsbCloudInfrastructure/Services/SAUB/SetpointsService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/SetpointsService.cs @@ -3,6 +3,7 @@ using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Repository; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Linq; @@ -30,9 +31,9 @@ namespace AsbCloudInfrastructure.Services.SAUB private readonly SetpointsRequestRepository setpointsRepository; private readonly ITelemetryService telemetryService; - public SetpointsService(IAsbCloudDbContext db, ITelemetryService telemetryService, IWellService wellService) + public SetpointsService(IAsbCloudDbContext db, IMemoryCache memoryCache, ITelemetryService telemetryService, IWellService wellService) { - setpointsRepository = new SetpointsRequestRepository(db, wellService); + setpointsRepository = new SetpointsRequestRepository(db, memoryCache, wellService); this.telemetryService = telemetryService; } diff --git a/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs b/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs index 749dbbdf..6491265a 100644 --- a/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs +++ b/AsbCloudInfrastructure/Services/Subsystems/SubsystemService.cs @@ -6,6 +6,7 @@ using AsbCloudDb.Model.Subsystems; using AsbCloudInfrastructure.Repository; using Mapster; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -17,7 +18,7 @@ namespace AsbCloudInfrastructure.Services.Subsystems internal class SubsystemService : CrudCacheServiceBase, ISubsystemService { private readonly IWellService wellService; - public SubsystemService(IAsbCloudDbContext dbContext, IWellService wellService) : base(dbContext) + public SubsystemService(IAsbCloudDbContext dbContext, IMemoryCache memoryCache, IWellService wellService) : base(dbContext, memoryCache) { this.wellService = wellService; } diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index 2b33d7bf..7869d534 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -7,6 +7,7 @@ using AsbCloudInfrastructure.Repository; using AsbCloudInfrastructure.Services.Cache; using Mapster; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Linq; @@ -34,16 +35,16 @@ namespace AsbCloudInfrastructure.Services .Include(w => w.Telemetry) .Include(w => w.WellType) .Include(w => w.RelationCompaniesWells) - .ThenInclude(r => r.Company); + .ThenInclude(r => r.Company); - public WellService(IAsbCloudDbContext db, CacheDb cacheDb, ITelemetryService telemetryService, ITimezoneService timezoneService) - : base(db, MakeQueryWell) + public WellService(IAsbCloudDbContext db, IMemoryCache memoryCache, CacheDb cacheDb, ITelemetryService telemetryService, ITimezoneService timezoneService) + : base(db, memoryCache, MakeQueryWell) { this.telemetryService = telemetryService; this.timezoneService = timezoneService; this.wellOperationService = new WellOperationService.WellOperationService(db, cacheDb, this); - companyTypesService = new CrudCacheServiceBase(dbContext); + companyTypesService = new CrudCacheServiceBase(dbContext, memoryCache); } private IEnumerable GetCacheRelationCompanyWell() @@ -167,13 +168,6 @@ namespace AsbCloudInfrastructure.Services return dtos; } - private IEnumerable GetCompanies(int idWell) - { - var relations = GetCacheRelationCompanyWell().Where(r => r.IdWell == idWell); - var dtos = relations.Select(r => Convert(r.Company)); - return dtos; - } - public string GetStateText(int state) { return state switch From de6bbcfa7219921912023138e189a9254afb19d1 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 18 Nov 2022 12:58:53 +0500 Subject: [PATCH 02/14] =?UTF-8?q?TelemetryUserService=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=20microsoft=20cache?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudApp/Data/SAUB/TelemetryUserDto.cs | 25 +++++- AsbCloudApp/Services/ITelemetryUserService.cs | 19 +++++ .../Services/SAUB/TelemetryDataBaseService.cs | 5 +- .../Services/SAUB/TelemetryDataSaubService.cs | 29 +++---- .../Services/SAUB/TelemetryDataSpinService.cs | 8 +- .../Services/SAUB/TelemetryUserService.cs | 78 ++++++++++++++++--- .../Controllers/SAUB/TelemetryController.cs | 3 +- 7 files changed, 129 insertions(+), 38 deletions(-) diff --git a/AsbCloudApp/Data/SAUB/TelemetryUserDto.cs b/AsbCloudApp/Data/SAUB/TelemetryUserDto.cs index 5fb67dc0..ee2504e6 100644 --- a/AsbCloudApp/Data/SAUB/TelemetryUserDto.cs +++ b/AsbCloudApp/Data/SAUB/TelemetryUserDto.cs @@ -1,4 +1,6 @@ -namespace AsbCloudApp.Data.SAUB +using AsbCloudDb.Model; + +namespace AsbCloudApp.Data.SAUB { /// /// Пользователь панели оператора @@ -27,5 +29,26 @@ /// Уровень доступа /// public int Level { get; set; } + + /// + /// Собрать отображаемое имя пользователя + /// + /// + public string MakeDisplayName() + { + if (!string.IsNullOrEmpty(Surname)) + { + var s = Surname; + if (!string.IsNullOrEmpty(Name)) + { + s += $"{Name[0]}."; + if (!string.IsNullOrEmpty(Patronymic)) + s += $" {Patronymic[0]}."; + } + return s; + } + else + return $"User #{Id}"; + } } } diff --git a/AsbCloudApp/Services/ITelemetryUserService.cs b/AsbCloudApp/Services/ITelemetryUserService.cs index aa529634..558f24c5 100644 --- a/AsbCloudApp/Services/ITelemetryUserService.cs +++ b/AsbCloudApp/Services/ITelemetryUserService.cs @@ -1,15 +1,33 @@ using AsbCloudApp.Data.SAUB; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace AsbCloudApp.Services { +#nullable enable /// /// сервис пользователей телеметрии /// public interface ITelemetryUserService { + /// + /// get user by ids + /// + /// + /// + /// + TelemetryUserDto? GetOrDefault(int idTelemetry, int idUser); + + /// + /// get users by id telemetry and predicate + /// + /// + /// + /// + IEnumerable GetUsers(int idTelemetry, Func? predicate = default); + /// /// получает и сохраняет/обновляет список пользователей панели оператора /// @@ -19,4 +37,5 @@ namespace AsbCloudApp.Services /// Task UpsertAsync(string uid, IEnumerable dtos, CancellationToken token = default); } +#nullable disable } \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs index e918025a..6d67f9bc 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs @@ -19,18 +19,15 @@ namespace AsbCloudInfrastructure.Services.SAUB { protected readonly IAsbCloudDbContext db; private readonly ITelemetryService telemetryService; - protected readonly CacheTable cacheTelemetryUsers; private readonly TelemetryDataCache telemetryDataCache; public TelemetryDataBaseService( IAsbCloudDbContext db, ITelemetryService telemetryService, - TelemetryDataCache telemetryDataCache, - CacheDb cacheDb) + TelemetryDataCache telemetryDataCache) { this.db = db; this.telemetryService = telemetryService; - cacheTelemetryUsers = cacheDb.GetCachedTable((AsbCloudDbContext)db); this.telemetryDataCache = telemetryDataCache; } diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs index 10a13913..aa6067b3 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSaubService.cs @@ -1,32 +1,33 @@ using AsbCloudApp.Data.SAUB; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; -using DocumentFormat.OpenXml.Drawing.Charts; using Mapster; -using Microsoft.EntityFrameworkCore; using System.Linq; -using System.Threading; -using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.SAUB { +#nullable enable public class TelemetryDataSaubService : TelemetryDataBaseService { + private readonly ITelemetryUserService telemetryUserService; + public TelemetryDataSaubService( IAsbCloudDbContext db, ITelemetryService telemetryService, - TelemetryDataCache telemetryDataCache, - CacheDb cacheDb) - : base(db, telemetryService, telemetryDataCache, cacheDb) - { } + ITelemetryUserService telemetryUserService, + TelemetryDataCache telemetryDataCache) + : base(db, telemetryService, telemetryDataCache) + { + this.telemetryUserService = telemetryUserService; + } public override TelemetryDataSaub Convert(TelemetryDataSaubDto src, double timezoneOffset) { var entity = src.Adapt(); - var telemetryUser = cacheTelemetryUsers? - .FirstOrDefault(u => u.IdTelemetry == src.IdTelemetry && (u.Name == src.User || u.Surname == src.User)); - entity.IdUser = telemetryUser?.IdUser; + var telemetryUser = telemetryUserService + .GetUsers(src.IdTelemetry, u => (u.Name == src.User || u.Surname == src.User)) + .FirstOrDefault(); + entity.IdUser = telemetryUser?.Id; entity.DateTime = src.DateTime.ToUtcDateTimeOffset(timezoneOffset); return entity; } @@ -34,11 +35,11 @@ namespace AsbCloudInfrastructure.Services.SAUB public override TelemetryDataSaubDto Convert(TelemetryDataSaub src, double timezoneOffset) { var dto = src.Adapt(); - var telemetryUser = cacheTelemetryUsers? - .FirstOrDefault(u => u.IdTelemetry == src.IdTelemetry && u.IdUser == src.IdUser); + var telemetryUser = telemetryUserService.GetOrDefault(src.IdTelemetry, src.IdUser??int.MinValue); dto.User = telemetryUser?.MakeDisplayName(); dto.DateTime = src.DateTime.ToRemoteDateTime(timezoneOffset); return dto; } } +#nullable disable } diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSpinService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSpinService.cs index 14e84955..64862dcc 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSpinService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataSpinService.cs @@ -1,10 +1,7 @@ using AsbCloudApp.Data.SAUB; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; using Mapster; -using System.Threading; -using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.SAUB { @@ -13,9 +10,8 @@ namespace AsbCloudInfrastructure.Services.SAUB public TelemetryDataSpinService( IAsbCloudDbContext db, ITelemetryService telemetryService, - TelemetryDataCache telemetryDataCache, - CacheDb cacheDb) - : base(db, telemetryService, telemetryDataCache, cacheDb) + TelemetryDataCache telemetryDataCache) + : base(db, telemetryService, telemetryDataCache) { } public override TelemetryDataSpin Convert(TelemetryDataSpinDto src, double timezoneOffset) diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryUserService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryUserService.cs index 2406a2b0..f5740a3c 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryUserService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryUserService.cs @@ -3,7 +3,9 @@ using AsbCloudApp.Data.SAUB; using AsbCloudApp.Services; using AsbCloudDb; using AsbCloudDb.Model; -using AsbCloudInfrastructure.EfCache; +using Mapster; +using Microsoft.Extensions.Caching.Memory; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -11,15 +13,49 @@ using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.SAUB { +#nullable enable public class TelemetryUserService : ITelemetryUserService { + private const string CacheTag = "TelemetryUserCacheTag"; + private readonly TimeSpan CacheOlescence = TimeSpan.FromMinutes(5); + private readonly IAsbCloudDbContext db; private readonly ITelemetryService telemetryService; + private readonly IMemoryCache memoryCache; - public TelemetryUserService(IAsbCloudDbContext db, ITelemetryService telemetryService) + public TelemetryUserService(IAsbCloudDbContext db, + ITelemetryService telemetryService, + IMemoryCache memoryCache) { this.db = db; this.telemetryService = telemetryService; + this.memoryCache = memoryCache; + } + + public TelemetryUserDto? GetOrDefault(int idTelemetry, int idUser) + { + var entity = GetCache() + .FirstOrDefault(u => u.IdTelemetry == idTelemetry && u.IdUser == idUser); + + if(entity is null) + return null; + + return Convert(entity); + } + + public IEnumerable GetUsers(int idTelemetry, Func? predicate = null) + { + var entities = GetCache() + .Where(u => u.IdTelemetry == idTelemetry); + + foreach (var entity in entities) + { + var dto = Convert(entity); + if(predicate?.Invoke(dto)??true) + yield return dto; + } + + yield break; } public async Task UpsertAsync(string uid, IEnumerable dtos, CancellationToken token = default) @@ -29,17 +65,37 @@ namespace AsbCloudInfrastructure.Services.SAUB var telemetryId = telemetryService.GetOrCreateTelemetryIdByUid(uid); - var entities = dtos.Distinct(new TelemetryUserDtoComparer()).Select(dto => new TelemetryUser - { - IdUser = dto.Id, - IdTelemetry = telemetryId, - Level = dto.Level, - Name = dto.Name, - Patronymic = dto.Patronymic, - Surname = dto.Surname, + var entities = dtos.Distinct(new TelemetryUserDtoComparer()).Select(dto => { + var entity = dto.Adapt(); + entity.IdUser = dto.Id; + entity.IdTelemetry = telemetryId; + return entity; }); var result = await db.Database.ExecInsertOrUpdateAsync(db.TelemetryUsers, entities, token); - db.TelemetryUsers.DropCache(); + DropCache(); + } + + private IEnumerable GetCache() + { + var cache = memoryCache.GetOrCreate(CacheTag, cacheEntry => { + cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence; + cacheEntry.SlidingExpiration = CacheOlescence; + + var entities = db.Set().ToArray(); + return entities; + }); + return cache; + } + + private void DropCache() + => memoryCache.Remove(CacheTag); + + private static TelemetryUserDto Convert(TelemetryUser entity) + { + var dto = entity.Adapt(); + dto.Id = entity.IdUser; + return dto; } } +#nullable disable } diff --git a/AsbCloudWebApi/Controllers/SAUB/TelemetryController.cs b/AsbCloudWebApi/Controllers/SAUB/TelemetryController.cs index 4a77088a..f7fe0b74 100644 --- a/AsbCloudWebApi/Controllers/SAUB/TelemetryController.cs +++ b/AsbCloudWebApi/Controllers/SAUB/TelemetryController.cs @@ -1,5 +1,4 @@ -using AsbCloudApp.Data; -using AsbCloudApp.Data.SAUB; +using AsbCloudApp.Data.SAUB; using AsbCloudApp.Services; using AsbCloudWebApi.SignalR; using Microsoft.AspNetCore.Mvc; From 82063a2c223fb5b59176d2750497b26b7589fa8e Mon Sep 17 00:00:00 2001 From: "ai.astrakhantsev" Date: Fri, 18 Nov 2022 14:48:10 +0500 Subject: [PATCH 03/14] #7798026 fix --- AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs b/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs index c82b9f7a..ec417604 100644 --- a/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs +++ b/AsbCloudInfrastructure/Services/WellFinalDocumentsService.cs @@ -121,7 +121,7 @@ namespace AsbCloudInfrastructure.Services var result = new WellCaseDto { IdWell = idWell, - PermissionToSetPubliher = userRepository.HasPermission(idUser, "WellFinalDocument.editPublisher"), + PermissionToSetPubliher = userRepository.HasPermission(idUser, "WellFinalDocuments.editPublisher"), WellFinalDocuments = docs, }; return result; From 04414b3c75368333d33c11fe8d72ead597eb3bc0 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 18 Nov 2022 15:07:27 +0500 Subject: [PATCH 04/14] replace CacheDb in TelemetryTracker --- AsbCloudInfrastructure/CacheExtentions.cs | 44 +++++++++++++++++++ .../Services/SAUB/TelemetryService.cs | 8 ++-- .../Services/SAUB/TelemetryTracker.cs | 5 ++- 3 files changed, 51 insertions(+), 6 deletions(-) create mode 100644 AsbCloudInfrastructure/CacheExtentions.cs diff --git a/AsbCloudInfrastructure/CacheExtentions.cs b/AsbCloudInfrastructure/CacheExtentions.cs new file mode 100644 index 00000000..28e791e9 --- /dev/null +++ b/AsbCloudInfrastructure/CacheExtentions.cs @@ -0,0 +1,44 @@ +using AsbCloudDb.Model; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace AsbCloudInfrastructure +{ + public static class MemoryCacheExtentions + { + private static readonly TimeSpan CacheOlescence = TimeSpan.FromMinutes(5); + + public static Task> GetOrCreateBasicAsync(this IMemoryCache memoryCache, IAsbCloudDbContext dbContext, CancellationToken token) + where T : class + { + var cacheTag = typeof(T).FullName; + var cache = memoryCache.GetOrCreateAsync(cacheTag, async (cacheEntry) => { + cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence; + cacheEntry.SlidingExpiration = CacheOlescence; + + var entities = await dbContext.Set().ToArrayAsync(token); + return entities.AsEnumerable(); + }); + return cache; + } + + public static IEnumerable GetOrCreateBasic(this IMemoryCache memoryCache, IAsbCloudDbContext dbContext) + where T : class + { + var cacheTag = typeof(T).FullName; + var cache = memoryCache.GetOrCreate(cacheTag, cacheEntry => { + cacheEntry.AbsoluteExpirationRelativeToNow = CacheOlescence; + cacheEntry.SlidingExpiration = CacheOlescence; + + var entities = dbContext.Set().ToArray(); + return entities.AsEnumerable(); + }); + return cache; + } + } +} diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs index f8574152..b2e15216 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryService.cs @@ -17,7 +17,7 @@ namespace AsbCloudInfrastructure.Services.SAUB #nullable enable public class TelemetryService : ITelemetryService { - private const string telemetryCacheTag = "telemetryCache"; + private const string CacheTag = "TelemetryCache"; private static readonly TimeSpan telemetryCacheObsolescence = TimeSpan.FromMinutes(5); private readonly IAsbCloudDbContext db; @@ -39,15 +39,15 @@ namespace AsbCloudInfrastructure.Services.SAUB private IEnumerable GetTelemetryCache() { - var cache = db.Telemetries + var cache = db.Set() .Include(t => t.Well) - .FromCache(telemetryCacheTag, telemetryCacheObsolescence); + .FromCache(CacheTag, telemetryCacheObsolescence); return cache; } private void DropTelemetryCache() { - db.Telemetries.DropCache(telemetryCacheTag); + db.Telemetries.DropCache(CacheTag); } public DateTime GetLastTelemetryDate(int idTelemetry) diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs index 99dce9d6..b6104e57 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs @@ -3,6 +3,7 @@ using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Services.Cache; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using System; using System.Collections.Concurrent; @@ -39,14 +40,14 @@ namespace AsbCloudInfrastructure.Services.SAUB private readonly ConcurrentDictionary telemetriesStats; - public TelemetryTracker(CacheDb cacheDb, IConfiguration configuration) + public TelemetryTracker(IConfiguration configuration, IMemoryCache memoryCache) { var contextOptions = new DbContextOptionsBuilder() .UseNpgsql(configuration.GetConnectionString("DefaultConnection")) .Options; var db = new AsbCloudDbContext(contextOptions); - var cacheTelemetry = cacheDb.GetCachedTable(db); + var cacheTelemetry = memoryCache.GetOrCreateBasic(db); var keyValuePairs = new Dictionary(cacheTelemetry.Count()); foreach (var telemetry in cacheTelemetry) { From dcecfe4dc8a9028c0690289aa01fb6d17134192e Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 18 Nov 2022 15:17:04 +0500 Subject: [PATCH 05/14] replace CacheDb in WellOperationService by ms.MemoryCache --- .../WellOperationService.cs | 22 ++++++++++--------- .../Services/WellService.cs | 2 +- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs index 3b1ef55a..f1ae1144 100644 --- a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs +++ b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs @@ -1,9 +1,9 @@ using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; using Mapster; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Linq; @@ -16,9 +16,8 @@ namespace AsbCloudInfrastructure.Services.WellOperationService public class WellOperationService : IWellOperationService { private readonly IAsbCloudDbContext db; + private readonly IMemoryCache memoryCache; private readonly IWellService wellService; - private readonly CacheTable cachedOperationCategories; - private readonly CacheTable cachedSectionTypes; private Dictionary? firstOperationsCache = null; @@ -33,22 +32,25 @@ namespace AsbCloudInfrastructure.Services.WellOperationService public const int idOperationTypeFact = 1; public const int idOperationTypeRepair = 1031; - public WellOperationService(IAsbCloudDbContext db, CacheDb cache, IWellService wellService) + public WellOperationService(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService) { this.db = db; + this.memoryCache = memoryCache; this.wellService = wellService; - cachedOperationCategories = cache.GetCachedTable((DbContext)db); - cachedSectionTypes = cache.GetCachedTable((DbContext)db); } public IDictionary GetSectionTypes() - => cachedSectionTypes.ToDictionary(s => s.Id, s => s.Caption); + => memoryCache + .GetOrCreateBasic(db) + .ToDictionary(s => s.Id, s => s.Caption); public IEnumerable GetCategories() { - var operationTypes = cachedOperationCategories - .Distinct().OrderBy(o => o.Name); - var result = operationTypes.Adapt>(); + var operationCategories = memoryCache + .GetOrCreateBasic(db) + .Distinct() + .OrderBy(o => o.Name); + var result = operationCategories.Adapt>(); return result; } diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index 7869d534..621dc3b3 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -43,7 +43,7 @@ namespace AsbCloudInfrastructure.Services this.telemetryService = telemetryService; this.timezoneService = timezoneService; - this.wellOperationService = new WellOperationService.WellOperationService(db, cacheDb, this); + this.wellOperationService = new WellOperationService.WellOperationService(db, memoryCache, this); companyTypesService = new CrudCacheServiceBase(dbContext, memoryCache); } From ede40c9a231e671fc352656aedaaf98f8476e654 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 18 Nov 2022 15:17:04 +0500 Subject: [PATCH 06/14] replace CacheDb in WellOperationService by ms.MemoryCache --- .../WellOperationService.cs | 22 ++++++++++--------- .../Services/WellService.cs | 4 ++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs index 3b1ef55a..f1ae1144 100644 --- a/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs +++ b/AsbCloudInfrastructure/Services/WellOperationService/WellOperationService.cs @@ -1,9 +1,9 @@ using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; using Mapster; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Linq; @@ -16,9 +16,8 @@ namespace AsbCloudInfrastructure.Services.WellOperationService public class WellOperationService : IWellOperationService { private readonly IAsbCloudDbContext db; + private readonly IMemoryCache memoryCache; private readonly IWellService wellService; - private readonly CacheTable cachedOperationCategories; - private readonly CacheTable cachedSectionTypes; private Dictionary? firstOperationsCache = null; @@ -33,22 +32,25 @@ namespace AsbCloudInfrastructure.Services.WellOperationService public const int idOperationTypeFact = 1; public const int idOperationTypeRepair = 1031; - public WellOperationService(IAsbCloudDbContext db, CacheDb cache, IWellService wellService) + public WellOperationService(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService) { this.db = db; + this.memoryCache = memoryCache; this.wellService = wellService; - cachedOperationCategories = cache.GetCachedTable((DbContext)db); - cachedSectionTypes = cache.GetCachedTable((DbContext)db); } public IDictionary GetSectionTypes() - => cachedSectionTypes.ToDictionary(s => s.Id, s => s.Caption); + => memoryCache + .GetOrCreateBasic(db) + .ToDictionary(s => s.Id, s => s.Caption); public IEnumerable GetCategories() { - var operationTypes = cachedOperationCategories - .Distinct().OrderBy(o => o.Name); - var result = operationTypes.Adapt>(); + var operationCategories = memoryCache + .GetOrCreateBasic(db) + .Distinct() + .OrderBy(o => o.Name); + var result = operationCategories.Adapt>(); return result; } diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index 7869d534..f4dc40de 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -37,13 +37,13 @@ namespace AsbCloudInfrastructure.Services .Include(w => w.RelationCompaniesWells) .ThenInclude(r => r.Company); - public WellService(IAsbCloudDbContext db, IMemoryCache memoryCache, CacheDb cacheDb, ITelemetryService telemetryService, ITimezoneService timezoneService) + public WellService(IAsbCloudDbContext db, IMemoryCache memoryCache, ITelemetryService telemetryService, ITimezoneService timezoneService) : base(db, memoryCache, MakeQueryWell) { this.telemetryService = telemetryService; this.timezoneService = timezoneService; - this.wellOperationService = new WellOperationService.WellOperationService(db, cacheDb, this); + this.wellOperationService = new WellOperationService.WellOperationService(db, memoryCache, this); companyTypesService = new CrudCacheServiceBase(dbContext, memoryCache); } From e837baf5e7d9c1884140543545a46a92265edd31 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 18 Nov 2022 15:25:38 +0500 Subject: [PATCH 07/14] replace CacheDb in OperationsStatService by IMemoryCache --- .../OperationsStatService.cs | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs b/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs index 1eeb1e3a..dd85deef 100644 --- a/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs +++ b/AsbCloudInfrastructure/Services/WellOperationService/OperationsStatService.cs @@ -1,9 +1,9 @@ using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; using Mapster; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using System; using System.Collections.Generic; using System.Linq; @@ -15,18 +15,14 @@ namespace AsbCloudInfrastructure.Services.WellOperationService public class OperationsStatService : IOperationsStatService { private readonly IAsbCloudDbContext db; + private readonly IMemoryCache memoryCache; private readonly IWellService wellService; - private readonly CacheTable cacheSectionsTypes; - private readonly CacheTable cacheWellType; - private readonly CacheTable cacheCluster; - public OperationsStatService(IAsbCloudDbContext db, CacheDb cache, IWellService wellService) + public OperationsStatService(IAsbCloudDbContext db, IMemoryCache memoryCache, IWellService wellService) { this.db = db; + this.memoryCache = memoryCache; this.wellService = wellService; - cacheSectionsTypes = cache.GetCachedTable((DbContext)db); - cacheWellType = cache.GetCachedTable((DbContext)db); - cacheCluster = cache.GetCachedTable((DbContext)db); } public async Task GetStatClusterAsync(int idCluster, int idCompany, CancellationToken token = default) @@ -44,7 +40,9 @@ namespace AsbCloudInfrastructure.Services.WellOperationService var statsWells = await GetWellsStatAsync(wells, token).ConfigureAwait(false); - var cluster = await cacheCluster.FirstOrDefaultAsync(c => c.Id == idCluster, token); + var cluster = (await memoryCache + .GetOrCreateBasicAsync(db, token)) + .FirstOrDefault(c => c.Id == idCluster); var statClusterDto = new StatClusterDto { Id = idCluster, @@ -133,7 +131,9 @@ namespace AsbCloudInfrastructure.Services.WellOperationService private async Task CalcWellStatAsync(Well well, CancellationToken token = default) { - var wellType = await cacheWellType.FirstOrDefaultAsync(t => t.Id == well.IdWellType, token); + var wellType = (await memoryCache + .GetOrCreateBasicAsync(db, token)) + .FirstOrDefault(t => t.Id == well.IdWellType); var statWellDto = new StatWellDto() { Id = well.Id, @@ -169,7 +169,8 @@ namespace AsbCloudInfrastructure.Services.WellOperationService .Select(o => o.IdWellSectionType) .Distinct(); - var sectionTypes = cacheSectionsTypes + var sectionTypes = memoryCache + .GetOrCreateBasic(db) .Where(s => sectionTypeIds.Contains(s.Id)) .ToDictionary(s => s.Id); From b2844cd5b2a491c73b64caacbec4ebbf248dd1fa Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 18 Nov 2022 15:32:11 +0500 Subject: [PATCH 08/14] remove CacheDB from DI and project --- AsbCloudInfrastructure/DependencyInjection.cs | 4 +- .../Services/Cache/CacheDb.cs | 50 -- .../Services/Cache/CacheTable.cs | 449 ------------------ .../Services/Cache/CacheTableDataStore.cs | 17 - .../Services/SAUB/TelemetryDataBaseService.cs | 1 - .../Services/SAUB/TelemetryTracker.cs | 1 - .../Services/WellService.cs | 1 - 7 files changed, 1 insertion(+), 522 deletions(-) delete mode 100644 AsbCloudInfrastructure/Services/Cache/CacheDb.cs delete mode 100644 AsbCloudInfrastructure/Services/Cache/CacheTable.cs delete mode 100644 AsbCloudInfrastructure/Services/Cache/CacheTableDataStore.cs diff --git a/AsbCloudInfrastructure/DependencyInjection.cs b/AsbCloudInfrastructure/DependencyInjection.cs index b55581ce..0fbad38d 100644 --- a/AsbCloudInfrastructure/DependencyInjection.cs +++ b/AsbCloudInfrastructure/DependencyInjection.cs @@ -8,7 +8,6 @@ using AsbCloudDb.Model; using AsbCloudDb.Model.Subsystems; using AsbCloudInfrastructure.Repository; using AsbCloudInfrastructure.Services; -using AsbCloudInfrastructure.Services.Cache; using AsbCloudInfrastructure.Services.DailyReport; using AsbCloudInfrastructure.Services.DetectOperations; using AsbCloudInfrastructure.Services.DrillingProgram; @@ -100,8 +99,7 @@ namespace AsbCloudInfrastructure services.AddHostedService(); services.AddHostedService(); - services.AddSingleton(new WitsInfoService()); - services.AddSingleton(new CacheDb()); + services.AddSingleton(new WitsInfoService()); services.AddSingleton(new InstantDataRepository()); services.AddSingleton(provider=> TelemetryDataCache.GetInstance(configuration)); services.AddSingleton(provider=> TelemetryDataCache.GetInstance(configuration)); diff --git a/AsbCloudInfrastructure/Services/Cache/CacheDb.cs b/AsbCloudInfrastructure/Services/Cache/CacheDb.cs deleted file mode 100644 index f8f56f0c..00000000 --- a/AsbCloudInfrastructure/Services/Cache/CacheDb.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; - -namespace AsbCloudInfrastructure.Services.Cache -{ - public class CacheDb - { - private readonly ConcurrentDictionary cache = - new ConcurrentDictionary(); - - public CacheTable GetCachedTable(DbContext context, params string[] includes) - where TEntity : class - => GetCachedTable(context, new SortedSet(includes)); - - public CacheTable GetCachedTable(DbContext context, ISet includes = null) - where TEntity : class - { - var cacheItem = GetCacheTableDataStore(); - var tableCache = new CacheTable(context, cacheItem, includes); - return tableCache; - } - - public CacheTable GetCachedTable(DbContext context, Func, IQueryable> configureDbSet) - where TEntity : class - { - var cacheItem = GetCacheTableDataStore(); - var tableCache = new CacheTable(context, cacheItem, configureDbSet); - return tableCache; - } - - private CacheTableDataStore GetCacheTableDataStore() - where TEntity : class - { - var nameOfTEntity = typeof(TEntity).FullName; - var cacheItem = cache.GetOrAdd(nameOfTEntity, (nameOfTEntity) => new CacheTableDataStore - { - NameOfTEntity = nameOfTEntity, - Entities = new List(), - }); - return cacheItem; - } - - public void DropAll() => cache.Clear(); - - public void Drop() => cache.Remove(typeof(TEntity).FullName, out _); - } -} diff --git a/AsbCloudInfrastructure/Services/Cache/CacheTable.cs b/AsbCloudInfrastructure/Services/Cache/CacheTable.cs deleted file mode 100644 index 46ff4572..00000000 --- a/AsbCloudInfrastructure/Services/Cache/CacheTable.cs +++ /dev/null @@ -1,449 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace AsbCloudInfrastructure.Services.Cache -{ - public class CacheTable : IEnumerable - where TEntity : class - { - private const int semaphoreTimeout = 5_000; - private static readonly SemaphoreSlim semaphore = new(1); - private static readonly TimeSpan minPeriodRefresh = TimeSpan.FromSeconds(5); - private static readonly string nameOfTEntity = typeof(TEntity).Name; - - private readonly CacheTableDataStore data; - private readonly Func, IQueryable> configureDbSet; - private readonly List cached; - private readonly DbContext context; - private readonly DbSet dbSet; - - internal CacheTable(DbContext context, CacheTableDataStore data, ISet includes = null) - { - this.context = context; - this.data = data; - dbSet = context.Set(); - - if (includes?.Any() == true) - configureDbSet = (DbSet dbSet) => - { - IQueryable result = dbSet; - foreach (var include in includes) - result = result.Include(include); - return result; - }; - - cached = (List)data.Entities; - if ((cached.Count == 0) || data.IsObsolete) - Refresh(false); - } - - internal CacheTable(DbContext context, CacheTableDataStore data, - Func, IQueryable> configureDbSet = null) - { - this.context = context; - this.data = data; - this.configureDbSet = configureDbSet; - - dbSet = context.Set(); - - cached = (List)data.Entities; - if ((cached.Count == 0) || data.IsObsolete) - Refresh(false); - } - - public TEntity this[int index] - { - get => cached.ElementAt(index); - } - - /// - /// Runs action like atomic operation. - /// wasFree is action argument indicates that semaphore was free. - /// It may be needed to avoid multiple operations like Refresh(). - /// - /// (wasFree) => {...} - /// default if semaphoreTimeout. Or result of func(..) - private static T Sync(Func func) - { - var wasFree = semaphore.CurrentCount > 0; - T result = default; - if (func is null || !semaphore.Wait(semaphoreTimeout)) - return result; - - try - { - result = func.Invoke(wasFree); - } - catch (Exception ex) - { - Trace.WriteLine($"{DateTime.Now:yyyy.MM.dd HH:mm:ss:fff} error in CacheTable<{nameOfTEntity}>.Sync()"); - Trace.WriteLine(ex.Message); - Trace.WriteLine(ex.StackTrace); - } - finally - { - semaphore.Release(); - } - - return result; - } - - /// - /// Runs action like atomic operation. - /// wasFree is action argument indicates that semaphore was free. - /// It may be needed to avoid multiple operations like Refresh(). - /// - /// (wasFree) => {...} - /// default if semaphoreTimeout. Or result of func(..) - private static async Task SyncAsync(Func> funcAsync, - CancellationToken token = default) - { - var wasFree = semaphore.CurrentCount > 0; - T result = default; - - if (funcAsync is null || !await semaphore.WaitAsync(semaphoreTimeout, token).ConfigureAwait(false)) - return result; - - try - { - result = await funcAsync.Invoke(wasFree, token); - } - catch (Exception ex) - { - Trace.WriteLine( - $"{DateTime.Now:yyyy.MM.dd HH:mm:ss:fff} error in CacheTable<{nameOfTEntity}>.SyncAsync()"); - Trace.WriteLine(ex.Message); - Trace.WriteLine(ex.StackTrace); - } - finally - { - semaphore.Release(); - } - - return result; - } - - private int InternalRefresh(bool force) - { - if (force || data.LastResreshDate + minPeriodRefresh < DateTime.Now) - { - cached.Clear(); - IQueryable query = configureDbSet is null ? dbSet : configureDbSet(dbSet); - var entities = query.AsNoTracking().ToList(); - //Trace.WriteLine($"CacheTable<{nameOfTEntity}> refresh"); - cached.AddRange(entities); - data.LastResreshDate = DateTime.Now; - } - - return cached.Count; - } - - private async Task InternalRefreshAsync(bool force, CancellationToken token = default) - { - if (force || data.LastResreshDate + minPeriodRefresh < DateTime.Now) - { - cached.Clear(); - IQueryable query = configureDbSet is null ? dbSet : configureDbSet(dbSet); - var entities = await query.AsNoTracking() - .ToListAsync(token).ConfigureAwait(false); - //Trace.WriteLine($"CacheTable<{nameOfTEntity}> refreshAsync"); - cached.AddRange(entities); - data.LastResreshDate = DateTime.Now; - } - - return cached.Count; - } - - public int Refresh(bool force) - => Sync((wasFree) => wasFree ? InternalRefresh(force) : 0); - - public Task RefreshAsync(bool force, CancellationToken token = default) - { - return SyncAsync( - async (wasFree, token) => - { - return wasFree ? await InternalRefreshAsync(force, token) : 0; - }, token); - } - - public bool Contains(Func predicate) - => FirstOrDefault(predicate) != default; - - public async Task ContainsAsync(Func predicate, CancellationToken token = default) - => await FirstOrDefaultAsync(predicate, token) != default; - - public TEntity GetOrCreate(Func predicate, Func makeNew) - => Sync(wasFree => - { - var result = cached.FirstOrDefault(predicate); - if (result != default) - return result; - - InternalRefresh(true); - result = cached.FirstOrDefault(predicate); - if (result != default) - return result; - - var entry = dbSet.Add(makeNew()); - context.SaveChanges(); - InternalRefresh(true); - return entry.Entity; - }); - - public TEntity FirstOrDefault() - { - var result = cached.FirstOrDefault(); - if (result != default) - return result; - - Refresh(false); - return cached.FirstOrDefault(); - } - - public async Task FirstOrDefaultAsync(CancellationToken token = default) - { - var result = cached.FirstOrDefault(); - if (result != default) - return result; - - await RefreshAsync(false, token); - return cached.FirstOrDefault(); - } - - public TEntity FirstOrDefault(Func predicate) - { - var result = cached.FirstOrDefault(predicate); - if (result != default) - return result; - - Refresh(false); - return cached.FirstOrDefault(predicate); - } - - public async Task FirstOrDefaultAsync(Func predicate, CancellationToken token = default) - { - var result = cached.FirstOrDefault(predicate); - if (result != default) - return result; - - await RefreshAsync(false, token); - return cached.FirstOrDefault(predicate); - } - - public IEnumerable Where(Func predicate = default) - { - var result = (predicate != default) - ? cached.Where(predicate) - : cached; - if (result.Any()) - return result; - - Refresh(false); - result = (predicate != default) - ? cached.Where(predicate) - : cached; - return result; - } - - public Task> WhereAsync(CancellationToken token = default) => - WhereAsync(default, token); - - public async Task> WhereAsync(Func predicate = default, - CancellationToken token = default) - { - var result = (predicate != default) - ? cached.Where(predicate) - : cached; - if (result.Any()) - return result; - - await RefreshAsync(false, token); - result = (predicate != default) - ? cached.Where(predicate) - : cached; - return result; - } - - public int Upsert(TEntity entity) - { - if (entity == default) - return 0; - return Sync((wasFree) => - { - if (dbSet.Contains(entity)) - dbSet.Update(entity); - else - dbSet.Add(entity); - var affected = context.SaveChanges(); - if (affected > 0) - InternalRefresh(true); - return affected; - }); - } - - public Task UpsertAsync(TEntity entity, CancellationToken token = default) - => SyncAsync(async (wasFree, token) => - { - if (dbSet.Contains(entity)) - dbSet.Update(entity); - else - dbSet.Add(entity); - var affected = await context.SaveChangesAsync(token).ConfigureAwait(false); - if (affected > 0) - await InternalRefreshAsync(true, token); - return affected; - }, token); - - public int Upsert(IEnumerable entities) - { - if (!entities.Any()) - return 0; - - return Sync((wasFree) => - { - foreach (var entity in entities) - { - if (dbSet.Contains(entity)) // TODO: это очень медленно - dbSet.Update(entity); - else - dbSet.Add(entity); - } - - var affected = context.SaveChanges(); - if (affected > 0) - InternalRefresh(true); - return affected; - }); - } - - public Task UpsertAsync(IEnumerable entities, CancellationToken token = default) - { - if (!entities.Any()) - return Task.FromResult(0); - - return SyncAsync(async (wasFree, token) => - { - var upsertedEntries = new List(entities.Count()); - foreach (var entity in entities) - { - if (dbSet.Contains(entity)) - dbSet.Update(entity); - else - dbSet.Add(entity); - } - - var affected = await context.SaveChangesAsync(token).ConfigureAwait(false); - if (affected > 0) - await InternalRefreshAsync(true, token); - return affected; - }, token); - } - - public int Remove(Func predicate) - => Sync(_ => - { - dbSet.RemoveRange(dbSet.Where(predicate)); - var affected = context.SaveChanges(); - if (affected > 0) - InternalRefresh(true); - return affected; - }); - - public Task RemoveAsync(Func predicate, CancellationToken token = default) - => SyncAsync(async (wasFree, token) => - { - dbSet.RemoveRange(dbSet.Where(predicate)); - var affected = await context.SaveChangesAsync(token).ConfigureAwait(false); - if (affected > 0) - await InternalRefreshAsync(true, token); - return affected; - }, token); - - public TEntity Insert(TEntity entity) - { - return Sync(_ => - { - var entry = dbSet.Add(entity); - var affected = context.SaveChanges(); - if (affected > 0) - InternalRefresh(true); - return entry.Entity; - }); - } - - public Task InsertAsync(TEntity entity, CancellationToken token = default) - { - return SyncAsync(async (wasFree, token) => - { - var entry = dbSet.Add(entity); - var affected = await context.SaveChangesAsync(token).ConfigureAwait(false); - if (affected > 0) - await InternalRefreshAsync(true, token); - return entry.Entity; - }, token); - } - - public IEnumerable Insert(IEnumerable newEntities) - { - if (newEntities is null) - return null; - var count = newEntities.Count(); - if (count == 0) - return null; - - return Sync(_ => - { - var entries = new List>(count); - foreach (var newEntity in newEntities) - { - var entry = dbSet.Add(newEntity); - entries.Add(entry); - } - - var affected = context.SaveChanges(); - if (affected > 0) - InternalRefresh(true); - else - return null; - - return entries.Select(e => e.Entity); - }); - } - - public Task> InsertAsync(IEnumerable newEntities, CancellationToken token = default) - { - if (newEntities is null) - return null; - var count = newEntities.Count(); - if (count == 0) - return null; - - return SyncAsync(async (wasFree, token) => - { - var entries = new List>(count); - foreach (var newEntity in newEntities) - { - var entry = dbSet.Add(newEntity); - entries.Add(entry); - } - var affected = await context.SaveChangesAsync(token).ConfigureAwait(false); - if (affected > 0) - await InternalRefreshAsync(true, token); - else - return null; - - return entries.Select(e => e.Entity); - }, token); - } - - public IEnumerator GetEnumerator() => Where().GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} \ No newline at end of file diff --git a/AsbCloudInfrastructure/Services/Cache/CacheTableDataStore.cs b/AsbCloudInfrastructure/Services/Cache/CacheTableDataStore.cs deleted file mode 100644 index 3c2fad37..00000000 --- a/AsbCloudInfrastructure/Services/Cache/CacheTableDataStore.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections; - -namespace AsbCloudInfrastructure.Services.Cache -{ - class CacheTableDataStore - { - public string NameOfTEntity { get; set; } - public DateTime LastResreshDate { get; set; } - - //public ISet Includes { get; set; } //TODO: this prop change should update entities - public IEnumerable Entities { get; set; } - public TimeSpan ObsolesenceTime { get; set; } = TimeSpan.FromMinutes(15); - public bool IsObsolete => (DateTime.Now - LastResreshDate > ObsolesenceTime); - - } -} diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs index 6d67f9bc..941bc012 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryDataBaseService.cs @@ -1,7 +1,6 @@ using AsbCloudApp.Services; using AsbCloudDb; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; diff --git a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs index b6104e57..190094a1 100644 --- a/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs +++ b/AsbCloudInfrastructure/Services/SAUB/TelemetryTracker.cs @@ -1,7 +1,6 @@ using AsbCloudApp.Data; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index f4dc40de..a1827851 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -4,7 +4,6 @@ using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.EfCache; using AsbCloudInfrastructure.Repository; -using AsbCloudInfrastructure.Services.Cache; using Mapster; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; From 342f5845f47a3c65aaa7189ac8eeb780f4631413 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 18 Nov 2022 15:49:04 +0500 Subject: [PATCH 09/14] remove CacheDb from ConsoleApp1 --- ConsoleApp1/ServiceFactory.cs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/ConsoleApp1/ServiceFactory.cs b/ConsoleApp1/ServiceFactory.cs index a1fb9d91..bf7c9a42 100644 --- a/ConsoleApp1/ServiceFactory.cs +++ b/ConsoleApp1/ServiceFactory.cs @@ -1,9 +1,9 @@ using AsbCloudDb.Model; using AsbCloudInfrastructure.Services; -using AsbCloudInfrastructure.Services.Cache; using AsbCloudInfrastructure.Services.SAUB; using AsbCloudInfrastructure.Services.WellOperationService; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Primitives; using System.Collections.Generic; @@ -41,19 +41,20 @@ namespace ConsoleApp1 internal static class ServiceFactory { - private static DbContextOptions options = new DbContextOptionsBuilder() + private static readonly DbContextOptions options = new DbContextOptionsBuilder() .UseNpgsql("Host=localhost;Database=postgres;Username=postgres;Password=q;Persist Security Info=True") .Options; - static CacheDb CacheDb { get; } = new CacheDb(); static ConfigurationService ConfigurationService { get; } = new ConfigurationService(); static TimezoneService TimezoneService { get; } = new TimezoneService(); public static AsbCloudDbContext Context { get; } = MakeContext(); public static AsbCloudDbContext MakeContext() => MakeContext(options); + public static IMemoryCache memoryCache = new MemoryCache(new MemoryCacheOptions()); + public static AsbCloudDbContext MakeContext(DbContextOptions options) - => new AsbCloudDbContext(options); + => new (options); public static AsbCloudDbContext MakeContext(string cusomConnectionString) => MakeContext(new DbContextOptionsBuilder().UseNpgsql(cusomConnectionString).Options); @@ -62,21 +63,21 @@ namespace ConsoleApp1 => AsbCloudInfrastructure.DependencyInjection.MapsterSetup(); public static TelemetryTracker MakeTelemetryTracker() - => new TelemetryTracker(CacheDb, ConfigurationService); + => new (ConfigurationService, memoryCache); public static TelemetryService MakeTelemetryService() - => new TelemetryService(Context, MakeTelemetryTracker(), TimezoneService); + => new (Context, MakeTelemetryTracker(), TimezoneService); public static WellService MakeWellService() - => new WellService(Context, CacheDb, MakeTelemetryService(), TimezoneService); + => new (Context, memoryCache, MakeTelemetryService(), TimezoneService); public static WellOperationService MakeWellOperationsService() - => new WellOperationService(Context, CacheDb, MakeWellService()); + => new (Context, memoryCache, MakeWellService()); public static OperationsStatService MakeOperationsStatService() - => new OperationsStatService(Context, CacheDb, MakeWellService()); + => new (Context, memoryCache, MakeWellService()); public static ScheduleReportService MakeScheduleReportService() - => new ScheduleReportService(MakeOperationsStatService(), MakeWellService()); + => new(MakeOperationsStatService(), MakeWellService()); } } From 69971a1004458b93adee6285528c718294d60d4f Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 18 Nov 2022 16:13:32 +0500 Subject: [PATCH 10/14] remove CacheDB from tests --- .../ServicesTests/DepositCrudCacheServiceTest.cs | 2 +- .../ServicesTests/DetectedOperationServiceTest.cs | 3 --- .../ServicesTests/DrillerServiceTest.cs | 5 ----- .../ServicesTests/EventServiceTest.cs | 3 --- .../ServicesTests/FileCategoryServiceTest.cs | 6 ++---- .../ServicesTests/TelemetryDataSaubServiceTest.cs | 11 +++++------ AsbCloudWebApi.Tests/TestHelpter.cs | 3 +++ 7 files changed, 11 insertions(+), 22 deletions(-) diff --git a/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs index 91664385..e95c7cc3 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs @@ -22,7 +22,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests protected override ICrudService MakeService() { var dbContext = TestHelpter.MakeRealTestContext(); - return new CrudCacheServiceBase(dbContext); + return new CrudCacheServiceBase(dbContext, TestHelpter.MemoryCache); } } } diff --git a/AsbCloudWebApi.Tests/ServicesTests/DetectedOperationServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DetectedOperationServiceTest.cs index f341a15a..00bca2aa 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DetectedOperationServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DetectedOperationServiceTest.cs @@ -3,8 +3,6 @@ using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Repository; -using AsbCloudInfrastructure.Services; -using AsbCloudInfrastructure.Services.Cache; using AsbCloudInfrastructure.Services.DetectOperations; using Moq; using System; @@ -19,7 +17,6 @@ namespace AsbCloudWebApi.Tests.ServicesTests public class DetectedOperationServiceTest { private readonly AsbCloudDbContext context; - private readonly CacheDb cacheDb; private readonly DetectedOperationService service; private readonly DetectedOperationRequest request; private Deposit deposit = new Deposit { Id = 1, Caption = "Депозит 1" }; diff --git a/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs index 7c1e60ff..f829c76c 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs @@ -1,10 +1,5 @@ using AsbCloudApp.Data; -using AsbCloudApp.Repositories; using AsbCloudApp.Services; -using AsbCloudDb.Model; -using AsbCloudInfrastructure.Repository; -using AsbCloudInfrastructure.Services; -using AsbCloudInfrastructure.Services.Cache; using Moq; using System.Collections.Generic; using System.Linq; diff --git a/AsbCloudWebApi.Tests/ServicesTests/EventServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/EventServiceTest.cs index 0c406f1e..b13cab4e 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/EventServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/EventServiceTest.cs @@ -1,7 +1,6 @@ using AsbCloudApp.Data.SAUB; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; using AsbCloudInfrastructure.Services.SAUB; using Moq; using System.Collections.Generic; @@ -14,13 +13,11 @@ namespace AsbCloudWebApi.Tests.ServicesTests; public class EventServiceTest { private readonly AsbCloudDbContext context; - private readonly CacheDb cacheDb; private readonly EventService service; public EventServiceTest() { context = TestHelpter.MakeRealTestContext(); - cacheDb = new CacheDb(); var telemetryTracker = new Mock(); var imezoneServiceMock = new Mock(); var telemetryService = new TelemetryService(context, telemetryTracker.Object, imezoneServiceMock.Object); diff --git a/AsbCloudWebApi.Tests/ServicesTests/FileCategoryServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/FileCategoryServiceTest.cs index 345f2693..12874dea 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/FileCategoryServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/FileCategoryServiceTest.cs @@ -1,7 +1,5 @@ -using AsbCloudApp.Data; -using AsbCloudDb.Model; +using AsbCloudDb.Model; using AsbCloudInfrastructure.Services; -using AsbCloudInfrastructure.Services.Cache; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -19,7 +17,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests context = TestHelpter.MakeRealTestContext(); context.SaveChanges(); - service = new FileCategoryService(context); + service = new FileCategoryService(context, TestHelpter.MemoryCache); } ~FileCategoryServiceTest() diff --git a/AsbCloudWebApi.Tests/ServicesTests/TelemetryDataSaubServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/TelemetryDataSaubServiceTest.cs index 46d9136d..6d395cf2 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/TelemetryDataSaubServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/TelemetryDataSaubServiceTest.cs @@ -2,7 +2,6 @@ using AsbCloudApp.Data.SAUB; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; using AsbCloudInfrastructure.Services.SAUB; using Moq; using System; @@ -20,9 +19,9 @@ namespace AsbCloudWebApi.Tests.ServicesTests private readonly Mock timezoneService; private readonly SimpleTimezoneDto timezone; private readonly AsbCloudDbContext context; - private readonly CacheDb cacheDb; private readonly TelemetryService telemetryService; - + private readonly TelemetryUserService telemetryUserService; + private readonly TelemetryDataCache telemetryDataSaubCache; private readonly DateTime drillingStartDate; private readonly string uuid; public TelemetryDataSaubServiceTest() @@ -41,9 +40,9 @@ namespace AsbCloudWebApi.Tests.ServicesTests .Returns(timezone); context = TestHelpter.MakeRealTestContext(); - cacheDb = new CacheDb(); telemetryService = new TelemetryService(context, telemetryTracker.Object, timezoneService.Object); - + telemetryUserService = new TelemetryUserService(context, telemetryService, TestHelpter.MemoryCache); + telemetryDataSaubCache = TelemetryDataCache.GetInstance(context, out Task _); var info = new TelemetryInfoDto { TimeZoneOffsetTotalHours = timezone.Hours, @@ -64,7 +63,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests public async Task UpdateDataAsync() { // Arrange - var telemetryDataSaubService = new TelemetryDataSaubService(context, telemetryService, null, cacheDb); + var telemetryDataSaubService = new TelemetryDataSaubService(context, telemetryService, telemetryUserService, telemetryDataSaubCache); var now = DateTimeOffset.UtcNow.ToOffset(TimeSpan.FromHours(timezone.Hours)).DateTime; var tuser = "Завулон"; diff --git a/AsbCloudWebApi.Tests/TestHelpter.cs b/AsbCloudWebApi.Tests/TestHelpter.cs index 94bf1392..ad71a71f 100644 --- a/AsbCloudWebApi.Tests/TestHelpter.cs +++ b/AsbCloudWebApi.Tests/TestHelpter.cs @@ -1,5 +1,6 @@ using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using Moq; using System.Collections.Generic; using System.Linq; @@ -10,6 +11,8 @@ namespace AsbCloudWebApi.Tests { // Попробовать когда-нибудь https://github.com/MichalJankowskii/Moq.EntityFrameworkCore + public static IMemoryCache MemoryCache = new MemoryCache(new MemoryCacheOptions()); + public static AsbCloudDbContext MakeRealTestContext() { var options = new DbContextOptionsBuilder() From 431d291782d1b0a2afd0c626b6096b71667650c5 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Fri, 18 Nov 2022 16:13:32 +0500 Subject: [PATCH 11/14] remove CacheDB from tests --- .../ServicesTests/DepositCrudCacheServiceTest.cs | 2 +- .../ServicesTests/DetectedOperationServiceTest.cs | 3 --- .../ServicesTests/DrillerServiceTest.cs | 5 ----- .../ServicesTests/EventServiceTest.cs | 3 --- .../ServicesTests/FileCategoryServiceTest.cs | 6 ++---- .../ServicesTests/TelemetryDataSaubServiceTest.cs | 11 +++++------ AsbCloudWebApi.Tests/TestHelpter.cs | 7 +++---- 7 files changed, 11 insertions(+), 26 deletions(-) diff --git a/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs index 91664385..e95c7cc3 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DepositCrudCacheServiceTest.cs @@ -22,7 +22,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests protected override ICrudService MakeService() { var dbContext = TestHelpter.MakeRealTestContext(); - return new CrudCacheServiceBase(dbContext); + return new CrudCacheServiceBase(dbContext, TestHelpter.MemoryCache); } } } diff --git a/AsbCloudWebApi.Tests/ServicesTests/DetectedOperationServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DetectedOperationServiceTest.cs index f341a15a..00bca2aa 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DetectedOperationServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DetectedOperationServiceTest.cs @@ -3,8 +3,6 @@ using AsbCloudApp.Requests; using AsbCloudApp.Services; using AsbCloudDb.Model; using AsbCloudInfrastructure.Repository; -using AsbCloudInfrastructure.Services; -using AsbCloudInfrastructure.Services.Cache; using AsbCloudInfrastructure.Services.DetectOperations; using Moq; using System; @@ -19,7 +17,6 @@ namespace AsbCloudWebApi.Tests.ServicesTests public class DetectedOperationServiceTest { private readonly AsbCloudDbContext context; - private readonly CacheDb cacheDb; private readonly DetectedOperationService service; private readonly DetectedOperationRequest request; private Deposit deposit = new Deposit { Id = 1, Caption = "Депозит 1" }; diff --git a/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs index 7c1e60ff..f829c76c 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/DrillerServiceTest.cs @@ -1,10 +1,5 @@ using AsbCloudApp.Data; -using AsbCloudApp.Repositories; using AsbCloudApp.Services; -using AsbCloudDb.Model; -using AsbCloudInfrastructure.Repository; -using AsbCloudInfrastructure.Services; -using AsbCloudInfrastructure.Services.Cache; using Moq; using System.Collections.Generic; using System.Linq; diff --git a/AsbCloudWebApi.Tests/ServicesTests/EventServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/EventServiceTest.cs index 0c406f1e..b13cab4e 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/EventServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/EventServiceTest.cs @@ -1,7 +1,6 @@ using AsbCloudApp.Data.SAUB; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; using AsbCloudInfrastructure.Services.SAUB; using Moq; using System.Collections.Generic; @@ -14,13 +13,11 @@ namespace AsbCloudWebApi.Tests.ServicesTests; public class EventServiceTest { private readonly AsbCloudDbContext context; - private readonly CacheDb cacheDb; private readonly EventService service; public EventServiceTest() { context = TestHelpter.MakeRealTestContext(); - cacheDb = new CacheDb(); var telemetryTracker = new Mock(); var imezoneServiceMock = new Mock(); var telemetryService = new TelemetryService(context, telemetryTracker.Object, imezoneServiceMock.Object); diff --git a/AsbCloudWebApi.Tests/ServicesTests/FileCategoryServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/FileCategoryServiceTest.cs index 345f2693..12874dea 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/FileCategoryServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/FileCategoryServiceTest.cs @@ -1,7 +1,5 @@ -using AsbCloudApp.Data; -using AsbCloudDb.Model; +using AsbCloudDb.Model; using AsbCloudInfrastructure.Services; -using AsbCloudInfrastructure.Services.Cache; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -19,7 +17,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests context = TestHelpter.MakeRealTestContext(); context.SaveChanges(); - service = new FileCategoryService(context); + service = new FileCategoryService(context, TestHelpter.MemoryCache); } ~FileCategoryServiceTest() diff --git a/AsbCloudWebApi.Tests/ServicesTests/TelemetryDataSaubServiceTest.cs b/AsbCloudWebApi.Tests/ServicesTests/TelemetryDataSaubServiceTest.cs index 46d9136d..6d395cf2 100644 --- a/AsbCloudWebApi.Tests/ServicesTests/TelemetryDataSaubServiceTest.cs +++ b/AsbCloudWebApi.Tests/ServicesTests/TelemetryDataSaubServiceTest.cs @@ -2,7 +2,6 @@ using AsbCloudApp.Data.SAUB; using AsbCloudApp.Services; using AsbCloudDb.Model; -using AsbCloudInfrastructure.Services.Cache; using AsbCloudInfrastructure.Services.SAUB; using Moq; using System; @@ -20,9 +19,9 @@ namespace AsbCloudWebApi.Tests.ServicesTests private readonly Mock timezoneService; private readonly SimpleTimezoneDto timezone; private readonly AsbCloudDbContext context; - private readonly CacheDb cacheDb; private readonly TelemetryService telemetryService; - + private readonly TelemetryUserService telemetryUserService; + private readonly TelemetryDataCache telemetryDataSaubCache; private readonly DateTime drillingStartDate; private readonly string uuid; public TelemetryDataSaubServiceTest() @@ -41,9 +40,9 @@ namespace AsbCloudWebApi.Tests.ServicesTests .Returns(timezone); context = TestHelpter.MakeRealTestContext(); - cacheDb = new CacheDb(); telemetryService = new TelemetryService(context, telemetryTracker.Object, timezoneService.Object); - + telemetryUserService = new TelemetryUserService(context, telemetryService, TestHelpter.MemoryCache); + telemetryDataSaubCache = TelemetryDataCache.GetInstance(context, out Task _); var info = new TelemetryInfoDto { TimeZoneOffsetTotalHours = timezone.Hours, @@ -64,7 +63,7 @@ namespace AsbCloudWebApi.Tests.ServicesTests public async Task UpdateDataAsync() { // Arrange - var telemetryDataSaubService = new TelemetryDataSaubService(context, telemetryService, null, cacheDb); + var telemetryDataSaubService = new TelemetryDataSaubService(context, telemetryService, telemetryUserService, telemetryDataSaubCache); var now = DateTimeOffset.UtcNow.ToOffset(TimeSpan.FromHours(timezone.Hours)).DateTime; var tuser = "Завулон"; diff --git a/AsbCloudWebApi.Tests/TestHelpter.cs b/AsbCloudWebApi.Tests/TestHelpter.cs index 94bf1392..430e6dc7 100644 --- a/AsbCloudWebApi.Tests/TestHelpter.cs +++ b/AsbCloudWebApi.Tests/TestHelpter.cs @@ -1,5 +1,6 @@ using AsbCloudDb.Model; using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Caching.Memory; using Moq; using System.Collections.Generic; using System.Linq; @@ -10,16 +11,14 @@ namespace AsbCloudWebApi.Tests { // Попробовать когда-нибудь https://github.com/MichalJankowskii/Moq.EntityFrameworkCore + public static IMemoryCache MemoryCache = new MemoryCache(new MemoryCacheOptions()); + public static AsbCloudDbContext MakeRealTestContext() { var options = new DbContextOptionsBuilder() - //.UseInMemoryDatabase(System.Guid.NewGuid().ToString()) - //.ConfigureWarnings(configBuilder => - // configBuilder.Ignore(InMemoryEventId.TransactionIgnoredWarning)) .UseNpgsql("Host=localhost;Database=tests;Username=postgres;Password=q;Persist Security Info=True;Include Error Detail=True") .Options; var context = new AsbCloudDbContext(options); - //context.Database.EnsureDeleted(); context.Database.EnsureCreated(); return context; } From 41816719b9ff1972f144d448a3d0b73c295eba3f Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Mon, 21 Nov 2022 16:10:32 +0500 Subject: [PATCH 12/14] WirsRepository save fix --- AsbCloudInfrastructure/Repository/WitsRecordRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AsbCloudInfrastructure/Repository/WitsRecordRepository.cs b/AsbCloudInfrastructure/Repository/WitsRecordRepository.cs index cc53d92e..35c2fabb 100644 --- a/AsbCloudInfrastructure/Repository/WitsRecordRepository.cs +++ b/AsbCloudInfrastructure/Repository/WitsRecordRepository.cs @@ -88,7 +88,7 @@ namespace AsbCloudInfrastructure.Repository foreach (var entity in entities) { - if (!existingEntities.Any(e => e.DateTime == entity.DateTime)) + if (!existingEntities.Any(e => e == entity.DateTime)) { dbset.Add(entity); } From 9b06438935404595953dc20da312e46a343a2ed4 Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Mon, 21 Nov 2022 16:58:37 +0500 Subject: [PATCH 13/14] GetDatesRange Fix handle exception "Well id: {idWell} does not contain telemetry." --- AsbCloudInfrastructure/Services/WellService.cs | 2 +- .../SAUB/TelemetryDataBaseController.cs | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/AsbCloudInfrastructure/Services/WellService.cs b/AsbCloudInfrastructure/Services/WellService.cs index a1827851..c96672f7 100644 --- a/AsbCloudInfrastructure/Services/WellService.cs +++ b/AsbCloudInfrastructure/Services/WellService.cs @@ -332,7 +332,7 @@ namespace AsbCloudInfrastructure.Services throw new Exception($"Well id: {idWell} does not exist."); if (well.IdTelemetry is null) - throw new Exception($"Well id: {idWell} does not contain telemetry."); + throw new KeyNotFoundException($"Well id: {idWell} does not contain telemetry."); return telemetryService.GetDatesRange((int)well.IdTelemetry); } diff --git a/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs b/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs index 132f5513..51640915 100644 --- a/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs +++ b/AsbCloudWebApi/Controllers/SAUB/TelemetryDataBaseController.cs @@ -115,11 +115,15 @@ namespace AsbCloudWebApi.Controllers.SAUB if (!isCompanyOwnsWell) return Forbid(); - - var dataDatesRange = wellService.GetDatesRange(idWell); - - return Ok(dataDatesRange); + try + { + var dataDatesRange = wellService.GetDatesRange(idWell); + return Ok(dataDatesRange); + } + catch(KeyNotFoundException) + { + return NoContent(); + } } - } } From 0ea49dd72fcef69998a5dbf9b6c07236c51ece8f Mon Sep 17 00:00:00 2001 From: ngfrolov Date: Tue, 22 Nov 2022 17:26:06 +0500 Subject: [PATCH 14/14] =?UTF-8?q?improve=20DbSetMock.=20=D0=B4=D0=BE=D0=B1?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4?= =?UTF-8?q?=D0=B5=D1=80=D0=B6=D0=BA=D0=B0=20=D0=B0=D1=81=D0=B8=D0=BD=D1=85?= =?UTF-8?q?=D1=80=D0=BE=D0=BD=D0=BD=D1=8B=D1=85=20=D0=BC=D0=B5=D1=82=D0=BE?= =?UTF-8?q?=D0=B4=D0=BE=D0=B2=20=D0=B2=20DbSetMock.=20AddDbSetMock=20?= =?UTF-8?q?=D1=82=D0=B0=D0=BA=D0=B6=D0=B5=20=D0=BC=D0=BE=D0=BA=D0=B0=D0=B5?= =?UTF-8?q?=D1=82=20=D1=81=D0=B2=D0=BE=D0=B9=D1=81=D1=82=D0=B2=D0=BE=20?= =?UTF-8?q?=D1=81=D0=BE=D0=BE=D1=82=D0=B2=D0=B5=D1=82=D1=81=D1=82=D0=B2?= =?UTF-8?q?=D1=83=D1=8E=D1=89=D0=B5=D0=B3=D0=BE=20=D1=82=D0=B8=D0=BF=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AsbCloudWebApi.Tests/TestHelpter.cs | 81 ++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 7 deletions(-) diff --git a/AsbCloudWebApi.Tests/TestHelpter.cs b/AsbCloudWebApi.Tests/TestHelpter.cs index 430e6dc7..7df75b49 100644 --- a/AsbCloudWebApi.Tests/TestHelpter.cs +++ b/AsbCloudWebApi.Tests/TestHelpter.cs @@ -2,15 +2,18 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Moq; +using System; +using System.Collections; using System.Collections.Generic; using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; namespace AsbCloudWebApi.Tests { internal static class TestHelpter { - // Попробовать когда-нибудь https://github.com/MichalJankowskii/Moq.EntityFrameworkCore - public static IMemoryCache MemoryCache = new MemoryCache(new MemoryCacheOptions()); public static AsbCloudDbContext MakeRealTestContext() @@ -30,22 +33,82 @@ namespace AsbCloudWebApi.Tests contextMock.Setup(o => o.Set()) .Returns(() => dbSetMock.Object); + var dbSetProperty = typeof(IAsbCloudDbContext) + .GetProperties() + .FirstOrDefault(p => p.PropertyType.IsPublic && p.PropertyType == typeof(DbSet)); + + if (dbSetProperty is not null) + { + // https://learn.microsoft.com/ru-ru/dotnet/api/system.linq.expressions.expression?view=net-7.0 + var objParameterExpr = Expression.Parameter(typeof(IAsbCloudDbContext), "instance"); + var propertyExpr = Expression.Property(objParameterExpr, dbSetProperty); + var expression = Expression.Lambda>>(propertyExpr, objParameterExpr); + contextMock.SetupGet(expression).Returns(dbSetMock.Object); + } + return contextMock; } public static Mock> MakeDbSetMock() where T : class + => MakeDbSetMock(new List()); + + class DummyAsyncQueriable : IQueryable, IAsyncEnumerable { - var dbSetData = new List(); - return MakeDbSetMock(dbSetData); + private readonly IQueryable queriable; + + public Type ElementType => queriable.ElementType; + + public Expression Expression => queriable.Expression; + + public IQueryProvider Provider => queriable.Provider; + + class QueriableAsyncEminaretor : IAsyncEnumerator + { + private readonly IEnumerator syncEnumerator; + + public QueriableAsyncEminaretor(IEnumerator syncEnumerator) + { + this.syncEnumerator = syncEnumerator; + } + + public T2 Current => syncEnumerator.Current; + + public ValueTask DisposeAsync() + { + syncEnumerator.Dispose(); + return ValueTask.CompletedTask; + } + + public ValueTask MoveNextAsync() + { + var result = syncEnumerator.MoveNext(); + return ValueTask.FromResult(result); + } + } + + public DummyAsyncQueriable(IEnumerable dbSetData) + { + queriable = dbSetData.ToList().AsQueryable(); + } + + public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + var enumerator = this.AsEnumerable().GetEnumerator(); + return new QueriableAsyncEminaretor(enumerator); + } + + public IEnumerator GetEnumerator() + => queriable.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() + => queriable.GetEnumerator(); } public static Mock> MakeDbSetMock(IEnumerable dbSetData) where T : class { - var dbSetDataQueriable = dbSetData - .ToList() - .AsQueryable(); + var dbSetDataQueriable = new DummyAsyncQueriable(dbSetData); Mock> dbSetMock = new(); dbSetMock.As>().Setup(o => o.Provider).Returns(() => dbSetDataQueriable.Provider); @@ -53,6 +116,10 @@ namespace AsbCloudWebApi.Tests dbSetMock.As>().Setup(o => o.ElementType).Returns(() => dbSetDataQueriable.ElementType); dbSetMock.As>().Setup(o => o.GetEnumerator()).Returns(() => dbSetDataQueriable.GetEnumerator()); + dbSetMock.As>() + .Setup(o => o.GetAsyncEnumerator(It.IsAny())) + .Returns(() => dbSetDataQueriable.GetAsyncEnumerator()); + return dbSetMock; } }