2021-04-07 18:01:56 +05:00
|
|
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Threading;
|
|
|
|
|
using System.Threading.Tasks;
|
2021-10-03 20:08:17 +05:00
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Diagnostics;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
|
|
|
|
|
namespace AsbCloudInfrastructure.Services.Cache
|
|
|
|
|
{
|
2021-09-08 11:51:55 +05:00
|
|
|
|
public class CacheTable<TEntity> : IEnumerable<TEntity>
|
|
|
|
|
where TEntity : class
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
private const int semaphoreTimeout = 5_000;
|
|
|
|
|
private static readonly SemaphoreSlim semaphore = new(1);
|
2021-04-07 18:01:56 +05:00
|
|
|
|
private readonly DbContext context;
|
2021-10-03 20:08:17 +05:00
|
|
|
|
private (DateTime refreshDate, IEnumerable entities) data;
|
|
|
|
|
private readonly List<TEntity> cached;
|
2021-08-10 16:49:14 +05:00
|
|
|
|
private readonly DbSet<TEntity> dbSet;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
internal CacheTable(DbContext context, (DateTime refreshDate, IEnumerable entities) data)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
|
|
|
|
this.context = context;
|
2021-05-21 12:30:25 +05:00
|
|
|
|
this.data = data;
|
2021-08-10 16:49:14 +05:00
|
|
|
|
dbSet = context.Set<TEntity>();
|
2021-10-03 20:08:17 +05:00
|
|
|
|
cached = (List<TEntity>)data.entities;
|
|
|
|
|
if (cached.Count == 0)
|
|
|
|
|
Refresh();
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-23 10:21:25 +05:00
|
|
|
|
public TEntity this[int index] { get => cached.ElementAt(index); }
|
2021-04-07 18:01:56 +05:00
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
//public static void Sync(Action)
|
2021-09-29 10:26:25 +05:00
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public int Refresh()
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
var wasFree = semaphore.CurrentCount > 0;
|
|
|
|
|
if(!semaphore.Wait(semaphoreTimeout))
|
|
|
|
|
return 0;
|
|
|
|
|
try
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
if (wasFree)
|
|
|
|
|
{
|
|
|
|
|
cached.Clear();
|
|
|
|
|
var entities = dbSet.AsNoTracking().ToList();
|
|
|
|
|
cached.AddRange(entities);
|
|
|
|
|
data.refreshDate = DateTime.Now;
|
|
|
|
|
}
|
|
|
|
|
//else - nothing, it was just updated in another thread
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
2021-10-03 20:08:17 +05:00
|
|
|
|
catch (Exception ex)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
Trace.WriteLine($"{DateTime.Now:yyyy.MM.dd HH:mm:ss:fff} error in CacheTable<{typeof(TEntity).Name}>.Refresh()");
|
|
|
|
|
Trace.WriteLine(ex.Message);
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
2021-10-03 20:08:17 +05:00
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
semaphore.Release();
|
|
|
|
|
}
|
|
|
|
|
return cached.Count;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public async Task<int> RefreshAsync(CancellationToken token = default)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
var wasFree = semaphore.CurrentCount > 0;
|
|
|
|
|
if (!await semaphore.WaitAsync(semaphoreTimeout, token).ConfigureAwait(false))
|
|
|
|
|
return 0;
|
|
|
|
|
try
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
if (wasFree)
|
|
|
|
|
{
|
|
|
|
|
cached.Clear();
|
|
|
|
|
var entities = await context.Set<TEntity>().AsNoTracking()
|
|
|
|
|
.ToListAsync(token).ConfigureAwait(false);
|
|
|
|
|
cached.AddRange(entities);
|
|
|
|
|
data.refreshDate = DateTime.Now;
|
|
|
|
|
}
|
|
|
|
|
//else - nothing, it was just updated in another thread
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
2021-10-03 20:08:17 +05:00
|
|
|
|
catch (Exception ex)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
Trace.WriteLine($"{DateTime.Now:yyyy.MM.dd HH:mm:ss:fff} error in CacheTable<{typeof(TEntity).Name}>.Refresh()");
|
|
|
|
|
Trace.WriteLine(ex.Message);
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
2021-10-03 20:08:17 +05:00
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
semaphore.Release();
|
|
|
|
|
}
|
|
|
|
|
return cached.Count;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public bool Contains(Func<TEntity, bool> predicate)
|
|
|
|
|
=> FirstOrDefault(predicate) != default;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public async Task<bool> ContainsAsync(Func<TEntity, bool> predicate, CancellationToken token = default)
|
|
|
|
|
=> await FirstOrDefaultAsync(predicate, token) != default;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public TEntity FirstOrDefault()
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-04-23 10:21:25 +05:00
|
|
|
|
var result = cached.FirstOrDefault();
|
2021-10-03 20:08:17 +05:00
|
|
|
|
if (result != default)
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
Refresh();
|
|
|
|
|
return cached.FirstOrDefault();
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public async Task<TEntity> FirstOrDefaultAsync(CancellationToken token = default)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-04-23 10:21:25 +05:00
|
|
|
|
var result = cached.FirstOrDefault();
|
2021-10-03 20:08:17 +05:00
|
|
|
|
if (result != default)
|
|
|
|
|
return result;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
await RefreshAsync(token);
|
|
|
|
|
return cached.FirstOrDefault();
|
|
|
|
|
|
|
|
|
|
}
|
2021-04-07 18:01:56 +05:00
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public TEntity FirstOrDefault(Func<TEntity, bool> predicate)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-04-23 10:21:25 +05:00
|
|
|
|
var result = cached.FirstOrDefault(predicate);
|
2021-10-03 20:08:17 +05:00
|
|
|
|
if (result != default)
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
Refresh();
|
|
|
|
|
return cached.FirstOrDefault(predicate);
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public async Task<TEntity> FirstOrDefaultAsync(Func<TEntity, bool> predicate, CancellationToken token = default)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-04-23 10:21:25 +05:00
|
|
|
|
var result = cached.FirstOrDefault(predicate);
|
2021-10-03 20:08:17 +05:00
|
|
|
|
if (result != default)
|
|
|
|
|
return result;
|
2021-08-27 17:55:22 +05:00
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
await RefreshAsync(token);
|
|
|
|
|
return cached.FirstOrDefault(predicate);
|
|
|
|
|
}
|
2021-04-07 18:01:56 +05:00
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public IEnumerable<TEntity> Where(Func<TEntity, bool> predicate = default)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-08-16 14:16:44 +05:00
|
|
|
|
var result = (predicate != default)
|
|
|
|
|
? cached.Where(predicate)
|
|
|
|
|
: cached;
|
2021-10-03 20:08:17 +05:00
|
|
|
|
if (result.Any())
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
Refresh();
|
|
|
|
|
result = (predicate != default)
|
|
|
|
|
? cached.Where(predicate)
|
|
|
|
|
: cached;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-08-24 10:59:10 +05:00
|
|
|
|
public async Task<IEnumerable<TEntity>> WhereAsync(Func<TEntity, bool> predicate = default,
|
2021-10-03 20:08:17 +05:00
|
|
|
|
CancellationToken token = default)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-08-24 10:59:10 +05:00
|
|
|
|
var result = (predicate != default)
|
|
|
|
|
? cached.Where(predicate)
|
2021-08-16 14:16:44 +05:00
|
|
|
|
: cached;
|
2021-10-03 20:08:17 +05:00
|
|
|
|
if (result.Any())
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
|
|
await RefreshAsync(token);
|
|
|
|
|
result = (predicate != default)
|
|
|
|
|
? cached.Where(predicate)
|
|
|
|
|
: cached;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-21 17:42:18 +05:00
|
|
|
|
public TEntity Upsert(TEntity entity)
|
2021-04-23 10:21:25 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<TEntity> updated;
|
|
|
|
|
if (dbSet.Contains(entity))
|
|
|
|
|
updated = dbSet.Update(entity);
|
|
|
|
|
else
|
|
|
|
|
updated = dbSet.Add(entity);
|
2021-07-16 09:15:10 +05:00
|
|
|
|
context.SaveChanges();
|
|
|
|
|
Refresh();
|
|
|
|
|
return updated.Entity;
|
2021-05-21 17:42:18 +05:00
|
|
|
|
}
|
2021-04-23 10:21:25 +05:00
|
|
|
|
|
2021-05-21 17:42:18 +05:00
|
|
|
|
public async Task<TEntity> UpsertAsync(TEntity entity, CancellationToken token = default)
|
2021-04-23 10:21:25 +05:00
|
|
|
|
{
|
2021-07-20 12:28:56 +05:00
|
|
|
|
Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<TEntity> updated;
|
|
|
|
|
if (dbSet.Contains(entity))
|
|
|
|
|
updated = dbSet.Update(entity);
|
|
|
|
|
else
|
|
|
|
|
updated = dbSet.Add(entity);
|
2021-07-16 09:15:10 +05:00
|
|
|
|
await context.SaveChangesAsync(token).ConfigureAwait(false);
|
|
|
|
|
await RefreshAsync(token).ConfigureAwait(false);
|
|
|
|
|
return updated.Entity;
|
2021-05-21 17:42:18 +05:00
|
|
|
|
}
|
2021-04-23 10:21:25 +05:00
|
|
|
|
|
2021-05-21 17:42:18 +05:00
|
|
|
|
public IEnumerable<TEntity> Upsert(IEnumerable<TEntity> entities)
|
|
|
|
|
{
|
2021-07-16 09:15:10 +05:00
|
|
|
|
var upsertedEntries = new List<TEntity>(entities.Count());
|
|
|
|
|
foreach (var entity in entities)
|
2021-07-20 12:28:56 +05:00
|
|
|
|
{
|
|
|
|
|
Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<TEntity> updated;
|
2021-08-10 16:35:31 +05:00
|
|
|
|
if (dbSet.Contains(entity)) // TODO: это очень ммедленно
|
2021-07-20 12:28:56 +05:00
|
|
|
|
updated = dbSet.Update(entity);
|
|
|
|
|
else
|
|
|
|
|
updated = dbSet.Add(entity);
|
|
|
|
|
upsertedEntries.Add(updated.Entity);
|
2021-07-21 15:29:19 +05:00
|
|
|
|
}
|
2021-07-16 09:15:10 +05:00
|
|
|
|
context.SaveChanges();
|
|
|
|
|
Refresh();
|
|
|
|
|
return upsertedEntries;
|
2021-04-23 10:21:25 +05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<IEnumerable<TEntity>> UpsertAsync(IEnumerable<TEntity> entities, CancellationToken token = default)
|
|
|
|
|
{
|
2021-07-16 09:15:10 +05:00
|
|
|
|
var upsertedEntries = new List<TEntity>(entities.Count());
|
|
|
|
|
foreach (var entity in entities)
|
2021-07-20 12:28:56 +05:00
|
|
|
|
{
|
|
|
|
|
Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<TEntity> updated;
|
|
|
|
|
if (dbSet.Contains(entity))
|
|
|
|
|
updated = dbSet.Update(entity);
|
|
|
|
|
else
|
|
|
|
|
updated = dbSet.Add(entity);
|
|
|
|
|
upsertedEntries.Add(updated.Entity);
|
|
|
|
|
}
|
2021-07-16 09:15:10 +05:00
|
|
|
|
await context.SaveChangesAsync(token).ConfigureAwait(false);
|
|
|
|
|
await RefreshAsync(token).ConfigureAwait(false);
|
|
|
|
|
return upsertedEntries;
|
2021-04-23 10:21:25 +05:00
|
|
|
|
}
|
2021-07-16 09:15:10 +05:00
|
|
|
|
|
2021-04-07 18:01:56 +05:00
|
|
|
|
public void Remove(Func<TEntity, bool> predicate)
|
|
|
|
|
{
|
2021-07-16 09:15:10 +05:00
|
|
|
|
dbSet.RemoveRange(dbSet.Where(predicate));
|
|
|
|
|
context.SaveChanges();
|
2021-09-29 10:26:25 +05:00
|
|
|
|
Refresh();
|
2021-07-16 09:15:10 +05:00
|
|
|
|
return;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task RemoveAsync(Func<TEntity, bool> predicate, CancellationToken token = default)
|
|
|
|
|
{
|
2021-07-16 09:15:10 +05:00
|
|
|
|
dbSet.RemoveRange(dbSet.Where(predicate));
|
|
|
|
|
await context.SaveChangesAsync(token).ConfigureAwait(false);
|
2021-09-29 10:26:25 +05:00
|
|
|
|
await RefreshAsync(token).ConfigureAwait(false);
|
2021-07-16 09:15:10 +05:00
|
|
|
|
return;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public TEntity Insert(TEntity entity)
|
|
|
|
|
{
|
2021-09-29 10:26:25 +05:00
|
|
|
|
var entry = dbSet.Add(entity);
|
2021-07-16 09:15:10 +05:00
|
|
|
|
context.SaveChanges();
|
2021-10-03 20:08:17 +05:00
|
|
|
|
Refresh();
|
2021-09-29 10:26:25 +05:00
|
|
|
|
return entry.Entity;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public async Task<TEntity> InsertAsync(TEntity entity, CancellationToken token = default)
|
|
|
|
|
{
|
2021-09-29 10:26:25 +05:00
|
|
|
|
var entry = dbSet.Add(entity);
|
2021-07-16 09:15:10 +05:00
|
|
|
|
await context.SaveChangesAsync(token).ConfigureAwait(false);
|
2021-10-03 20:08:17 +05:00
|
|
|
|
await RefreshAsync(token).ConfigureAwait(false);
|
2021-09-29 10:26:25 +05:00
|
|
|
|
return entry.Entity;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public int Insert(IEnumerable<TEntity> newEntities)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
dbSet.AddRange(newEntities);
|
|
|
|
|
var result = context.SaveChanges();
|
|
|
|
|
Refresh();
|
|
|
|
|
return result;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
public async Task<int> InsertAsync(IEnumerable<TEntity> newEntities, CancellationToken token = default)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
dbSet.AddRange(newEntities);
|
|
|
|
|
var result = await context.SaveChangesAsync(token).ConfigureAwait(false);
|
|
|
|
|
await RefreshAsync(token).ConfigureAwait(false);
|
|
|
|
|
return result;
|
2021-05-21 17:42:18 +05:00
|
|
|
|
}
|
2021-09-08 11:51:55 +05:00
|
|
|
|
|
|
|
|
|
public IEnumerator<TEntity> GetEnumerator() => Where().GetEnumerator();
|
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
}
|