Files
lux/EgwCoreLib.Lux.Data/Services/DataLayerServices.cs
T
2025-09-18 12:52:40 +02:00

805 lines
30 KiB
C#

using EgwCoreLib.Lux.Core.RestPayload;
using EgwCoreLib.Lux.Data.Controllers;
using EgwCoreLib.Lux.Data.DbModel;
using EgwMultiEngineManager.Data;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using NLog;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static EgwCoreLib.Lux.Core.Enums;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace EgwCoreLib.Lux.Data.Services
{
public class DataLayerServices : BaseServ
{
#region Public Constructors
public DataLayerServices(IConfiguration configuration, IConnectionMultiplexer RedisConn) : base(configuration, RedisConn)
{
// conf DB
string connStr = BaseServ.configuration.GetConnectionString("Lux.All") ?? "";
if (string.IsNullOrEmpty(connStr))
{
Log.Error("ConnString empty!");
}
else
{
dbController = new LuxController();
//dbController = new Controllers.LuxController(configuration);
StringBuilder sb = new StringBuilder();
sb.AppendLine($"DataLayerServices | LuxController OK");
Log.Info(sb.ToString());
}
}
#endregion Public Constructors
#region Public Properties
public static LuxController dbController { get; set; } = null!;
#endregion Public Properties
#region Public Methods
/// <summary>
/// Elenco completo Customers
/// </summary>
/// <returns></returns>
public List<CustomerModel> CustomersGetAll()
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<CustomerModel>? result = new List<CustomerModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:Customers:ALL";
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<CustomerModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = dbController.CustomersGetAll();
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<CustomerModel>();
}
sw.Stop();
Log.Debug($"CustomersGetAll | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Elenco completo dealers
/// </summary>
/// <returns></returns>
public List<DealerModel> DealersGetAll()
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<DealerModel>? result = new List<DealerModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:Dealers:ALL";
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<DealerModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = dbController.DealersGetAll();
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<DealerModel>();
}
sw.Stop();
Log.Debug($"DealersGetAll | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Reset completo cache sistema
/// </summary>
public bool FlushCache()
{
bool answ = false;
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
RedisValue pattern = new RedisValue($"{redisBaseKey}:*");
answ = ExecFlushRedisPattern(pattern);
stopWatch.Stop();
Log.Debug($"FlushCache in {stopWatch.Elapsed.TotalMilliseconds} ms");
return answ;
}
/// <summary>
/// Reset completo cache sistema modalità async
/// </summary>
public async Task<bool> FlushCacheAsync()
{
bool answ = false;
Stopwatch sw = new Stopwatch();
sw.Start();
RedisValue pattern = new RedisValue($"{redisBaseKey}:*");
answ = await ExecFlushRedisPatternAsync(pattern);
sw.Stop();
Log.Debug($"FlushCacheAsync in {sw.Elapsed.TotalMilliseconds} ms");
return answ;
}
/// <summary>
/// Elenco completo GenClass
/// </summary>
/// <returns></returns>
public async Task<List<GenClassModel>> GenClassGetAllAsync()
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<GenClassModel>? result = new List<GenClassModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:GenClass";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<GenClassModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.GenClassGetAllAsync();
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<GenClassModel>();
}
sw.Stop();
Log.Debug($"GenClassGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Elenco valori x classe richiesta
/// </summary>
/// <param name="codClass"></param>
/// <returns></returns>
public async Task<List<GenValueModel>> GenValGetFiltAsync(string codClass)
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<GenValueModel>? result = new List<GenValueModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:GenVal:{codClass}";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<GenValueModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.GenValGetFiltAsync(codClass);
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<GenValueModel>();
}
sw.Stop();
Log.Debug($"GenValGetFiltAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Eliminazione record item
/// </summary>
/// <param name="rec2del"></param>
/// <returns></returns>
public async Task<bool> ItemDeleteAsync(ItemModel rec2del)
{
bool result = await dbController.ItemDeleteAsync(rec2del);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Item:*");
return result;
}
/// <summary>
/// Elenco item alternativi x sostituzione in preventivo, ovvero
/// - sono item "child" di un parent record
/// - condividono lo stesso parent record
/// </summary>
/// <param name="ItemId">ID ite corrente (valido quindi >0)</param>
/// <returns></returns>
public List<ItemModel> ItemGetAlt(int ItemId)
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<ItemModel>? result = new List<ItemModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:Item:ListAlt:{ItemId}";
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<ItemModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = dbController.ItemGetAlt(ItemId);
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
redisDb.StringSet(currKey, rawData, FastCache);
}
if (result == null)
{
result = new List<ItemModel>();
}
sw.Stop();
Log.Debug($"ItemGetAlt | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
#if false
/// <summary>
/// Elenco item Child da ParentId (per sostituzione) async
/// </summary>
/// <param name="ItemId">ID corrente di cui cercare parent + fratelli</param>
/// <returns></returns>
public List<ItemModel> ItemGetChild(int ItemId)
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<ItemModel>? result = new List<ItemModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:Item:ListChild:{ItemId}";
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<ItemModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = dbController.ItemGetChild(ItemId);
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
redisDb.StringSet(currKey, rawData, FastCache);
}
if (result == null)
{
result = new List<ItemModel>();
}
sw.Stop();
Log.Debug($"ItemGetChild | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
#endif
/// <summary>
/// Elenco item da ricerca completa Async
/// </summary>
/// <param name="SearchVal"></param>
/// <param name="CodGroup"></param>
/// <param name="ItemType"></param>
/// <returns></returns>
public async Task<List<ItemModel>> ItemGetFiltAsync(string CodGroup, ItemClassType ItemType)
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<ItemModel>? result = new List<ItemModel>();
// cerco in redis...
string groupTok = string.IsNullOrEmpty(CodGroup) ? "ALL" : CodGroup;
string currKey = $"{redisBaseKey}:Item:Filt:{groupTok}:{ItemType}";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<ItemModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.ItemGetFiltAsync(CodGroup, ItemType);
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<ItemModel>();
}
sw.Stop();
Log.Debug($"ItemGetFiltAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Elenco item da ricerca async
/// </summary>
/// <param name="term"></param>
/// <returns></returns>
public async Task<List<ItemModel>> ItemGetSearchAsync(string term)
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<ItemModel>? result = new List<ItemModel>();
// cerco in redis...
string token = string.IsNullOrEmpty(term) ? "ALL" : term;
string currKey = $"{redisBaseKey}:Item:Search:{token}";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<ItemModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.ItemGetSearchAsync(term);
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<ItemModel>();
}
sw.Stop();
Log.Debug($"ItemGetSearchAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Elenco completo ItemGroup gestiti
/// </summary>
/// <returns></returns>
public async Task<List<ItemGroupModel>> ItemGroupGetAllAsync()
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<ItemGroupModel>? result = new List<ItemGroupModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:ItemGroup:ALL";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<ItemGroupModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.ItemGroupGetAllAsync();
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<ItemGroupModel>();
}
sw.Stop();
Log.Debug($"ItemGroupGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Update / Insert record item
/// </summary>
/// <param name="currRec"></param>
/// <returns></returns>
public async Task<bool> ItemUpsertAsync(ItemModel currRec)
{
bool result = await dbController.ItemUpsertAsync(currRec);
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Item:*");
return result;
}
/// <summary>
/// Elenco completo offerte da DB
/// </summary>
/// <returns></returns>
public async Task<List<OfferModel>> OfferGetAll()
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<OfferModel>? result = new List<OfferModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:Offers:ALL";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
//if (!string.IsNullOrEmpty($"{rawData}"))
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<OfferModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.OfferGetAll();
// serializzo e salvo con config x evitare loop...
rawData = JsonConvert.SerializeObject(result, JSSettings);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<OfferModel>();
}
sw.Stop();
Log.Debug($"OfferGetAll | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Elenco righe offerta specificata
/// </summary>
/// <param name="OfferID"></param>
/// <returns></returns>
public async Task<List<OfferRowModel>> OfferRowGetByOffer(int OfferID)
{
string source = "DB";
Stopwatch sw = new Stopwatch();
sw.Start();
List<OfferRowModel>? result = new List<OfferRowModel>();
// cerco in redis...
string currKey = $"{redisBaseKey}:OfferRows:{OfferID}";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
//if (!string.IsNullOrEmpty($"{rawData}"))
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<OfferRowModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = dbController.OfferRowGetByOffer(OfferID);
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
await redisDb.StringSetAsync(currKey, rawData, LongCache);
}
if (result == null)
{
result = new List<OfferRowModel>();
}
sw.Stop();
Log.Debug($"OfferRowGetByOffer | {source} | {sw.Elapsed.TotalMilliseconds}ms");
return result;
}
/// <summary>
/// Converte il campo raw della BOM in lista oggetti da gestire
/// </summary>
/// <param name="currRec"></param>
/// <returns></returns>
public List<BomItemDTO> OffertGetBomList(OfferRowModel currRec)
{
List<BomItemDTO> answ = new List<BomItemDTO>();
var bomList = JsonConvert.DeserializeObject<List<BomItemDTO>>(currRec.ItemBOM);
if (bomList != null)
{
answ = bomList;
}
return answ;
}
/// <summary>
/// Effettua update dei costi di tutte le righe dell'offerta indicata
/// </summary>
/// <param name="OfferRowID">ID riga offerta da aggiornare</param>
/// <param name="newBomList">Bom aggiornata da salvare</param>
/// <returns></returns>
public async Task<bool> OffertRowUpdateBom(int OfferRowID, List<BomItemDTO> newBomList)
{
Stopwatch sw = new Stopwatch();
sw.Start();
// calcolo
bool fatto = await dbController.OffertRowUpdateBom(OfferRowID, newBomList);
// svuoto cache...
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
sw.Stop();
Log.Debug($"OffertRowUpdateBom in {sw.Elapsed.TotalMilliseconds} ms");
return fatto;
}
/// <summary>
/// Effettua update dei costi di tutte le righe dell'offerta indicata
/// </summary>
/// <param name="OfferID">Key</param>
/// <returns></returns>
public async Task<bool> OffertUpdateCost(int OfferID)
{
Stopwatch sw = new Stopwatch();
sw.Start();
// calcolo
bool fatto = await dbController.OffertUpdateCost(OfferID);
// svuoto cache...
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:Offers:*");
await ExecFlushRedisPatternAsync((RedisValue)$"{redisBaseKey}:OfferRows:*");
sw.Stop();
Log.Debug($"OffertUpdateCost in {sw.Elapsed.TotalMilliseconds} ms");
return fatto;
}
/// <summary>
/// Esegue salvataggio BOM sul DB
/// </summary>
/// <param name="uID">UID dell'item offerta di cui si è ricevuto la BOM</param>
/// <param name="execEnvironment">Environment dell'item</param>
/// <param name="bomContent">BOM serializzata</param>
/// <returns></returns>
public async Task SaveBomAsync(string uID, Constants.EXECENVIRONMENTS execEnvironment, string bomContent)
{
// salvo sul DB il risultato della BOM
if (!string.IsNullOrEmpty(bomContent))
{
try
{
// deserializzo la Bom...
var bomList = JsonConvert.DeserializeObject<List<BomItemDTO>>(bomContent);
if (bomList != null)
{
// verifico 1:1 gli item ricevuti dalla BOM sul DB con eventuale insert in anagrafica
dbController.ItemUpsertFromBom(bomList);
// salvo la BOM nel record del DB relativo all'oggetto richiesto
dbController.OfferUpsertFromBom(uID, bomList);
}
}
catch { }
}
await Task.Delay(1);
}
/// <summary>
/// Esegue salvataggio HardwareModelList sul DB
/// </summary>
/// <param name="uID">UID dell'item offerta di cui si è ricevuto la BOM</param>
/// <param name="execEnvironment">Environment dell'item</param>
/// <param name="rawContent">HardwareModelList serializzata</param>
/// <returns></returns>
public async Task SaveHmlAsync(string uID, Constants.EXECENVIRONMENTS execEnvironment, string rawContent)
{
// salvo sul DB il risultato della BOM
if (!string.IsNullOrEmpty(rawContent))
{
try
{
// salvo il set come oggetti deserializzati/testati?
#if false
// deserializzo la Bom...
var bomList = JsonConvert.DeserializeObject<List<BomItemDTO>>(rawContent);
if (bomList != null)
{
// verifico 1:1 gli item ricevuti dalla BOM sul DB con eventuale insert
dbController.ItemUpsertFromBom(bomList);
// salvo la BOM nel record del DB relativo all'oggetto richiesto
dbController.OfferUpsertFromBom(uID, bomList);
}
#endif
}
catch { }
}
await Task.Delay(1);
}
#endregion Public Methods
#region Protected Fields
/// <summary>
/// Numero di operazioni parallele che si possono svolgere... (se 0 NON usa cicli paralleli)
/// </summary>
protected int numPar = 0;
#endregion Protected Fields
#region Protected Methods
/// <summary>
/// Esegue flush memoria redis dato pat2Flush
/// </summary>
/// <param name="pat2Flush"></param>
/// <returns></returns>
protected bool ExecFlushRedisPattern(RedisValue pat2Flush)
{
bool answ = false;
/*******************************
* Recupero elenco dei server da cui cancellare le chiavi:
* - prendo solo i server connessi
* - prendo solo NON repliche (= master)
* - me ne aspetto 1 in uscita cmq
*******************************/
var connServ = redisConn.GetEndPoints()
.Select(endpoint =>
{
var server = redisConn.GetServer(endpoint);
return server;
})
.Where(x => x.IsConnected && !x.IsReplica)
.FirstOrDefault();
if (connServ != null)
{
try
{
// sepattern è "*" elimino intero DB...
if ((pat2Flush.Equals(new RedisValue("*")) || pat2Flush == RedisValue.Null))
{
connServ.FlushDatabase(database: redisDb.Database);
}
else
{
var keys = connServ.Keys(database: redisDb.Database, pattern: pat2Flush, pageSize: 1000);
var batch = new List<RedisKey>();
foreach (var key in keys)
{
batch.Add(key);
// Flush in batches of 1000
if (batch.Count >= 1000)
{
foreach (var item in batch)
redisDb.KeyDelete(item);
batch.Clear();
}
}
// Flush remaining keys
foreach (var item in batch)
redisDb.KeyDelete(item);
}
#if false
var keyList = connServ.Keys(redisDb.Database, pattern);
if (numPar > 0)
{
var options = new ParallelOptions { MaxDegreeOfParallelism = numPar };
Parallel.ForEach(keyList, (item, token) =>
{
// cancello
redisDb.KeyDelete(item);
});
}
else
{
foreach (var item in keyList)
{
redisDb.KeyDelete(item);
}
}
#endif
answ = true;
}
catch (Exception exc)
{
Log.Error($"Eccezione durante ExecFlushRedisPattern | pat2Flush: {pat2Flush}{Environment.NewLine}{exc}");
}
}
else
{
Log.Error($"Server REDIS master non trovato");
}
return answ;
}
/// <summary>
/// Esegue flush memoria redis dato pat2Flush
/// </summary>
/// <param name="pat2Flush"></param>
/// <returns></returns>
protected async Task<bool> ExecFlushRedisPatternAsync(RedisValue pat2Flush)
{
bool answ = false;
/*******************************
* Recupero elenco dei server da cui cancellare le chaivi:
* - prendo solo i server connessi
* - prendo solo NON repliche (= master)
* - me ne aspetto 1 in uscita / prendo primo o default
*******************************/
var connServ = redisConn.GetEndPoints()
.Select(endpoint =>
{
var server = redisConn.GetServer(endpoint);
return server;
})
.Where(x => x.IsConnected && !x.IsReplica)
.FirstOrDefault();
if (connServ != null)
{
// ciclo (anche se me ne aspetto 1 solo)
// se pat2Flush è "*" elimino intero DB...
if ((pat2Flush.Equals(new RedisValue("*")) || pat2Flush == RedisValue.Null))
{
connServ.FlushDatabase(database: redisDb.Database);
}
else
{
try
{
var keys = connServ.Keys(database: redisDb.Database, pattern: pat2Flush, pageSize: 1000);
var deleteTasks = new List<Task>();
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);
}
#if false
if (numPar > 0)
{
var options = new ParallelOptions { MaxDegreeOfParallelism = numPar };
await Parallel.ForEachAsync(keys, async (item, token) =>
{
// cancello
await redisDb.KeyDeleteAsync(item);
});
}
else
{
foreach (var item in keys)
{
await redisDb.KeyDeleteAsync(item);
}
}
#endif
answ = true;
}
catch (Exception exc)
{
Log.Error($"Eccezione durante ExecFlushRedisPatternAsync | pat2Flush: {pat2Flush}{Environment.NewLine}{exc}");
}
}
}
else
{
Log.Error($"Server REDIS master non trovato");
}
return answ;
}
#endregion Protected Methods
#region Private Fields
private static Logger Log = LogManager.GetCurrentClassLogger();
private string redisBaseKey = "Lux:Cache";
#endregion Private Fields
}
}