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-12-03 15:03:33 +05:00
|
|
|
|
private static readonly TimeSpan minPeriodRefresh = TimeSpan.FromSeconds(5);
|
2021-11-10 17:52:28 +05:00
|
|
|
|
private static readonly string nameOfTEntity = typeof(TEntity).Name;
|
2021-11-10 17:01:18 +05:00
|
|
|
|
|
2021-11-11 10:57:08 +05:00
|
|
|
|
private readonly CacheTableDataStore data;
|
2021-11-24 16:16:17 +05:00
|
|
|
|
private readonly Func<DbSet<TEntity>, IQueryable<TEntity>> configureDbSet;
|
2021-10-03 20:08:17 +05:00
|
|
|
|
private readonly List<TEntity> cached;
|
2021-11-11 10:57:08 +05:00
|
|
|
|
private readonly DbContext context;
|
2021-08-10 16:49:14 +05:00
|
|
|
|
private readonly DbSet<TEntity> dbSet;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
|
2021-12-22 11:40:29 +05:00
|
|
|
|
internal CacheTable(DbContext context, CacheTableDataStore data, ISet<string> includes = null)
|
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-12-22 11:40:29 +05:00
|
|
|
|
|
2021-11-24 16:16:17 +05:00
|
|
|
|
if (includes?.Any() == true)
|
|
|
|
|
configureDbSet = (DbSet<TEntity> dbSet) =>
|
|
|
|
|
{
|
|
|
|
|
IQueryable<TEntity> result = dbSet;
|
2021-12-22 11:40:29 +05:00
|
|
|
|
foreach (var include in includes)
|
|
|
|
|
result = result.Include(include);
|
2021-11-24 16:16:17 +05:00
|
|
|
|
return result;
|
|
|
|
|
};
|
2021-12-03 15:03:33 +05:00
|
|
|
|
|
2021-11-11 10:57:08 +05:00
|
|
|
|
cached = (List<TEntity>)data.Entities;
|
2021-11-24 16:16:17 +05:00
|
|
|
|
if ((cached.Count == 0) || data.IsObsolete)
|
|
|
|
|
Refresh(false);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-07 18:27:52 +05:00
|
|
|
|
internal CacheTable(DbContext context, CacheTableDataStore data,
|
|
|
|
|
Func<DbSet<TEntity>, IQueryable<TEntity>> configureDbSet = null)
|
2021-11-24 16:16:17 +05:00
|
|
|
|
{
|
|
|
|
|
this.context = context;
|
2021-12-03 15:03:33 +05:00
|
|
|
|
this.data = data;
|
2021-11-24 16:16:17 +05:00
|
|
|
|
this.configureDbSet = configureDbSet;
|
|
|
|
|
|
|
|
|
|
dbSet = context.Set<TEntity>();
|
|
|
|
|
|
|
|
|
|
cached = (List<TEntity>)data.Entities;
|
|
|
|
|
if ((cached.Count == 0) || data.IsObsolete)
|
2021-11-10 17:01:18 +05:00
|
|
|
|
Refresh(false);
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-07 18:27:52 +05:00
|
|
|
|
public TEntity this[int index]
|
|
|
|
|
{
|
|
|
|
|
get => cached.ElementAt(index);
|
|
|
|
|
}
|
2021-04-07 18:01:56 +05:00
|
|
|
|
|
2021-10-04 15:52:22 +05:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Runs action like atomic operation.
|
|
|
|
|
/// wasFree is action argument indicates that semaphore was free.
|
|
|
|
|
/// It may be needed to avoid multiple operations like Refresh().
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="action">(wasFree) => {...}</param>
|
2021-12-03 15:03:33 +05:00
|
|
|
|
/// <returns>default if semaphoreTimeout. Or result of func(..)</returns>
|
|
|
|
|
private static T Sync<T>(Func<bool, T> func)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
var wasFree = semaphore.CurrentCount > 0;
|
2021-12-03 15:03:33 +05:00
|
|
|
|
T result = default;
|
|
|
|
|
if (func is null || !semaphore.Wait(semaphoreTimeout))
|
|
|
|
|
return result;
|
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
try
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-12-03 15:03:33 +05:00
|
|
|
|
result = func.Invoke(wasFree);
|
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-11-11 10:57:08 +05:00
|
|
|
|
Trace.WriteLine($"{DateTime.Now:yyyy.MM.dd HH:mm:ss:fff} error in CacheTable<{nameOfTEntity}>.Sync()");
|
2021-10-03 20:08:17 +05:00
|
|
|
|
Trace.WriteLine(ex.Message);
|
2021-10-04 15:52:22 +05:00
|
|
|
|
Trace.WriteLine(ex.StackTrace);
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
2021-10-03 20:08:17 +05:00
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
semaphore.Release();
|
|
|
|
|
}
|
2021-12-07 18:27:52 +05:00
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return result;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-10-04 15:52:22 +05:00
|
|
|
|
/// <summary>
|
|
|
|
|
/// Runs action like atomic operation.
|
|
|
|
|
/// wasFree is action argument indicates that semaphore was free.
|
|
|
|
|
/// It may be needed to avoid multiple operations like Refresh().
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="action">(wasFree) => {...}</param>
|
2021-12-03 15:03:33 +05:00
|
|
|
|
/// <returns>default if semaphoreTimeout. Or result of func(..)</returns>
|
2021-12-07 18:27:52 +05:00
|
|
|
|
private static async Task<T> SyncAsync<T>(Func<bool, CancellationToken, Task<T>> funcAsync,
|
|
|
|
|
CancellationToken token = default)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-03 20:08:17 +05:00
|
|
|
|
var wasFree = semaphore.CurrentCount > 0;
|
2021-12-03 15:03:33 +05:00
|
|
|
|
T result = default;
|
|
|
|
|
|
|
|
|
|
if (funcAsync is null || !await semaphore.WaitAsync(semaphoreTimeout, token).ConfigureAwait(false))
|
|
|
|
|
return result;
|
|
|
|
|
|
2021-10-03 20:08:17 +05:00
|
|
|
|
try
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-12-03 15:03:33 +05:00
|
|
|
|
result = await funcAsync.Invoke(wasFree, token);
|
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-12-07 18:27:52 +05:00
|
|
|
|
Trace.WriteLine(
|
|
|
|
|
$"{DateTime.Now:yyyy.MM.dd HH:mm:ss:fff} error in CacheTable<{nameOfTEntity}>.SyncAsync()");
|
2021-10-03 20:08:17 +05:00
|
|
|
|
Trace.WriteLine(ex.Message);
|
2021-10-04 15:52:22 +05:00
|
|
|
|
Trace.WriteLine(ex.StackTrace);
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
2021-10-03 20:08:17 +05:00
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
semaphore.Release();
|
|
|
|
|
}
|
2021-12-07 18:27:52 +05:00
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return result;
|
2021-10-04 15:52:22 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
private int InternalRefresh(bool force)
|
2021-10-04 15:52:22 +05:00
|
|
|
|
{
|
2021-11-11 10:57:08 +05:00
|
|
|
|
if (force || data.LastResreshDate + minPeriodRefresh < DateTime.Now)
|
2021-11-10 17:52:28 +05:00
|
|
|
|
{
|
|
|
|
|
cached.Clear();
|
2021-11-24 16:16:17 +05:00
|
|
|
|
IQueryable<TEntity> query = configureDbSet is null ? dbSet : configureDbSet(dbSet);
|
|
|
|
|
var entities = query.AsNoTracking().ToList();
|
2021-11-11 15:59:29 +05:00
|
|
|
|
//Trace.WriteLine($"CacheTable<{nameOfTEntity}> refresh");
|
2021-11-10 17:52:28 +05:00
|
|
|
|
cached.AddRange(entities);
|
2021-12-03 15:03:33 +05:00
|
|
|
|
data.LastResreshDate = DateTime.Now;
|
2021-11-10 17:52:28 +05:00
|
|
|
|
}
|
2021-12-07 18:27:52 +05:00
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return cached.Count;
|
2021-10-04 15:52:22 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
private async Task<int> InternalRefreshAsync(bool force, CancellationToken token = default)
|
2021-10-04 15:52:22 +05:00
|
|
|
|
{
|
2021-11-11 10:57:08 +05:00
|
|
|
|
if (force || data.LastResreshDate + minPeriodRefresh < DateTime.Now)
|
2021-11-10 17:52:28 +05:00
|
|
|
|
{
|
|
|
|
|
cached.Clear();
|
2021-11-24 16:16:17 +05:00
|
|
|
|
IQueryable<TEntity> query = configureDbSet is null ? dbSet : configureDbSet(dbSet);
|
|
|
|
|
var entities = await query.AsNoTracking()
|
2021-11-10 17:52:28 +05:00
|
|
|
|
.ToListAsync(token).ConfigureAwait(false);
|
2021-11-11 15:59:29 +05:00
|
|
|
|
//Trace.WriteLine($"CacheTable<{nameOfTEntity}> refreshAsync");
|
2021-11-10 17:52:28 +05:00
|
|
|
|
cached.AddRange(entities);
|
2021-11-11 10:57:08 +05:00
|
|
|
|
data.LastResreshDate = DateTime.Now;
|
2021-11-10 17:52:28 +05:00
|
|
|
|
}
|
2021-12-07 18:27:52 +05:00
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return cached.Count;
|
2021-10-04 15:52:22 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 17:01:18 +05:00
|
|
|
|
public int Refresh(bool force)
|
2021-12-07 18:27:52 +05:00
|
|
|
|
=> Sync((wasFree) => wasFree ? InternalRefresh(force) : 0);
|
2021-10-04 15:52:22 +05:00
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
public Task<int> RefreshAsync(bool force, CancellationToken token = default)
|
2021-10-04 15:52:22 +05:00
|
|
|
|
{
|
2021-12-07 18:27:52 +05:00
|
|
|
|
return SyncAsync(
|
|
|
|
|
async (wasFree, token) =>
|
|
|
|
|
{
|
2021-12-11 16:33:37 +05:00
|
|
|
|
return wasFree ? await InternalRefreshAsync(force, token) : 0;
|
2021-12-07 18:27:52 +05:00
|
|
|
|
}, token);
|
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-04 15:52:22 +05:00
|
|
|
|
public TEntity GetOrCreate(Func<TEntity, bool> predicate, Func<TEntity> makeNew)
|
2021-12-07 18:27:52 +05:00
|
|
|
|
=> Sync(wasFree =>
|
|
|
|
|
{
|
|
|
|
|
var result = cached.FirstOrDefault(predicate);
|
|
|
|
|
if (result != default)
|
|
|
|
|
return result;
|
2021-12-03 15:03:33 +05:00
|
|
|
|
|
2021-12-07 18:27:52 +05:00
|
|
|
|
InternalRefresh(true);
|
|
|
|
|
result = cached.FirstOrDefault(predicate);
|
|
|
|
|
if (result != default)
|
|
|
|
|
return result;
|
2021-12-03 15:03:33 +05:00
|
|
|
|
|
2021-12-07 18:27:52 +05:00
|
|
|
|
var entry = dbSet.Add(makeNew());
|
|
|
|
|
context.SaveChanges();
|
|
|
|
|
InternalRefresh(true);
|
|
|
|
|
return entry.Entity;
|
|
|
|
|
});
|
2021-10-04 15:52:22 +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;
|
|
|
|
|
|
2021-11-10 17:01:18 +05:00
|
|
|
|
Refresh(false);
|
2021-10-03 20:08:17 +05:00
|
|
|
|
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-11-10 17:01:18 +05:00
|
|
|
|
await RefreshAsync(false, token);
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return cached.FirstOrDefault();
|
2021-10-03 20:08:17 +05:00
|
|
|
|
}
|
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;
|
|
|
|
|
|
2021-11-10 17:01:18 +05:00
|
|
|
|
Refresh(false);
|
2021-10-03 20:08:17 +05:00
|
|
|
|
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-11-10 17:01:18 +05:00
|
|
|
|
await RefreshAsync(false, token);
|
2021-10-03 20:08:17 +05:00
|
|
|
|
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;
|
|
|
|
|
|
2021-11-10 17:01:18 +05:00
|
|
|
|
Refresh(false);
|
2021-10-03 20:08:17 +05:00
|
|
|
|
result = (predicate != default)
|
|
|
|
|
? cached.Where(predicate)
|
|
|
|
|
: cached;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-10-04 15:52:22 +05:00
|
|
|
|
public Task<IEnumerable<TEntity>> WhereAsync(CancellationToken token = default) =>
|
|
|
|
|
WhereAsync(default, token);
|
|
|
|
|
|
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;
|
|
|
|
|
|
2021-11-10 17:01:18 +05:00
|
|
|
|
await RefreshAsync(false, token);
|
2021-10-03 20:08:17 +05:00
|
|
|
|
result = (predicate != default)
|
|
|
|
|
? cached.Where(predicate)
|
|
|
|
|
: cached;
|
2021-04-07 18:01:56 +05:00
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
public int Upsert(TEntity entity)
|
2021-04-23 10:21:25 +05:00
|
|
|
|
{
|
2021-10-04 15:52:22 +05:00
|
|
|
|
if (entity == default)
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return 0;
|
|
|
|
|
return Sync((wasFree) =>
|
2021-07-20 12:28:56 +05:00
|
|
|
|
{
|
2021-10-04 15:52:22 +05:00
|
|
|
|
if (dbSet.Contains(entity))
|
|
|
|
|
dbSet.Update(entity);
|
2021-07-20 12:28:56 +05:00
|
|
|
|
else
|
2021-10-04 15:52:22 +05:00
|
|
|
|
dbSet.Add(entity);
|
2021-11-24 16:16:17 +05:00
|
|
|
|
var affected = context.SaveChanges();
|
|
|
|
|
if (affected > 0)
|
|
|
|
|
InternalRefresh(true);
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return affected;
|
|
|
|
|
});
|
2021-04-23 10:21:25 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
public Task<int> UpsertAsync(TEntity entity, CancellationToken token = default)
|
2021-12-07 18:27:52 +05:00
|
|
|
|
=> 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)
|
2021-12-11 16:33:37 +05:00
|
|
|
|
await InternalRefreshAsync(true, token);
|
2021-12-07 18:27:52 +05:00
|
|
|
|
return affected;
|
|
|
|
|
}, token);
|
2021-12-03 15:03:33 +05:00
|
|
|
|
|
|
|
|
|
public int Upsert(IEnumerable<TEntity> entities)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-04 15:52:22 +05:00
|
|
|
|
if (!entities.Any())
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return 0;
|
2021-10-04 15:52:22 +05:00
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return Sync((wasFree) =>
|
2021-10-04 15:52:22 +05:00
|
|
|
|
{
|
|
|
|
|
foreach (var entity in entities)
|
|
|
|
|
{
|
2022-01-05 17:50:45 +05:00
|
|
|
|
if (dbSet.Contains(entity)) // TODO: это очень медленно
|
2021-10-04 15:52:22 +05:00
|
|
|
|
dbSet.Update(entity);
|
|
|
|
|
else
|
2021-12-03 15:03:33 +05:00
|
|
|
|
dbSet.Add(entity);
|
2021-10-04 15:52:22 +05:00
|
|
|
|
}
|
2021-12-07 18:27:52 +05:00
|
|
|
|
|
2021-11-24 16:16:17 +05:00
|
|
|
|
var affected = context.SaveChanges();
|
|
|
|
|
if (affected > 0)
|
|
|
|
|
InternalRefresh(true);
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return affected;
|
2021-10-04 15:52:22 +05:00
|
|
|
|
});
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
public Task<int> UpsertAsync(IEnumerable<TEntity> entities, CancellationToken token = default)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-10-04 15:52:22 +05:00
|
|
|
|
if (!entities.Any())
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return Task.FromResult(0);
|
2021-10-04 15:52:22 +05:00
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return SyncAsync(async (wasFree, token) =>
|
|
|
|
|
{
|
2021-10-04 15:52:22 +05:00
|
|
|
|
var upsertedEntries = new List<TEntity>(entities.Count());
|
|
|
|
|
foreach (var entity in entities)
|
|
|
|
|
{
|
|
|
|
|
if (dbSet.Contains(entity))
|
|
|
|
|
dbSet.Update(entity);
|
|
|
|
|
else
|
|
|
|
|
dbSet.Add(entity);
|
|
|
|
|
}
|
2021-12-07 18:27:52 +05:00
|
|
|
|
|
2021-11-24 16:16:17 +05:00
|
|
|
|
var affected = await context.SaveChangesAsync(token).ConfigureAwait(false);
|
|
|
|
|
if (affected > 0)
|
2021-12-11 16:33:37 +05:00
|
|
|
|
await InternalRefreshAsync(true, token);
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return affected;
|
2021-10-04 15:52:22 +05:00
|
|
|
|
}, token);
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
public int Remove(Func<TEntity, bool> predicate)
|
2021-12-07 18:27:52 +05:00
|
|
|
|
=> Sync(_ =>
|
2021-10-04 15:52:22 +05:00
|
|
|
|
{
|
|
|
|
|
dbSet.RemoveRange(dbSet.Where(predicate));
|
2021-11-24 16:16:17 +05:00
|
|
|
|
var affected = context.SaveChanges();
|
|
|
|
|
if (affected > 0)
|
|
|
|
|
InternalRefresh(true);
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return affected;
|
2021-10-04 15:52:22 +05:00
|
|
|
|
});
|
2021-12-03 15:03:33 +05:00
|
|
|
|
|
|
|
|
|
public Task<int> RemoveAsync(Func<TEntity, bool> predicate, CancellationToken token = default)
|
|
|
|
|
=> SyncAsync(async (wasFree, token) =>
|
|
|
|
|
{
|
2021-10-04 15:52:22 +05:00
|
|
|
|
dbSet.RemoveRange(dbSet.Where(predicate));
|
2021-11-24 16:16:17 +05:00
|
|
|
|
var affected = await context.SaveChangesAsync(token).ConfigureAwait(false);
|
|
|
|
|
if (affected > 0)
|
2021-12-11 16:33:37 +05:00
|
|
|
|
await InternalRefreshAsync(true, token);
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return affected;
|
2021-10-04 15:52:22 +05:00
|
|
|
|
}, token);
|
|
|
|
|
|
2021-04-07 18:01:56 +05:00
|
|
|
|
public TEntity Insert(TEntity entity)
|
|
|
|
|
{
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return Sync(_ =>
|
2021-10-04 15:52:22 +05:00
|
|
|
|
{
|
|
|
|
|
var entry = dbSet.Add(entity);
|
2021-11-24 16:16:17 +05:00
|
|
|
|
var affected = context.SaveChanges();
|
|
|
|
|
if (affected > 0)
|
|
|
|
|
InternalRefresh(true);
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return entry.Entity;
|
2021-10-04 15:52:22 +05:00
|
|
|
|
});
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
public Task<TEntity> InsertAsync(TEntity entity, CancellationToken token = default)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return SyncAsync(async (wasFree, token) =>
|
2021-12-07 18:27:52 +05:00
|
|
|
|
{
|
|
|
|
|
var entry = dbSet.Add(entity);
|
|
|
|
|
var affected = await context.SaveChangesAsync(token).ConfigureAwait(false);
|
|
|
|
|
if (affected > 0)
|
2021-12-11 16:33:37 +05:00
|
|
|
|
await InternalRefreshAsync(true, token);
|
2021-12-07 18:27:52 +05:00
|
|
|
|
return entry.Entity;
|
|
|
|
|
}, token);
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-11 16:33:37 +05:00
|
|
|
|
public IEnumerable<TEntity> Insert(IEnumerable<TEntity> newEntities)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-12-11 16:33:37 +05:00
|
|
|
|
if (newEntities is null)
|
|
|
|
|
return null;
|
|
|
|
|
var count = newEntities.Count();
|
|
|
|
|
if (count == 0)
|
|
|
|
|
return null;
|
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return Sync(_ =>
|
2021-12-07 18:27:52 +05:00
|
|
|
|
{
|
2021-12-11 16:33:37 +05:00
|
|
|
|
var entries = new List<Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<TEntity>>(count);
|
|
|
|
|
foreach (var newEntity in newEntities)
|
|
|
|
|
{
|
|
|
|
|
var entry = dbSet.Add(newEntity);
|
|
|
|
|
entries.Add(entry);
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-07 18:27:52 +05:00
|
|
|
|
var affected = context.SaveChanges();
|
|
|
|
|
if (affected > 0)
|
|
|
|
|
InternalRefresh(true);
|
2021-12-11 16:33:37 +05:00
|
|
|
|
else
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
return entries.Select(e => e.Entity);
|
2021-12-07 18:27:52 +05:00
|
|
|
|
});
|
2021-04-07 18:01:56 +05:00
|
|
|
|
}
|
|
|
|
|
|
2021-12-11 16:33:37 +05:00
|
|
|
|
public Task<IEnumerable<TEntity>> InsertAsync(IEnumerable<TEntity> newEntities, CancellationToken token = default)
|
2021-04-07 18:01:56 +05:00
|
|
|
|
{
|
2021-12-11 16:33:37 +05:00
|
|
|
|
if(newEntities is null)
|
|
|
|
|
return null;
|
|
|
|
|
var count = newEntities.Count();
|
|
|
|
|
if (count == 0)
|
|
|
|
|
return null;
|
|
|
|
|
|
2021-12-03 15:03:33 +05:00
|
|
|
|
return SyncAsync(async (wasFree, token) =>
|
|
|
|
|
{
|
2021-12-11 16:33:37 +05:00
|
|
|
|
var entries = new List<Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<TEntity>>(count);
|
|
|
|
|
foreach (var newEntity in newEntities)
|
|
|
|
|
{
|
|
|
|
|
var entry = dbSet.Add(newEntity);
|
|
|
|
|
entries.Add(entry);
|
|
|
|
|
}
|
2021-12-03 15:03:33 +05:00
|
|
|
|
var affected = await context.SaveChangesAsync(token).ConfigureAwait(false);
|
|
|
|
|
if (affected > 0)
|
2021-12-11 16:33:37 +05:00
|
|
|
|
await InternalRefreshAsync(true, token);
|
|
|
|
|
else
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
return entries.Select(e => e.Entity);
|
2021-10-04 15:52:22 +05:00
|
|
|
|
}, token);
|
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
|
|
|
|
}
|
2021-12-07 18:27:52 +05:00
|
|
|
|
}
|