using Blazored.LocalStorage; using Core; using LiMan.DB; using LiMan.GLS; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime; using System.Threading.Tasks; namespace LiMan.UI.Data { public class MessageService { #region Public Constructors public MessageService(ILocalStorageService genLocalStorage, IConnectionMultiplexer redisConnMult) { // gestione sessioni in browser localStore = genLocalStorage; // 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 }; } #endregion Public Constructors #region Public Events public event Action EA_FilterUpdated; public event Action EA_HideSearch; public event Action EA_PageUpdated; public event Action EA_SearchUpdated; public event Action EA_SelCodApp; public event Action EA_SelCodImp; public event Action EA_SelCodInst; public event Action EA_ShowSearch; public event Action EA_TaskUpdate; public event Action EA_UserNameUpd; #endregion Public Events #region Public Properties public SelectNext DetailDBFilter { get => _detailFilterNext; set { if (_detailFilterNext != value) { _detailFilterNext = value; if (EA_FilterUpdated != null) { EA_FilterUpdated?.Invoke(); } } } } public SelectData DetailFilter { get => _detailFilter; set { if (_detailFilter != value) { _detailFilter = value; if (EA_FilterUpdated != null) { EA_FilterUpdated?.Invoke(); } } } } public SelectGLS DetailGLSFilter { get => _detailFilterGLS; set { if (_detailFilterGLS != value) { _detailFilterGLS = value; if (EA_FilterUpdated != null) { EA_FilterUpdated?.Invoke(); } } } } public string PageIcon { get => _pageIcon; set { if (_pageIcon != value) { _pageIcon = value; ReportPageUpd(); } } } public string PageName { get => _pageName; set { if (_pageName != value) { _pageName = value; //ReportPageUpd(); } } } public int PageNum { get; set; } = 1; public int PageSize { get; set; } = 10; public string SearchVal { get => _searchVal; set { if (_searchVal != value) { _searchVal = value; if (EA_SearchUpdated != null) { EA_SearchUpdated?.Invoke(); } } } } public bool ShowSearch { get => showSearch; set { if (showSearch != value) { showSearch = value; if (showSearch) { if (EA_ShowSearch != null) { EA_ShowSearch?.Invoke(); } } else { if (EA_HideSearch != null) { EA_HideSearch?.Invoke(); } } } } } public string UserName { get => _userName; set { if (_userName != value) { _userName = value; ReportUserUpd(); } } } /// /// Dizionario totale preferenze utente /// public Dictionary UsersPrefDict { get => redisHashDictGet((RedisKey)$"{redisUserKey}"); set => redisHashDictSet((RedisKey)$"{redisUserKey}", value); } #endregion Public Properties #region Public Methods /// /// Refresh globale cache redis /// /// public async Task FlushRedisCache() { Stopwatch sw = new Stopwatch(); sw.Start(); await Task.Delay(1); RedisValue pattern = new RedisValue($"{redisBaseKey}:*"); bool answ = await ExecFlushRedisPattern(pattern); sw.Stop(); Log.Debug($"FlushRedisCache in {sw.Elapsed.TotalMilliseconds} ms"); return answ; } /// /// Num record x la pagina corrente /// public async Task NumRowGridGet(string gridName) { var answ = await localStore.GetItemAsync($"{gridName}_nRow"); answ = answ > 0 ? answ : 10; return answ; } /// /// Imposta num record x la pagina corrente /// public async Task NumRowGridSet(string gridName, int newVal) { await localStore.SetItemAsync($"{gridName}_nRow", newVal); } public void ReportSelCodApp() { if (EA_SelCodApp != null) { EA_SelCodApp?.Invoke(); } } public void ReportSelCodImp() { if (EA_SelCodImp != null) { EA_SelCodImp?.Invoke(); } } public void ReportSelCodInst() { if (EA_SelCodInst != null) { EA_SelCodInst?.Invoke(); } } public void ReportTaskChange() { if (EA_TaskUpdate != null) { EA_TaskUpdate?.Invoke(); } } /// /// Preferenza utente salvata in local storage /// public async ValueTask UserPrefGet(string userPref) { var sData = await localStore.GetItemAsync(userPref); if (string.IsNullOrEmpty(sData)) { return default(T); } else { return (T)Convert.ChangeType(sData, typeof(T)); } } /// /// Imposta pref utente /// public async Task UserPrefSet(string userPref, string newVal) { await localStore.SetItemAsync(userPref, newVal); } /// /// Cerca un parametro utente salvato (se presente) e restituisce, se non trova restituisce "" /// /// /// public string UsrParamGet(string key) { string answ = ""; var currDict = UsersPrefDict; if (currDict.ContainsKey(key)) { answ = currDict[key]; } return answ; } /// /// Cerca un parametro utente salvato (se presente) e restituisce, se non trova restituisce 0 /// /// /// Valore da restituire se NON trovato /// public int UsrParamGetUInt(string key, int nullVal) { int answ = nullVal; var sVal = UsrParamGet(key); if (!string.IsNullOrEmpty(sVal)) { int.TryParse(sVal, out answ); } return answ; } /// /// Upsert di un parametro utente /// /// /// /// public string UsrParamSet(string key, string value) { string answ = ""; var currDict = UsersPrefDict; if (currDict.ContainsKey(key)) { currDict[key] = value; } else { currDict.Add(key, value); } UsersPrefDict = currDict; return answ; } #endregion Public Methods #region Protected Fields protected static JsonSerializerSettings? JSSettings; /// /// Oggetto per connessione a REDIS /// protected IConnectionMultiplexer redisConn = null!; /// /// Oggetto DB redis da impiegare x chiamate R/W /// protected IDatabase redisDb = null!; #endregion Protected Fields #region Protected Properties protected ILocalStorageService localStore { get; set; } = null!; #endregion Protected Properties #region Protected Methods /// /// Esegue flush memoria redis dato pat2Flush /// /// /// protected async Task ExecFlushRedisPattern(RedisValue pat2Flush) { bool answ = false; var masterEndpoint = redisConn.GetEndPoints() .Where(ep => redisConn.GetServer(ep).IsConnected && !redisConn.GetServer(ep).IsReplica) .FirstOrDefault(); // sepattern è "*" elimino intero DB... if (masterEndpoint != null && (pat2Flush.Equals(new RedisValue("*")) || pat2Flush == RedisValue.Null)) { redisConn.GetServer(masterEndpoint).FlushDatabase(database: redisDb.Database); } else { var server = redisConn.GetServer(masterEndpoint); var keys = server.Keys(database: redisDb.Database, pattern: pat2Flush, pageSize: 1000); var deleteTasks = new List(); foreach (var key in keys) { deleteTasks.Add(redisDb.KeyDeleteAsync(key)); if (deleteTasks.Count >= 1000) { await Task.WhenAll(deleteTasks); deleteTasks.Clear(); } } if (deleteTasks.Count > 0) { await Task.WhenAll(deleteTasks); } } answ = true; #if false var listEndpoints = redisConn.GetEndPoints(); foreach (var endPoint in listEndpoints) { //var server = redisConnAdmin.GetServer(listEndpoints[0]); var server = redisConn.GetServer(endPoint); if (server != null) { var keyList = server.Keys(redisDb.Database, pattern); foreach (var item in keyList) { await redisDb.KeyDeleteAsync(item); } answ = true; } } #endif return answ; } #endregion Protected Methods #region Private Fields private SelectData _detailFilter = SelectData.Init(5, 15); private SelectGLS _detailFilterGLS = SelectGLS.Init(60, -30); private SelectNext _detailFilterNext = SelectNext.Init(60, -30); private string _pageIcon; private string _pageName; private string _searchVal; private string _userName; private Logger Log = LogManager.GetCurrentClassLogger(); private string redisBaseKey = "LiMan:Ui:UserPref"; private bool showSearch; #endregion Private Fields #region Private Properties private string redisUserKey { get => $"{redisBaseKey}:{UserName.Replace("\\", ":")}"; } #endregion Private Properties #region Private Methods /// /// Recupero HashSet redis come Dictionary /// /// /// private Dictionary redisHashDictGet(RedisKey currKey) { Dictionary answ = new Dictionary(); try { answ = redisDb .HashGetAll(currKey) .ToDictionary(x => $"{x.Name}", x => $"{x.Value}"); } catch (Exception exc) { Log.Info($"Errore redisHashDictGet | currKey: {currKey}{Environment.NewLine}{exc}"); } return answ; } /// /// Salvataggio Dictionary come HashSet Redis /// /// /// private bool redisHashDictSet(RedisKey currKey, Dictionary dict) { bool fatto = false; try { HashEntry[] data2ins = new HashEntry[dict.Count]; int i = 0; foreach (KeyValuePair kvp in dict) { data2ins[i] = new HashEntry(kvp.Key, kvp.Value); i++; } // salvo! redisDb.HashSet(currKey, data2ins); redisDb.KeyExpire(currKey, DateTime.Now.AddMinutes(10)); fatto = true; } catch (Exception exc) { Log.Error($"Eccezione in redisHashDictSet | currKey: {currKey}{Environment.NewLine}{exc}"); } return fatto; } private void ReportPageUpd() { if (EA_PageUpdated != null) { EA_PageUpdated?.Invoke(); } } private void ReportSearch() { if (EA_SearchUpdated != null) { EA_SearchUpdated?.Invoke(); } } private void ReportUserUpd() { if (EA_UserNameUpd != null) { EA_UserNameUpd?.Invoke(); } } #endregion Private Methods } }