using Microsoft.Extensions.Configuration; using System; using Microsoft.Extensions.Logging; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using NLog; using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Memory; using System.Diagnostics; using System.Text; using Newtonsoft.Json; using System.Diagnostics.Eventing.Reader; using MP.AppAuth.Models; using StackExchange.Redis; using MP.AppAuth.Controllers; namespace MP.Land.Data { public class AppAuthService : IDisposable { #region Public Fields // diritti (cablòato) public const string RoleSuperAdmin = "MoonPro_SuperAdmin"; public static AppAuth.Controllers.AppAuthController dbController; public static AppAuth.Controllers.MPController MpDbController; public static AppAuth.Controllers.MPUserController userController; #endregion Public Fields #region Public Constructors public AppAuthService(IConfiguration configuration, ILogger logger, IConnectionMultiplexer redisConnMult, IMemoryCache memoryCache, IDistributedCache distributedCache) { _logger = logger; _configuration = configuration; // cod app CodApp = _configuration.GetValue("ServerConf:CodApp"); Modulo = _configuration.GetValue("ServerConf:Modulo"); // Conf cache redisConn = redisConnMult; redisDb = this.redisConn.GetDatabase(); // 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 cache this.memoryCache = memoryCache; this.distributedCache = distributedCache; // conf DB string connStr = _configuration.GetConnectionString("MP.Land"); if (string.IsNullOrEmpty(connStr)) { _logger.LogError("ConnString empty!"); } else { dbController = new AppAuthController(configuration); MpDbController = new MPController(configuration); userController = new MPUserController(configuration); _logger.LogInformation("DbController OK"); } } #endregion Public Constructors #region Public Methods public async Task> AnagGruppiAll() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.AnagGruppiGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagGruppiAll: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task> AnagGruppiFilt(string codTipo) { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.AnagGruppiFilt(codTipo); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagGruppiFilt: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task AnagKeyValAdd(AnagKeyValueModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.AnagKeyValuesUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"AnagKeyValAdd | Aggiunto rec | NomeVar: {currRec.NomeVar} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task AnagKeyValDelete(string NomeVar) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.AnagKeyValuesDelete(NomeVar); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"AnagKeyValDelete | Effettuata cancellazione | NomeVar: {NomeVar} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task> AnagKeyValList() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.AnagKeyValuesGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagKeyValList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task AnagKeyValUpd(AnagKeyValueModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.AnagKeyValuesUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"AnagKeyValUpd | Effettuata modifica | NomeVar: {currRec.NomeVar} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task> AnagOperByGroupList(string codGruppo, string searchVal) { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); var rawData = dbController .AnagOpByGruppoGetFilt(codGruppo, searchVal); dbResult = rawData .GroupBy(user => user.MatrOpr) .Select(grp => grp.First()) .ToList(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagOperByGroupList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task> AnagOperList(string searchVal) { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.AnagOpGetAll(searchVal); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per AnagOperList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task ConfigAdd(ConfigModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.ConfigUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"ConfigAdd | Aggiunto rec | Chiave: {currRec.Chiave} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task ConfigDelete(string Chiave) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.ConfigDelete(Chiave); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"ConfigDelete | Effettuata cancellazione | Chiave: {Chiave} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task> ConfigList() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.ConfigGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per ConfigList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task ConfigUpd(ConfigModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.ConfigUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"ConfigUpd | Effettuata modifica | Chiave: {currRec.Chiave} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } /// /// Elenco diritti dato utente /// /// /// public async Task> DirittiGetByUser(string UserName) { string source = "DB"; List? dbResult = new List(); string currKey = $"{rKeyDirittiUser}:{UserName}"; Stopwatch sw = new Stopwatch(); sw.Start(); string? rawData = await redisDb.StringGetAsync(currKey); if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); if (tempResult == null) { dbResult = new List(); } else { dbResult = tempResult; } } else { // recupero diritti utente dbResult = userController.DirittiUtente(UserName, Modulo); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); } if (dbResult == null) { dbResult = new List(); } sw.Stop(); Log.Debug($"DirittiGetByUser | {source} | {sw.ElapsedMilliseconds} ms"); return dbResult; } public void Dispose() { // Clear database controller dbController.Dispose(); MpDbController.Dispose(); } /// /// Elenco permessi dato utente /// /// /// public async Task> PermessiGetByUser(string UserName) { string source = "DB"; List? dbResult = new List(); string currKey = $"{rKeyPermUser}:{UserName}"; Stopwatch sw = new Stopwatch(); sw.Start(); string? rawData = await redisDb.StringGetAsync(currKey); if (!string.IsNullOrEmpty(rawData)) { source = "REDIS"; var tempResult = JsonConvert.DeserializeObject>(rawData); if (tempResult == null) { dbResult = new List(); } else { dbResult = tempResult; } } else { // recupero diritti utente var userRightList = userController.DirittiUtente(UserName, Modulo); // proietto come funzioni... var ListFunc = userRightList.Select(x => x.Funzione).ToList(); // trasformo i permessi utente if (ListFunc == null) { ListFunc = new List(); } dbResult = dbController.PermessiGetByFunc(ListFunc); rawData = JsonConvert.SerializeObject(dbResult, JSSettings); await redisDb.StringSetAsync(currKey, rawData, UltraLongCache); } if (dbResult == null) { dbResult = new List(); } sw.Stop(); Log.Debug($"PermessiGetByUser | {source} | {sw.ElapsedMilliseconds} ms"); return dbResult; } public async Task ResetCache() { string cacheKey = ":MP:VOCAB"; await distributedCache.RemoveAsync(cacheKey); // reset in RAM Vocabolario = new Dictionary(); CheckVoc(); } public string Traduci(string lemma, string lingua = "IT") { string answ = $"__{lemma}__"; string keyReq = $"{lingua}#{lemma}"; CheckVoc(); // cerco in vocabolario... if (Vocabolario.ContainsKey(keyReq)) { answ = Vocabolario[keyReq]; } return answ; } public async Task> UpdManList() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = dbController.UpdManGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per UpdManList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task VocabolarioAdd(VocabolarioModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.VocabolarioUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"VocabolarioAdd | Aggiunto rec | lingua: {currRec.Lingua} | lemma: {currRec.Lemma} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task VocabolarioDelete(VocabolarioModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.VocabolarioDelete(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"VocabolarioDelete | Effettuata cancellazione | lingua: {currRec.Lingua} | lemma: {currRec.Lemma} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } public async Task> VocabolarioList() { List dbResult = new List(); Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); dbResult = MpDbController.VocabolarioGetAll(); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuata lettura da DB per VocabolarioList: {ts.TotalMilliseconds} ms"); return await Task.FromResult(dbResult); } public async Task VocabolarioUpd(VocabolarioModel currRec) { bool answ = false; Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); answ = MpDbController.VocabolarioUpsert(currRec); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"VocabolarioUpd | Effettuata modifica | lingua: {currRec.Lingua} | lemma: {currRec.Lemma} | durata: {ts.TotalMilliseconds} ms"); return await Task.FromResult(answ); } #endregion Public Methods #region Protected Methods protected void CheckVoc() { string cacheKey = ":MP:VOCAB"; string rawData; if (Vocabolario.Count == 0) { var redisDataList = distributedCache.Get(cacheKey); if (redisDataList != null) { rawData = Encoding.UTF8.GetString(redisDataList); Vocabolario = JsonConvert.DeserializeObject>(rawData); } else { Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); Vocabolario = dbController.VocabolarioGetAll().ToDictionary(x => $"{x.Lingua}#{x.Lemma}", x => x.Traduzione); rawData = JsonConvert.SerializeObject(Vocabolario); redisDataList = Encoding.UTF8.GetBytes(rawData); distributedCache.Set(cacheKey, redisDataList, cacheOptLong); stopWatch.Stop(); TimeSpan ts = stopWatch.Elapsed; Log.Trace($"Effettuato rilettura da DB + caching per VocabolarioModel: {ts.TotalMilliseconds} ms"); } } } #endregion Protected Methods #region Private Fields // gestione key Redis private const string redisBaseAddr = "MP:LAND"; private const string rKeyDirittiUser = $"{redisBaseAddr}:DIR_USER"; private const string rKeyPermUser = $"{redisBaseAddr}:PERM_USER"; private static IConfiguration _configuration; private static ILogger _logger; private static List ElencoMacchine = new List(); private static JsonSerializerSettings? JSSettings; private static Logger Log = LogManager.GetCurrentClassLogger(); private static string Modulo = ""; private readonly IDistributedCache distributedCache; private readonly IMemoryCache memoryCache; /// /// Durata cache lunga IN SECONDI /// private int cacheTtlLong = 60 * 5; /// /// Durata cache breve IN SECONDI /// private int cacheTtlShort = 60 * 1; /// /// Durata assoluta massima della cache /// private int chAbsExp = 15; /// /// Durata della cache in modalità inattiva (non acceduta) prima di venire rimossa NON /// estende oltre il tempo massimo di validità della cache (chAbsExp) /// private int chSliExp = 5; /// /// Oggetto per connessione a REDIS /// private IConnectionMultiplexer redisConn; //ISubscriber sub = redis.GetSubscriber(); /// /// Oggetto DB redis da impiegare x chiamate R/W /// private IDatabase redisDb = null!; private Random rnd = new Random(); private Dictionary Vocabolario = new Dictionary(); #endregion Private Fields #region Private Properties private DistributedCacheEntryOptions cacheOpt { get { return new DistributedCacheEntryOptions().SetAbsoluteExpiration(DateTime.Now.AddMinutes(chAbsExp)).SetSlidingExpiration(TimeSpan.FromMinutes(chSliExp)); } } private DistributedCacheEntryOptions cacheOptLong { get { return new DistributedCacheEntryOptions().SetAbsoluteExpiration(DateTime.Now.AddMinutes(chAbsExp * 10)).SetSlidingExpiration(TimeSpan.FromMinutes(chSliExp)); } } private string CodApp { get; set; } = ""; /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan FastCache { get => TimeSpan.FromSeconds(cacheTtlShort * rnd.Next(900, 1100) / 1000); } /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan LongCache { get => TimeSpan.FromSeconds(cacheTtlLong * rnd.Next(900, 1100) / 1000); } /// /// Durata cache lunga (+ perturbazione percentuale +/-10%) /// private TimeSpan UltraLongCache { get => TimeSpan.FromSeconds(cacheTtlLong * 10 * rnd.Next(900, 1100) / 1000); } #endregion Private Properties } }