using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace AsbCloudInfrastructure.Services.Cache { public class CacheTable where TEntity : class { private readonly DbContext context; private (DateTime refreshDate, IEnumerable entities) data; private readonly List cached; internal CacheTable(DbContext context, (DateTime refreshDate, IEnumerable entities) data) { this.context = context; this.data = data; this.cached = (List)data.entities; } public TEntity this[int index] { get => cached.ElementAt(index); } public int Refresh() { cached.Clear(); var dbEntities = context.Set().ToList(); cached.AddRange(dbEntities); data.refreshDate = DateTime.Now; return cached.Count; } public async Task RefreshAsync(CancellationToken token = default) { cached.Clear(); var dbEntities = await context.Set().ToListAsync(token).ConfigureAwait(false); cached.AddRange(dbEntities); data.refreshDate = DateTime.Now; return cached.Count; } private bool CheckRefresh(RefreshMode refreshMode) { if (refreshMode == RefreshMode.Force) { Refresh(); return true; } if ((!cached.Any()) && (refreshMode == RefreshMode.IfResultEmpty)) { Refresh(); return true; } return false; } private async Task CheckRefreshAsync(RefreshMode refreshMode, CancellationToken token = default) { if (refreshMode == RefreshMode.Force) { await RefreshAsync(token); return true; } if ((!cached.Any()) && (refreshMode == RefreshMode.IfResultEmpty)) { await RefreshAsync(token); return true; } return false; } public bool Contains(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty) => FirstOrDefault(predicate, refreshMode) != default; public Task ContainsAsync(Func predicate, CancellationToken token = default) => ContainsAsync(predicate, RefreshMode.IfResultEmpty, token); public async Task ContainsAsync(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default) => await FirstOrDefaultAsync(predicate, refreshMode, token) != default; public Task FirstOrDefaultAsync(CancellationToken token = default) => FirstOrDefaultAsync(RefreshMode.IfResultEmpty, token); public TEntity FirstOrDefault(RefreshMode refreshMode = RefreshMode.IfResultEmpty) { bool isUpdated = CheckRefresh(refreshMode); var result = cached.FirstOrDefault(); if (result == default && refreshMode == RefreshMode.IfResultEmpty && !isUpdated) { Refresh(); return cached.FirstOrDefault(); } return result; } public async Task FirstOrDefaultAsync(RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default) { bool isUpdated = await CheckRefreshAsync(refreshMode, token); var result = cached.FirstOrDefault(); if (result == default && refreshMode == RefreshMode.IfResultEmpty && !isUpdated) { await RefreshAsync(token); return cached.FirstOrDefault(); } return result; } public Task FirstOrDefaultAsync(Func predicate, CancellationToken token = default) => FirstOrDefaultAsync(predicate, RefreshMode.IfResultEmpty, token); public TEntity FirstOrDefault(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty) { bool isUpdated = CheckRefresh(refreshMode); var result = cached.FirstOrDefault(predicate); if (result == default && refreshMode == RefreshMode.IfResultEmpty && !isUpdated) { Refresh(); return cached.FirstOrDefault(predicate); } return result; } public async Task FirstOrDefaultAsync(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default) { bool isUpdated = await CheckRefreshAsync(refreshMode, token); var result = cached.FirstOrDefault(predicate); if (result == default && refreshMode == RefreshMode.IfResultEmpty && !isUpdated) { await RefreshAsync(token); return cached.FirstOrDefault(predicate); } return result; } public Task> SelectAsync(Func predicate, CancellationToken token = default) => SelectAsync(predicate, RefreshMode.IfResultEmpty, token); public IEnumerable Select(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty) { bool isUpdated = CheckRefresh(refreshMode); var result = cached.Where(predicate); if (!result.Any() && refreshMode == RefreshMode.IfResultEmpty && !isUpdated) { Refresh(); return cached.Where(predicate); } return result; } public async Task> SelectAsync(Func predicate, RefreshMode refreshMode = RefreshMode.IfResultEmpty, CancellationToken token = default) { bool isUpdated = await CheckRefreshAsync(refreshMode, token); var result = cached.Where(predicate); if (!result.Any() && refreshMode == RefreshMode.IfResultEmpty && !isUpdated) { await RefreshAsync(token); return cached.Where(predicate); } return result; } public IEnumerable Mutate(Func predicate, Action mutation) { var dbSet = context.Set(); var dbEntities = dbSet.Where(predicate); if (dbEntities.Any()) { foreach (var dbEntity in dbEntities) mutation(dbEntity); context.SaveChanges(); } cached.RemoveAll(e => predicate(e)); cached.AddRange(dbEntities); return dbEntities; } public async Task> MutateAsync(Func predicate, Action mutation, CancellationToken token = default) { var dbSet = context.Set(); var dbEntities = dbSet.Where(predicate); if (dbEntities.Any()) { foreach (var dbEntity in dbEntities) mutation(dbEntity); await context.SaveChangesAsync(token).ConfigureAwait(false); } cached.RemoveAll(e => predicate(e)); cached.AddRange(dbEntities); return dbEntities; } public TEntity Upsert(TEntity entity) { var dbSet = context.Set(); var updated = dbSet.Update(entity); context.SaveChanges(); Refresh(); return updated.Entity; } public async Task UpsertAsync(TEntity entity, CancellationToken token = default) { var dbSet = context.Set(); var updated = dbSet.Update(entity); await context.SaveChangesAsync(token).ConfigureAwait(false); await RefreshAsync(token).ConfigureAwait(false); return updated.Entity; } public IEnumerable Upsert(IEnumerable entities) { var dbSet = context.Set(); var upsertedEntries = new List(entities.Count()); foreach (var entity in entities) upsertedEntries.Add(dbSet.Update(entity).Entity); context.SaveChanges(); Refresh(); return upsertedEntries; } public async Task> UpsertAsync(IEnumerable entities, CancellationToken token = default) { var dbSet = context.Set(); var upsertedEntries = new List(entities.Count()); foreach (var entity in entities) upsertedEntries.Add(dbSet.Update(entity).Entity); await context.SaveChangesAsync(token).ConfigureAwait(false); await RefreshAsync(token).ConfigureAwait(false); return upsertedEntries; } public void Remove(Func predicate) { var dbSet = context.Set(); cached.RemoveAll(e => predicate(e)); dbSet.RemoveRange(dbSet.Where(predicate)); context.SaveChanges(); return; } public async Task RemoveAsync(Func predicate, CancellationToken token = default) { var dbSet = context.Set(); cached.RemoveAll(e => predicate(e)); dbSet.RemoveRange(dbSet.Where(predicate)); await context.SaveChangesAsync(token).ConfigureAwait(false); return; } public TEntity Insert(TEntity entity) { var dbSet = context.Set(); var dbEntity = dbSet.Add(entity).Entity; context.SaveChanges(); cached.Add(dbEntity); return dbEntity; } public async Task InsertAsync(TEntity entity, CancellationToken token = default) { var dbSet = context.Set(); var dbEntity = dbSet.Add(entity).Entity; await context.SaveChangesAsync(token).ConfigureAwait(false); cached.Add(dbEntity); return dbEntity; } public IEnumerable Insert(IEnumerable newEntities) { var dbSet = context.Set(); var dbEntities = new List(newEntities.Count()); foreach (var item in newEntities) dbEntities.Add(dbSet.Add(item).Entity); context.SaveChanges(); cached.AddRange(dbEntities); return dbEntities; } public async Task> InsertAsync(IEnumerable newEntities, CancellationToken token = default) { var dbSet = context.Set(); var dbEntities = new List(newEntities.Count()); foreach (var item in newEntities) dbEntities.Add(dbSet.Add(item).Entity); await context.SaveChangesAsync(token).ConfigureAwait(false); cached.AddRange(dbEntities); return dbEntities; } } }