using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Memory; using Newtonsoft.Json; using NKC.Data.DbModels; using NKC.Data.DTO; using NLog; using System.Diagnostics; using System.Text; namespace REMAN.Data { public class RDataService : IDisposable { #region Private Fields private static IConfiguration _configuration = null!; private static ILogger _logger = null!; private static JsonSerializerSettings? JSSettings; private static NLog.Logger Log = LogManager.GetCurrentClassLogger(); //private readonly IEmailSender _emailSender; //private readonly UserManager _userManager; private readonly IDistributedCache distributedCache; private readonly IMemoryCache memoryCache; private List cachedDataList = new List(); /// /// Durata assoluta massima della cache IN SECONDI /// private int chAbsExp = 60 * 5; /// /// Durata della cache IN SECONDI in modalità inattiva (non acceduta) prima di venire rimossa /// NON estende oltre il tempo massimo di validità della cache (chAbsExp) /// private int chSliExp = 60 * 1; #endregion Private Fields #region Protected Fields protected const string rKeyMaterials = "Cache:Materials"; protected const string rKeyQrRemnants = "Cache:QrRemnants"; protected const string rKeyRemnants = "Cache:Remnants"; protected static string connStringBBM = ""; #endregion Protected Fields #region Public Fields public static NKC.Data.Controllers.NKCController dbController = null!; #endregion Public Fields #region Public Constructors public RDataService(IConfiguration configuration, ILogger logger, IMemoryCache memoryCache, IDistributedCache distributedCache) { _logger = logger; _configuration = configuration; // conf cache this.memoryCache = memoryCache; this.distributedCache = distributedCache; // json serializer... // FIX errore loop circolare // https://www.ryadel.com/en/jsonserializationexception-self-referencing-loop-detected-error-fix-entity-framework-asp-net-core/ JSSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; // conf DB string connStr = _configuration.GetConnectionString("NKC.DB"); if (string.IsNullOrEmpty(connStr)) { _logger.LogError("ConnString empty!"); } else { dbController = new NKC.Data.Controllers.NKCController(configuration); } } #endregion Public Constructors #region Private Methods private DistributedCacheEntryOptions cacheOpt(bool fastCache) { var numSecAbsExp = fastCache ? chAbsExp : chAbsExp * 10; var numSecSliExp = fastCache ? chSliExp : chSliExp * 10; return new DistributedCacheEntryOptions().SetAbsoluteExpiration(DateTime.Now.AddSeconds(numSecAbsExp)).SetSlidingExpiration(TimeSpan.FromSeconds(numSecSliExp)); } #endregion Private Methods #region Public Methods public void Dispose() { // Clear database controller dbController.Dispose(); } /// /// invalida tutta la cache in caso di update /// /// public async Task InvalidateAllCache() { await distributedCache.RemoveAsync(rKeyMaterials); await distributedCache.RemoveAsync(rKeyRemnants); foreach (var item in cachedDataList) { await distributedCache.RemoveAsync(item); } cachedDataList = new List(); } public async Task> MaterialsGetAll() { List? dbResult = new List(); string rawData; var redisDataList = await distributedCache.GetAsync(rKeyMaterials); if (redisDataList != null) { rawData = Encoding.UTF8.GetString(redisDataList); dbResult = JsonConvert.DeserializeObject>(rawData); } else { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.MaterialsGetAll(); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); redisDataList = Encoding.UTF8.GetBytes(rawData); await distributedCache.SetAsync(rKeyMaterials, redisDataList, cacheOpt(false)); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB + caching per GetMaterials: {ts.TotalMilliseconds} ms"); } if (dbResult == null) { dbResult = new List(); } return await Task.FromResult(dbResult); } public async Task> MovMagGetFilt(int RemnId, int numShow) { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.MovMagGetFilt(RemnId, numShow); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB + caching per MovMagGetFilt: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task AddPrintJob(int RemnId) { bool answ = dbController.AddPrintJob("docRemnant", $"{RemnId}", "queueRemnants"); return await Task.FromResult(answ); } public async Task> RemnantsGetFilt(int matId, int minQty) { List? dbResult = new List(); string rawData; string cacheKey = $"{rKeyRemnants}:{matId}:{minQty}"; if (!cachedDataList.Contains(cacheKey)) { cachedDataList.Add(cacheKey); } var redisDataList = await distributedCache.GetAsync(cacheKey); if (redisDataList != null) { rawData = Encoding.UTF8.GetString(redisDataList); dbResult = JsonConvert.DeserializeObject>(rawData); } else { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); var rawList = dbController.RemnantsGetFilt(matId, minQty); dbResult = rawList.OrderBy(r => r.RemDtmx).ThenBy(o => o.Area).ToList(); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); redisDataList = Encoding.UTF8.GetBytes(rawData); await distributedCache.SetAsync(cacheKey, redisDataList, cacheOpt(false)); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB + caching per RemnantsGetAll: {ts.TotalMilliseconds} ms"); } if (dbResult == null) { dbResult = new List(); } return await Task.FromResult(dbResult); } public async Task RemnantsIsDupl(RemnantsModel currItem) { bool answ = false; var rawList = dbController.RemnantsGetFilt(currItem.MatID, 0); var duplicati = rawList .Where(x => x.RemnID != currItem.RemnID && x.LMm == currItem.LMm && x.WMm == currItem.WMm && x.TMm == currItem.TMm) .ToList(); answ = duplicati.Count > 0; return await Task.FromResult(answ); } public async Task RemnantsMovMag(RemnantsModel currItem, string userId, int deltaQty) { bool done = false; try { // recupero item da DB var currRecord = dbController.RemnantGetByid(currItem.RemnID); if (currRecord != null && currRecord.RemnID == currItem.RemnID) { // modifico qty entro limiti >=0.. if (currRecord.QtyAvail + deltaQty >= 0) { currRecord.QtyAvail = currRecord.QtyAvail + deltaQty; done = dbController.RemnantsUpsert(currRecord, userId); await InvalidateAllCache(); } } } catch (Exception exc) { Log.Error($"Eccezione in RemnantsMovMag:{Environment.NewLine}{exc}"); } return await Task.FromResult(done); } public async Task RemnantsUpsert(RemnantsModel currItem, string userId) { bool done = false; try { done = dbController.RemnantsUpsert(currItem, userId); await InvalidateAllCache(); } catch (Exception exc) { Log.Error($"Eccezione in RemnantsUpsert:{Environment.NewLine}{exc}"); } return await Task.FromResult(done); } public void rollBackEdit(object item) { dbController.rollBackEntity(item); } public async Task SearchQrRemnant(string QrCode) { RemnantsModel? answ = new RemnantsModel(); string rawData = ""; string cacheKey = $"{rKeyQrRemnants}:{QrCode}"; // cerco in redis var redisData = await distributedCache.GetAsync(cacheKey); if (redisData != null) { rawData = Encoding.UTF8.GetString(redisData); answ = JsonConvert.DeserializeObject(rawData); } // se non trovo cerco su DB else { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); var foundItem = dbController.RemnantGetByQr(QrCode); if (foundItem != null && foundItem.RemDtmx == QrCode) { rawData = JsonConvert.SerializeObject(foundItem, JSSettings); redisData = Encoding.UTF8.GetBytes(rawData); await distributedCache.SetAsync(cacheKey, redisData, cacheOpt(false)); answ = foundItem; } stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB + caching per SearchQrRemnant: {ts.TotalMilliseconds} ms"); } if (answ == null) { answ = new RemnantsModel(); } return await Task.FromResult(answ); } #endregion Public Methods } }