Files
GPW/GPW.CORE.UI/Data/GpwDataService.cs
T

723 lines
28 KiB
C#

using GPW.CORE.Data.DbModels;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using NLog;
using System.Diagnostics;
using System.Text;
namespace GPW.CORE.UI.Data
{
public class GpwDataService : IDisposable
{
#region Private Fields
private static IConfiguration _configuration = null!;
private static ILogger<GpwDataService> _logger = null!;
private static JsonSerializerSettings? JSSettings;
private static NLog.Logger Log = LogManager.GetCurrentClassLogger();
//private readonly IEmailSender _emailSender;
//private readonly UserManager<IdentityUser> _userManager;
private readonly IDistributedCache distributedCache;
private readonly IMemoryCache memoryCache;
private List<string> cachedDataList = new List<string>();
/// <summary>
/// Durata assoluta massima della cache IN SECONDI
/// </summary>
private int chAbsExp = 60 * 5;
/// <summary>
/// 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)
/// </summary>
private int chSliExp = 60 * 1;
#endregion Private Fields
#region Protected Fields
protected const string rKeyCalcOreProj = "Cache:CalcOreProj";
protected const string rKeyCliAll = "Cache:CliAll";
protected const string rKeyDailyData = "Cache:DailyData";
protected const string rKeyDipendenti = "Cache:Dipendenti";
protected const string rKeyFasiAll = "Cache:FasiAll";
protected const string rKeyGrpAll = "Cache:GrpAll";
protected const string rKeyParetoRegAtt = "Cache:ParetoRegAtt";
protected const string rKeyProjAll = "Cache:ProjAll";
protected const string rKeyWeekStats = "Cache:WeekStats";
protected static string connStringBBM = "";
#endregion Protected Fields
#region Public Fields
public static CORE.Data.Controllers.GPWController dbController = null!;
#endregion Public Fields
#region Public Constructors
public GpwDataService(IConfiguration configuration, ILogger<GpwDataService> 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("GPW.DB");
if (string.IsNullOrEmpty(connStr))
{
_logger.LogError("ConnString empty!");
}
else
{
dbController = new CORE.Data.Controllers.GPWController(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
/// <summary>
/// Recupera l'elenco fasi (tutte)
/// </summary>
/// <returns></returns>
public async Task<List<AnagClientiModel>> AnagClientiAll()
{
List<AnagClientiModel>? dbResult = new List<AnagClientiModel>();
string currKey = $"{rKeyCliAll}";
string rawData;
var redisDataList = await distributedCache.GetAsync(currKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<AnagClientiModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.AnagClientiAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(currKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB + caching per AnagClientiAll: {ts.TotalMilliseconds} ms");
}
if (dbResult == null)
{
dbResult = new List<AnagClientiModel>();
}
return await Task.FromResult(dbResult);
}
/// <summary>
/// Recupera l'elenco fasi (tutte)
/// </summary>
/// <returns></returns>
public async Task<List<AnagFasiModel>> AnagFasiAll()
{
List<AnagFasiModel>? dbResult = new List<AnagFasiModel>();
string currKey = $"{rKeyFasiAll}";
string rawData;
var redisDataList = await distributedCache.GetAsync(currKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<AnagFasiModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.AnagFasiAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(currKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB + caching per AnagFasiAll: {ts.TotalMilliseconds} ms");
}
if (dbResult == null)
{
dbResult = new List<AnagFasiModel>();
}
return await Task.FromResult(dbResult);
}
/// <summary>
/// Recupera info x una specifica FASE
/// </summary>
/// <returns></returns>
public async Task<AnagFasiModel?> AnagFasiSearch(int idxFase)
{
AnagFasiModel dbResult = new AnagFasiModel();
var rawData = await AnagFasiAll();
if (rawData != null)
{
dbResult = rawData.FirstOrDefault(x => x.IdxFase == idxFase);
}
return await Task.FromResult(dbResult);
}
/// <summary>
/// Recupera l'elenco gruppi (tutti)
/// </summary>
/// <returns></returns>
public async Task<List<AnagGruppiModel>> AnagGruppiAll()
{
List<AnagGruppiModel>? dbResult = new List<AnagGruppiModel>();
string currKey = $"{rKeyGrpAll}";
string rawData;
var redisDataList = await distributedCache.GetAsync(currKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<AnagGruppiModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.AnagGruppiAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(currKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB + caching per AnagGruppiAll: {ts.TotalMilliseconds} ms");
}
if (dbResult == null)
{
dbResult = new List<AnagGruppiModel>();
}
return await Task.FromResult(dbResult);
}
/// <summary>
/// Recupera l'elenco progetti (tutti)
/// </summary>
/// <returns></returns>
public async Task<List<AnagProgettiModel>> AnagProjAll()
{
List<AnagProgettiModel>? dbResult = new List<AnagProgettiModel>();
string currKey = $"{rKeyProjAll}";
string rawData;
var redisDataList = await distributedCache.GetAsync(currKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<AnagProgettiModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.AnagProjAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(currKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB + caching per AnagProjAll: {ts.TotalMilliseconds} ms");
}
if (dbResult == null)
{
dbResult = new List<AnagProgettiModel>();
}
return await Task.FromResult(dbResult);
}
/// <summary>
/// Vista dati per progetto con totalizzaizone ore budget/real
/// </summary>
/// <param name="idxProj"></param>
/// <returns></returns>
public async Task<CalcOreProgettiModel> CalcOreProj(int idxProj)
{
CalcOreProgettiModel? dbResult = new CalcOreProgettiModel();
string currKey = $"{rKeyCalcOreProj}:{idxProj}";
string rawData;
var redisDataList = await distributedCache.GetAsync(currKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<CalcOreProgettiModel>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.CalcOreProj(idxProj);
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(currKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB + caching per CalcOreProj: {ts.TotalMilliseconds} ms");
}
if (dbResult == null)
{
dbResult = new CalcOreProgettiModel();
}
return await Task.FromResult(dbResult);
}
/// <summary>
/// Registra in cache chiave se non fosse già in elenco
/// </summary>
/// <param name="newKey"></param>
protected void trackCache(string newKey)
{
if (!cachedDataList.Contains(newKey))
{
cachedDataList.Add(newKey);
}
}
/// <summary>
/// Recupera l'elenco dei dettagli giornalieri attività di un dipendente, dato periodo riferimento
/// </summary>
/// <param name="idxDipendente">Dipendente interessato</param>
/// <param name="dtInizio">Data di riferimento (ultima/corrente)</param>
/// <param name="dtFine">NUm settimane precedenti da recuperare</param>
/// <returns></returns>
public async Task<List<CORE.Data.DTO.DailyDataDTO>> DailyDetails(int idxDipendente, DateTime dtInizio, DateTime dtFine)
{
List<CORE.Data.DTO.DailyDataDTO>? dbResult = new List<CORE.Data.DTO.DailyDataDTO>();
string currKey = $"{rKeyDailyData}:{idxDipendente}:{dtInizio.ToString("yyyy-MM-dd")}:{dtFine.ToString("yyyy-MM-dd")}";
trackCache(currKey);
string rawData;
var redisDataList = await distributedCache.GetAsync(currKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<CORE.Data.DTO.DailyDataDTO>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.DailyDetails(idxDipendente, dtInizio, dtFine);
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(currKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB + caching per DailyDetails: {ts.TotalMilliseconds} ms");
}
if (dbResult == null)
{
dbResult = new List<CORE.Data.DTO.DailyDataDTO>();
}
return await Task.FromResult(dbResult);
}
public async Task<List<DipendentiModel>> DipendentiGetAll()
{
List<DipendentiModel>? dbResult = new List<DipendentiModel>();
string rawData;
var redisDataList = await distributedCache.GetAsync(rKeyDipendenti);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<DipendentiModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.DipendentiGetAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(rKeyDipendenti, redisDataList, cacheOpt(false));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB + caching per DipendentiGetAll: {ts.TotalMilliseconds} ms");
}
if (dbResult == null)
{
dbResult = new List<DipendentiModel>();
}
return await Task.FromResult(dbResult);
}
public void Dispose()
{
// Clear database controller
dbController.Dispose();
}
/// <summary>
/// invalida tutta la cache in caso di update
/// </summary>
/// <returns></returns>
public async Task InvalidateAllCache()
{
await distributedCache.RemoveAsync(rKeyDipendenti);
await distributedCache.RemoveAsync(rKeyFasiAll);
foreach (var item in cachedDataList)
{
await distributedCache.RemoveAsync(item);
}
cachedDataList = new List<string>();
}
/// <summary>
/// Statistiche ultime settimane
/// </summary>
/// <param name="idxDipendente"></param>
/// <param name="dtRif"></param>
/// <param name="numWeek"></param>
/// <returns></returns>
public async Task<List<CORE.Data.DTO.WeekStatDTO>> LastWeeks(int idxDipendente, DateTime dtRif, int numWeek)
{
List<CORE.Data.DTO.WeekStatDTO>? dbResult = new List<CORE.Data.DTO.WeekStatDTO>();
string currKey = $"{rKeyWeekStats}:{idxDipendente}:{dtRif.ToString("yyyy-MM-dd")}:{numWeek}";
trackCache(currKey);
string rawData;
var redisDataList = await distributedCache.GetAsync(currKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<CORE.Data.DTO.WeekStatDTO>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.WeekOverview(idxDipendente, dtRif, numWeek);
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(currKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB + caching per LastWeeks: {ts.TotalMilliseconds} ms");
}
if (dbResult == null)
{
dbResult = new List<CORE.Data.DTO.WeekStatDTO>();
}
return await Task.FromResult(dbResult);
}
/// <summary>
/// Elenco pareto progetti ordinati da filtro
/// </summary>
/// <param name="idxDip"></param>
/// <param name="numDayPrev"></param>
/// <param name="maxResult"></param>
/// <returns></returns>
public async Task<List<ParetoRegAttModel>> ParetoRegAtt(int idxDip, int numDayPrev, int maxResult)
{
List<ParetoRegAttModel>? dbResult = new List<ParetoRegAttModel>();
string currKey = $"{rKeyParetoRegAtt}:{idxDip}:{numDayPrev}:{maxResult}";
DateTime dataFine = DateTime.Today.AddDays(1);
DateTime dataInizio = dataFine.AddDays(-numDayPrev);
string rawData;
var redisDataList = await distributedCache.GetAsync(currKey);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<ParetoRegAttModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.ParetoRegAtt(idxDip, dataInizio, dataFine, maxResult);
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(currKey, redisDataList, cacheOpt(true));
stopWatch.Stop();
TimeSpan ts = stopWatch.Elapsed;
Log.Trace($"Effettuata lettura da DB + caching per ParetoRegAtt: {ts.TotalMilliseconds} ms");
}
if (dbResult == null)
{
dbResult = new List<ParetoRegAttModel>();
}
return await Task.FromResult(dbResult);
}
public async Task<bool> RegAttDelete(RegAttivitaModel currItem)
{
bool answ = false;
try
{
dbController.RegAttDelete(currItem);
// invalido la cache...
await InvalidateAllCache();
answ = true;
}
catch
{ }
return answ;
}
public async Task<RegAttivitaModel> RegAttLastByDip(int IdxDipendente)
{
RegAttivitaModel dbResult = dbController.RegAttLastByDip(IdxDipendente);
return await Task.FromResult(dbResult);
}
public async Task<bool> RegAttUpdate(RegAttivitaModel currItem)
{
bool answ = false;
try
{
dbController.RegAttUpdate(currItem);
// invalido la cache...
await InvalidateAllCache();
answ = true;
}
catch
{ }
return answ;
}
#if false
public async Task<List<MaterialDTO>> MaterialsGetAll()
{
List<MaterialDTO>? dbResult = new List<MaterialDTO>();
string rawData;
var redisDataList = await distributedCache.GetAsync(rKeyDipendenti);
if (redisDataList != null)
{
rawData = Encoding.UTF8.GetString(redisDataList);
dbResult = JsonConvert.DeserializeObject<List<MaterialDTO>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
dbResult = dbController.MaterialsGetAll();
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
redisDataList = Encoding.UTF8.GetBytes(rawData);
await distributedCache.SetAsync(rKeyDipendenti, 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<MaterialDTO>();
}
return await Task.FromResult(dbResult);
}
public async Task<List<MovMagModel>> MovMagGetFilt(int RemnId, int numShow)
{
List<MovMagModel> dbResult = new List<MovMagModel>();
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<bool> AddPrintJob(int RemnId)
{
bool answ = dbController.AddPrintJob("docRemnant", $"{RemnId}", "queueRemnants");
return await Task.FromResult(answ);
}
public async Task<List<RemnantsModel>> RemnantsGetFilt(int matId, int minQty)
{
List<RemnantsModel>? dbResult = new List<RemnantsModel>();
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<List<RemnantsModel>>(rawData);
}
else
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
var rawList = dbController.RemnantsGetFilt(matId, minQty);
dbResult = rawList.OrderBy(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<RemnantsModel>();
}
return await Task.FromResult(dbResult);
}
public async Task<bool> 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<bool> 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<bool> 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);
}
#endif
public void rollBackEdit(object item)
{
dbController.rollBackEntity(item);
}
#if false
public async Task<RemnantsModel> 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<RemnantsModel>(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);
}
#endif
public async Task<bool> TimbratureDelete(TimbratureModel currItem)
{
bool answ = false;
try
{
dbController.TimbratureDelete(currItem);
// invalido la cache...
await InvalidateAllCache();
answ = true;
}
catch
{ }
return answ;
}
public async Task<bool> TimbratureUpdate(TimbratureModel currItem)
{
bool answ = false;
try
{
dbController.TimbratureUpdate(currItem);
// invalido la cache...
await InvalidateAllCache();
answ = true;
}
catch
{ }
return answ;
}
#endregion Public Methods
}
}