diff --git a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj index 1a8e2432..84cf9062 100644 --- a/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj +++ b/AsbCloudInfrastructure/AsbCloudInfrastructure.csproj @@ -43,6 +43,7 @@ + diff --git a/AsbCloudInfrastructure/Repository/UserRepository.cs b/AsbCloudInfrastructure/Repository/UserRepository.cs index 9a666b68..0b5a3a58 100644 --- a/AsbCloudInfrastructure/Repository/UserRepository.cs +++ b/AsbCloudInfrastructure/Repository/UserRepository.cs @@ -1,9 +1,7 @@ using AsbCloudApp.Data; using AsbCloudApp.Exceptions; using AsbCloudApp.Repositories; -using AsbCloudDb; using AsbCloudDb.Model; -using AsbCloudInfrastructure.EfCache; using Mapster; using Microsoft.EntityFrameworkCore; using System; @@ -11,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Microsoft.Extensions.Caching.Memory; namespace AsbCloudInfrastructure.Repository { @@ -29,10 +28,12 @@ namespace AsbCloudInfrastructure.Repository dst => dst.Files, dst => dst.RelationUsersUserRoles) .Config; - - public UserRepository(IAsbCloudDbContext dbContext, IUserRoleRepository userRoleRepository) { + private readonly IMemoryCache memoryCache; + public UserRepository(IAsbCloudDbContext dbContext, IUserRoleRepository userRoleRepository, IMemoryCache memoryCache) + { this.dbContext = dbContext; this.userRoleRepository = userRoleRepository; + this.memoryCache = memoryCache; } public async Task InsertAsync(UserExtendedDto dto, CancellationToken token) @@ -58,12 +59,13 @@ namespace AsbCloudInfrastructure.Repository public async Task> GetAllAsync(CancellationToken token) { - var dtos = (await GetCacheUserAsync(token)).ToList(); + var dtos = (await GetCacheUserAsync(token)).ToList(); + var listDtos = dtos.ToList(); if (dtos is null) return Enumerable.Empty(); - for (var i = 0; i < dtos.Count; i++) - dtos[i].RoleNames = GetRolesNamesByIdUser(dtos[i].Id); + for (var i = 0; i < listDtos.Count; i++) + listDtos[i].RoleNames = GetRolesNamesByIdUser(listDtos[i].Id); return dtos; } @@ -170,31 +172,43 @@ namespace AsbCloudInfrastructure.Repository throw new ArgumentInvalidException($"Login {login} is busy by {existingUserDto.MakeDisplayName()}, id{existingUserDto.Id}", nameof(login)); } - private Task> GetCacheUserAsync(CancellationToken token) - => dbContext.Users + private async Task> GetCacheUserAsync(CancellationToken token) + { + var query = dbContext.Users .Include(r => r.Company) - .Include(r => r.RelationUsersUserRoles) - .FromCacheAsync(userCacheTag, cacheObsolence, Convert, token); + .Include(r => r.RelationUsersUserRoles); + return await FromCacheAsync(query, userCacheTag, cacheObsolence, Convert, token); + } private IEnumerable GetCacheUser() - => dbContext.Users + { + var query = dbContext.Users .Include(r => r.Company) - .Include(r => r.RelationUsersUserRoles) - .FromCache(userCacheTag, cacheObsolence, Convert); + .Include(r => r.RelationUsersUserRoles); + return FromCache(query, userCacheTag, cacheObsolence, Convert); + } private void DropCacheUsers() - => dbContext.Users.DropCache(userCacheTag); + { + memoryCache.Remove(userCacheTag); + } - private Task> GetCacheRelationUserUserRoleAsync(CancellationToken token) - => dbContext.RelationUserUserRoles + private async Task> GetCacheRelationUserUserRoleAsync(CancellationToken token) + { + var query = dbContext.RelationUserUserRoles .Include(r => r.UserRole) - .Include(r => r.User) - .FromCacheAsync(relationUserUserRoleCacheTag, cacheObsolence, token); + .Include(r => r.User); + return await FromCacheAsync(query, relationUserUserRoleCacheTag, cacheObsolence, token); + } private IEnumerable GetCachRelationUserUserRoleCacheTag() - => dbContext.RelationUserUserRoles - .Include(r => r.UserRole) - .Include(r => r.User) - .FromCache(relationUserUserRoleCacheTag, cacheObsolence); + { + var query = dbContext.RelationUserUserRoles + .Include(r => r.UserRole) + .Include(r => r.User); + return FromCache(query, relationUserUserRoleCacheTag, cacheObsolence); + } private void DropCacheRelationUserUserRoleCacheTag() - => dbContext.RelationUserUserRoles.DropCache(relationUserUserRoleCacheTag); + { + memoryCache.Remove(relationUserUserRoleCacheTag); + } private async Task UpdateRolesCacheForUserAsync(int idUser, IEnumerable newRoles, CancellationToken token) { @@ -212,6 +226,71 @@ namespace AsbCloudInfrastructure.Repository DropCacheRelationUserUserRoleCacheTag(); } + public async Task> FromCacheAsync(IQueryable query, string tag, TimeSpan obsolescence, Func convert, CancellationToken token) + where TEntity : class + { + async Task factory(CancellationToken token) + => await query.AsNoTracking().ToArrayAsync(token); + var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token); + return cache.Select(convert); + } + public async Task> FromCacheAsync(IQueryable query, string tag, TimeSpan obsolescence, CancellationToken token) + where TEntity : class + { + async Task factory(CancellationToken token) + => await query.AsNoTracking().ToArrayAsync(token); + var cache = await GetOrAddCacheAsync(tag, factory, obsolescence, token); + return cache; + } + public IEnumerable FromCache(IQueryable query, string tag, TimeSpan obsolescence, Func convert) + where TEntity : class + { + TEntity[] factory() + => query.AsNoTracking().ToArray(); + var cache = GetOrAddCache(tag, factory, obsolescence); + return cache.Select(convert); + } + public IEnumerable FromCache(IQueryable query, string tag, TimeSpan obsolescence) + where TEntity : class + { + TEntity[] factory() + => query.AsNoTracking().ToArray(); + var cache = GetOrAddCache(tag, factory, obsolescence); + return cache; + } + private async Task GetOrAddCacheAsync(string tag, Func> valueFactoryAsync, TimeSpan obsolete, CancellationToken token) + + { + memoryCache.TryGetValue(tag, out TEntity[]? cached); + if (cached == null) + { + var values = await valueFactoryAsync(token); + + if (values != null) + { + memoryCache.Set(tag, values, new MemoryCacheEntryOptions().SetAbsoluteExpiration(obsolete)); + } + return values!; + } + return cached; + } + private TEntity[] GetOrAddCache(string tag, Func valueFactory, TimeSpan obsolete) + + { + memoryCache.TryGetValue(tag, out TEntity[]? cached); + if (cached == null) + { + var values = valueFactory(); + + if (values != null) + { + memoryCache.Set(tag, values, new MemoryCacheEntryOptions().SetAbsoluteExpiration(obsolete)); + } + return values!; + } + return cached; + } + protected virtual User Convert(UserExtendedDto dto) { var entity = dto.Adapt(userTypeAdapterConfig);