115 lines
3.3 KiB
C#
115 lines
3.3 KiB
C#
using Data.Abstractions;
|
|
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
using ILogger = Microsoft.Extensions.Logging.ILogger;
|
|
|
|
namespace Data.Helpers
|
|
{
|
|
public class LookupCache<T> : ILookupCache<T> where T : class, IUniqueId
|
|
{
|
|
private readonly ILogger _logger;
|
|
private readonly IDatabaseContextFactory _contextFactory;
|
|
private Dictionary<long, T> _cachedItems;
|
|
private readonly SemaphoreSlim _onOperation = new SemaphoreSlim(1, 1);
|
|
|
|
public LookupCache(ILogger<LookupCache<T>> logger, IDatabaseContextFactory contextFactory)
|
|
{
|
|
_logger = logger;
|
|
_contextFactory = contextFactory;
|
|
}
|
|
|
|
public async Task<T> AddAsync(T item)
|
|
{
|
|
await _onOperation.WaitAsync();
|
|
T existingItem = null;
|
|
|
|
if (_cachedItems.ContainsKey(item.Id))
|
|
{
|
|
existingItem = _cachedItems[item.Id];
|
|
}
|
|
|
|
if (existingItem != null)
|
|
{
|
|
_logger.LogDebug("Cached item already added for {type} {id} {value}", typeof(T).Name, item.Id,
|
|
item.Value);
|
|
_onOperation.Release();
|
|
return existingItem;
|
|
}
|
|
|
|
try
|
|
{
|
|
_logger.LogDebug("Adding new {type} with {id} {value}", typeof(T).Name, item.Id, item.Value);
|
|
await using var context = _contextFactory.CreateContext();
|
|
context.Set<T>().Add(item);
|
|
await context.SaveChangesAsync();
|
|
_cachedItems.Add(item.Id, item);
|
|
return item;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Could not add item to cache for {type}", typeof(T).Name);
|
|
throw new Exception("Could not add item to cache");
|
|
}
|
|
finally
|
|
{
|
|
if (_onOperation.CurrentCount == 0)
|
|
{
|
|
_onOperation.Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task<T> FirstAsync(Func<T, bool> query)
|
|
{
|
|
await _onOperation.WaitAsync();
|
|
|
|
try
|
|
{
|
|
var cachedResult = _cachedItems.Values.Where(query);
|
|
|
|
if (cachedResult.Any())
|
|
{
|
|
return cachedResult.FirstOrDefault();
|
|
}
|
|
}
|
|
|
|
catch
|
|
{
|
|
}
|
|
|
|
finally
|
|
{
|
|
if (_onOperation.CurrentCount == 0)
|
|
{
|
|
_onOperation.Release(1);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public IEnumerable<T> GetAll()
|
|
{
|
|
return _cachedItems.Values;
|
|
}
|
|
|
|
public async Task InitializeAsync()
|
|
{
|
|
try
|
|
{
|
|
await using var context = _contextFactory.CreateContext(false);
|
|
_cachedItems = await context.Set<T>().ToDictionaryAsync(item => item.Id);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Could not initialize caching for {cacheType}", typeof(T).Name);
|
|
}
|
|
}
|
|
}
|
|
}
|