using EgwCoreLib.Lux.Core.Generic; using EgwCoreLib.Lux.Core.RestPayload; using EgwCoreLib.Lux.Data.DbModel.Config; using EgwCoreLib.Lux.Data.DbModel.Cost; using EgwCoreLib.Lux.Data.DbModel.Items; using EgwCoreLib.Lux.Data.DbModel.Job; using EgwCoreLib.Lux.Data.DbModel.Production; using EgwCoreLib.Lux.Data.DbModel.Sales; using EgwCoreLib.Lux.Data.DbModel.Stats; using EgwCoreLib.Lux.Data.DbModel.Utils; using EgwCoreLib.Lux.Data.Domains; using EgwMultiEngineManager.Data; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using MySqlConnector; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System.Data; using static EgwCoreLib.Lux.Core.Enums; namespace EgwCoreLib.Lux.Data.Controllers { internal class LuxController { // manca costruttore parametrico contoller... #region Internal Methods /// /// Elenco CostDrivers /// /// internal async Task> CostDriverGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetCostDriver .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante CostDriverGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco contatori /// /// Anno riferimento, se null da tutti /// internal async Task> CountersGetAllAsync(int? yearRef = null) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetCounters .Where(x => yearRef == null || x.RefYear == yearRef) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante CountersGetAllAsync | yearRef: {yearRef}{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Recupera un nuovo valore del contatore per tipo ed anno richiesto, se mancasse crea /// /// Anno riferimento /// Counter richiesto /// internal async Task CountersGetNext(int yearRef, string countName) { int newCount = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { var outParam = new MySqlParameter("@pValue", MySqlDbType.Int32) { Direction = ParameterDirection.Output }; await dbCtx.Database.ExecuteSqlRawAsync( "CALL GetNextCounter(@pYear, @pName, @pValue);", new MySqlParameter("@pYear", yearRef), new MySqlParameter("@pName", countName), outParam ); if (outParam != null) { int.TryParse($"{outParam.Value}", out newCount); } } return newCount; } /// /// Elenco completo Customers da DB /// /// internal List CustomersGetAll() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetCustomer .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante CustomersGetAll{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco completo Dealers da DB /// /// internal List DealersGetAll() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetDealer .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante DealersGetAll{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Add item ricevuti da BOM calcolata /// /// /// internal bool ItemUpsertFromBom(List bomList) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // Controllo ed inserisco eventuali gruppi mancanti UpdateCodGroup(bomList); // prendo solo elementi a prezzo 0 da salvare sul DB var item2save = bomList .Where(x => x.Price == 0) .ToList(); List listInserted = new List(); // ciclo x ogni elemento della BOM, cercando x gruppo e ExtItemCode foreach (var item in item2save) { var currRec = dbCtx .DbSetItem .Where(x => x.CodGroup == item.ClassCode && x.ExtItemCode == item.ItemCode) .FirstOrDefault(); // se nullo --> verifico x inserire!!! if (currRec == null) { // verifico NON sia tra gli list2upd già in fase di inserimento if (!listInserted.Any(x => x.CodGroup == item.ClassCode && x.ExtItemCode == item.ItemCode)) { ItemModel newRec = new ItemModel() { CodGroup = item.ClassCode, ItemType = Core.Enums.ItemClassType.Bom, IsService = false, // da calcolare meglio x gruppo ItemCode = 0, ExtItemCode = item.ItemCode, SupplCode = "BOM ITEM", Description = $"BOM | {item.ClassCode} | {item.ItemCode}", Cost = 0, Margin = 0, QtyMin = 0, QtyMax = 0, UM = "#" }; dbCtx.DbSetItem.Add(newRec); listInserted.Add(newRec); } } } // salvo... dbCtx.SaveChanges(); } return answ; } /// /// Elenco item da ricerca filtro x gruppo/tipo /// /// /// /// internal List ItemGetFilt(string CodGroup, ItemClassType ItemType) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetItem .Where(x => (string.IsNullOrEmpty(CodGroup) || x.CodGroup == CodGroup) && (ItemType == ItemClassType.ND || x.ItemType == ItemType)) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGetFilt{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco completo ItemGroup gestiti /// /// internal List ItemGroupGetAll() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetItemGroup .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGroupGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco completo ItemGroup gestiti async /// /// internal async Task> ItemGroupGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetItemGroup .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ItemGroupGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } internal async Task OffersCheckExpired() { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { DateTime adesso = DateTime.Now; // recupero offerta... var listExpired = dbCtx .DbSetOffer .Where(x => x.ValidUntil < adesso && x.OffertState == OfferStates.Open) .ToList(); // se trovo le aggiorno come stato if (listExpired != null) { foreach (var item in listExpired) { item.OffertState = OfferStates.Expired; dbCtx.Entry(item).State = EntityState.Modified; } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } } catch (Exception exc) { Log.Error($"Eccezione durante OffersCheckExpired{Environment.NewLine}{exc}"); } } return answ; } /// /// Esegue upsert del record offerta data la BOM ricevuta /// /// /// internal bool OfferUpsertFromBom(string uID, List bomList) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetOfferRow .Where(x => x.OfferRowUID == uID) .FirstOrDefault(); // se trovato --> salvo BOM e calcolo costi if (currRec != null) { // recupero l'elenco degli itemGroup gestiti var itemGroupList = dbCtx .DbSetItemGroup .ToList(); // recupero il subset item da BOM... var bomGenList = dbCtx .DbSetItem //.Where(x => x.sourceType == Core.Enums.ItemClassType.Bom) .Where(x => (x.ItemType == Core.Enums.ItemClassType.Bom || x.ItemType == Core.Enums.ItemClassType.BomAlt)) .ToList(); // recupero la BOM list precedente var bomListPrev = JsonConvert.DeserializeObject>(currRec.ItemBOM); // calcolo il NUOVO costo e lo aggiorno... double totCost = 0; double totPrice = 0; int totItemQty = 0; int numGroupOk = 0; int numItemOk = 0; int numElems = bomList.Count; // validazione e completamento BOM BomCalculator.Validate(itemGroupList, bomGenList, ref bomList, bomListPrev, ref totCost, ref totPrice, ref totItemQty, ref numGroupOk, ref numItemOk); // salvo BOM... string itemBom = JsonConvert.SerializeObject(bomList); currRec.ItemBOM = itemBom; // salvo arrotondato alla 3° decimale currRec.BomCost = Math.Round(totCost, 3); currRec.BomPrice = Math.Round(totPrice, 3); currRec.BomOk = numElems == numGroupOk; currRec.ItemOk = numElems == numItemOk; // setto ok await di BOM e Price currRec.AwaitBom = false; currRec.AwaitPrice = false; currRec.ProdItemQty = totItemQty; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo... var result = dbCtx.SaveChanges(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OfferUpsertFromBom{Environment.NewLine}{exc}"); } } return answ; } #if true /// /// Elimina riga e sposta eventuali righe successive... /// /// /// internal async Task OrderRowDelete(OrderRowModel rec2Del) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == rec2Del.OrderRowID) .FirstOrDefault(); // se trovo procedo if (currRec != null) { // recupero indice attuale... int currRowNum = rec2Del.RowNum; // cerco righe successive var list2move = dbCtx .DbSetOrderRow .Where(x => x.OrderID == rec2Del.OrderID && x.RowNum > currRowNum) .ToList(); // se ci sono aggiorno! if (list2move != null && list2move.Count > 0) { foreach (var item in list2move) { item.RowNum--; dbCtx.Entry(item).State = EntityState.Modified; } } // infine rimuovo riga dbCtx.DbSetOrderRow.Remove(currRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowDelete{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiornamento valore UID non calcolato + ritorno elenco UID da aggiornare /// /// /// internal List OrderRowFixUid(int orderID) { List answ = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currList = dbCtx .DbSetOrderRow .Where(x => x.OrderID == orderID) .ToList(); // se trovato --> verifico valori differenti, aggiorno e restituisco da calcolare if (currList != null) { var list2fix = currList.Where(x => string.IsNullOrEmpty(x.OrderRowUID) || x.OrderRowUID != x.OrderRowCode).ToList(); if (list2fix != null && list2fix.Count > 0) { // salvo elenco answ = list2fix.Select(x => x.OrderRowCode).ToList(); // sistemo UID foreach (var item in list2fix) { item.OrderRowUID = item.OrderRowCode; dbCtx.Entry(item).State = EntityState.Modified; } // salvo... var result = dbCtx.SaveChanges(); } } } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowFixUid{Environment.NewLine}{exc}"); } } return answ; } /// /// Elenco righe Ordine specificato /// /// /// internal List OrderRowGetByOffer(int OrderID) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOrderRow .Where(x => x.OrderID == OrderID) .Include(s => s.SellingItemNav) //.Include(d => d.DealerNav) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowGetByOffer{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco righe ordine dato stato richiesto /// /// /// internal List OrderRowGetByState(OrderStates reqState, DateTime dtStart, DateTime dtEnd) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOrderRow .Where(x => x.OrderRowState == reqState && x.Inserted >= dtStart && x.Inserted <= dtEnd) .Include(s => s.SellingItemNav) //.Include(d => d.DealerNav) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowGetByState{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco righe ordine dato periodo + stato richiesto MINIMO /// /// /// internal List OrderRowGetByStateMin(OrderStates reqState, DateTime dtStart, DateTime dtEnd) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOrderRow .Where(x => x.OrderRowState >= reqState && x.Inserted >= dtStart && x.Inserted <= dtEnd) .Include(s => s.SellingItemNav) //.Include(d => d.DealerNav) .ToList(); } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowGetByStateMin{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Riga Ordine dato suo UID /// /// /// internal OrderRowModel OrderRowGetByUID(string OrderRowUID) { OrderRowModel? dbResult = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = dbCtx .DbSetOrderRow .Include(s => s.SellingItemNav) .FirstOrDefault(x => x.OrderRowUID == OrderRowUID); } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowGetByUID{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Effettua update stato await BOM/PRICE per l'Ordine indicato /// /// ID singola riga ordine /// Se non nullo è il nuovo stato await BOM /// Se non nullo è stato await Price /// internal async Task OrderRowUpdateAwaitState(int orderRowID, bool? awaitBom, bool? awaitPrice) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe ordine... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == orderRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.AwaitBom = awaitBom ?? currRec.AwaitBom; currRec.AwaitPrice = awaitPrice ?? currRec.AwaitPrice; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpdateAwaitState{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiorno sul DB i dati del file associato /// /// /// internal async Task OrderRowUpdateFileData(OrderRowModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == updRec.OrderRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.FileName = updRec.FileName; currRec.FileResource = updRec.FileResource; currRec.FileSize = updRec.FileSize; currRec.SerStruct = updRec.SerStruct; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpdateFileData{Environment.NewLine}{exc}"); } } return answ; } /// /// Effettua update serStruct per l'Ordine indicato /// /// ID singola riga Ordine /// Valore serializzatoe /// internal async Task OrderRowUpdateSerStruct(int orderRowID, string serStruct) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == orderRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.SerStruct = serStruct; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpdateSerStruct{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiorna stato riga ordine /// /// /// /// internal async Task OrderRowUpdateState(int orderRowID, OrderStates reqState) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero righe offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == orderRowID) .FirstOrDefault(); // aggiorno parametri (se inviati) if (currRec != null) { currRec.OrderRowState = reqState; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpdateState{Environment.NewLine}{exc}"); } } return answ; } /// /// Add riga ordine /// /// /// internal async Task OrderRowUpsert(OrderRowModel updRec) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowID == updRec.OrderRowID) .FirstOrDefault(); // se non trovo aggiungo if (currRec == null) { dbCtx.DbSetOrderRow.Add(updRec); // se ci sono record successivi li devo spostare... var list2move = dbCtx .DbSetOrderRow .Where(x => x.OrderID == updRec.OrderID && x.RowNum >= updRec.RowNum) .ToList(); if (list2move != null && list2move.Count > 0) { foreach (var item2move in list2move) { item2move.RowNum++; dbCtx.Entry(item2move).State = EntityState.Modified; } } } // altrimenti aggiorno else { dbCtx.Entry(currRec).CurrentValues.SetValues(updRec); } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpsert{Environment.NewLine}{exc}"); } } return answ; } #endif /// /// Add record del solo ProdEstimate /// /// /// internal async Task OrderRowUpsertProdEst(string uID, string prodEstim) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // recupero offerta... var currRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowUID == uID) .FirstOrDefault(); // se trovo aggiorno if (currRec != null) { // genero WLD x controllo var currWLD = new WorkLoadDetailDTO(currRec.OrderRowUID, currRec.ProdEstimate); // faccio update in cascata dei record collegati x macchine e items var listMachDb = dbCtx .DbSetProdPlant .Select(x => x.ProdPlantCod) .ToList(); List listMaccCurr = currWLD.MachineCalcResults.Select(x => x.Name).OrderBy(x => x).ToList() ?? new List(); if (listMaccCurr != null && listMaccCurr.Any()) { var listDiff = listMaccCurr.Except(listMachDb).ToList(); if (listDiff.Any()) { foreach (var macch in listDiff) { dbCtx .DbSetProdPlant .Add(new ProductionPlantModel() { ProdPlantCod = macch, ProdPlantDescript = macch }); } } } // resetta assegnazioni prodgroup agli items... List listItem2upd = await dbCtx .DbSetProdItem .Where(x => x.OrderRowID == currRec.OrderRowID && x.ProdGroupID != null) .ToListAsync(); if (listItem2upd != null && listItem2upd.Count > 0) { // li aggiorna tutti resettando ProdGroupID listItem2upd.ForEach(x => x.ProdGroupID = null); } // elimina eventuali oggetti ProductionGroup precedenti List listProdGroup2Rem = await dbCtx .DbSetProdGroup .Where(x => x.OrderRowID == currRec.OrderRowID) .ToListAsync(); // rimuovo... if (listProdGroup2Rem != null && listProdGroup2Rem.Count > 0) { dbCtx.DbSetProdGroup.RemoveRange(listProdGroup2Rem); } /*---------------------------------- * Generazione ProdGroup * FixMe ToDo !!! * * rifare onsiderando le REALI combinazioni scaturite x questo specifico caso e * - ENUMERARE le combinazioni * - ogni combinazione sarà un caso specifico tra 0...N dove N è il totale delle macchine gestite * - i successivi calcoli di balance/stima saranno fatti x questo SPECIFICO ID GROUP così da fare prima... a sto punto GroupIP potrebbe essere un int 0...n oppure l'id del record... forse meglio il counter 0..n * * */ int grpIdx = 1; // preparo x add nuovi ProductionGroup da analisi ritorno stime List listProdGroup2Add = new List(); // 1: non lavorabili... if (currWLD.ListUnWorkable.Count > 0) { // calcolo il dizionario degli elementi... Dictionary newWorkGroupList = new(); ProdMachineDetailDto detProd = new ProdMachineDetailDto() { TagList = currWLD.ListUnWorkable }; newWorkGroupList.Add("", detProd); string rawWGL = JsonConvert.SerializeObject(newWorkGroupList); ProductionGroupModel newRec = new ProductionGroupModel() { OrderRowID = currRec.OrderRowID, GrpIdx = grpIdx++, WorkGroupListRaw = rawWGL }; listProdGroup2Add.Add(newRec); } // dizionario x macchina delle parts LAVORABILI su impianto.. var machineTags = currWLD.MachineCalcResults .ToDictionary( m => m.Name, m => m.PartList .Where(p => p.CalcResult == EgwCoreLib.Lux.Core.Enums.PartVerificationResult.MACHINABLE) .ToList() ); // ciclo x tutte le combinazioni di gruppi lavorabilità... foreach (var item in currWLD.LoadDetail) { // calcolo il dizionario degli elementi... Dictionary newWorkGroupList = new(); foreach (var machineName in item.Machines) { decimal effectiveTime = 0; // Recuperiamo i dati della macchina dal dizionario if (machineTags.TryGetValue(machineName, out var machineParts)) { // Creiamo un set dei tag del gruppo per una ricerca veloce O(1) var groupTagsSet = item.Tags.ToHashSet(); // Sommiamo il tempo solo per i pezzi che appartengono a questo gruppo effectiveTime = machineParts .Where(p => groupTagsSet.Contains(p.Tag)) .Sum(p => p.Time); } ProdMachineDetailDto detProd = new ProdMachineDetailDto() { TagList = item.Tags, Time = effectiveTime // Tempo reale specifico per questa macchina/gruppo }; newWorkGroupList.Add(machineName, detProd); } string rawWGL = JsonConvert.SerializeObject(newWorkGroupList); ProductionGroupModel newRec = new ProductionGroupModel() { OrderRowID = currRec.OrderRowID, GrpIdx = grpIdx++, WorkGroupListRaw = rawWGL }; listProdGroup2Add.Add(newRec); } // aggiungo i record... dbCtx.DbSetProdGroup.AddRange(listProdGroup2Add); // aggiorno info Estimation, tempi e stato currRec.ProdEstimate = prodEstim; if (!string.IsNullOrEmpty(prodEstim)) { currRec.OrderRowState = OrderStates.Estimated; } var totEstim = listProdGroup2Add.Sum(x => x.TotalEstimTime); currRec.ProdEstimTime = totEstim; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante OrderRowUpsertProdEst{Environment.NewLine}{exc}"); } } return answ; } /// /// Validazione record: /// - controllo state vs estimate /// - segnala il numero dei record aggiornati /// /// Elenco record da verificare internal async Task OrderRowListValidate(List list2chk) { int numDone = 0; // verifica preliminare: serve SSE stato e estimate non corrispondono... var list2fix = list2chk .Where(x => x.OrderRowState == OrderStates.Created && !string.IsNullOrEmpty(x.ProdEstimate)) .ToList(); if (list2fix.Any()) { // per ogni record processo intera validazione foreach (var item in list2fix) { bool fatto = await OrderRowUpsertProdEst(item.OrderRowUID, item.ProdEstimate); numDone += fatto ? 1 : 0; } } return numDone; } /// /// Elenco record Fasi da DB /// /// internal async Task> PhasesGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetPhase .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante PhasesGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco record ProductionGroup dato OrderRow /// /// internal async Task> ProdGroupByOrderRow(int OrderRowID) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetProdGroup .Where(x => x.OrderRowID == OrderRowID) .Include(i => i.ItemsNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProdGroupByOrderRow{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Elenco record ProductionGroup dato Stato dell'OrderRow /// /// internal async Task> ProdGroupByOrderState(OrderStates reqState) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetProdGroup .Include(o => o.OrderRowNav) .Where(x => x.OrderRowNav.OrderRowState == reqState) .Include(i => i.ItemsNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProdGroupByOrderState{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Add record di un singolo ProdGroup da fase Balance /// /// UID dell'item offerta di cui si è ricevuto l'oggetto Balance' /// Prod Group di riferimento /// internal async Task ProdGroupUpsertBalance(string uID, string rGroup, string rawBalance) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // Tentativo di deserializzazione var data = JsonConvert.DeserializeObject>(rawBalance); // proseguo solo se è valida la deserializzazione... if (data != null) { // Togliamo la 'G' e convertiamo in int (gestisce automaticamente "01" -> 1) int grpIdx = int.Parse(rGroup.TrimStart('G')); // recupero ord row (parent)... var ordRowRec = dbCtx .DbSetOrderRow .Where(x => x.OrderRowUID == uID) .FirstOrDefault(); if (ordRowRec != null) { // recupero record specifico var currRec = dbCtx .DbSetProdGroup .Where(x => x.OrderRowID == ordRowRec.OrderRowID && x.GrpIdx == grpIdx) .FirstOrDefault(); // se trovato aggiorno if (currRec != null) { currRec.WorkGroupListRaw = rawBalance; dbCtx.Entry(currRec).State = EntityState.Modified; } // altrimenti aggiungo else { ProductionGroupModel newRec = new ProductionGroupModel() { OrderRowID = ordRowRec.OrderRowID, GrpIdx = grpIdx, WorkGroupListRaw = rawBalance }; dbCtx .DbSetProdGroup .Add(newRec); } // segno ordine come Assigned se non lo fosse... if (ordRowRec.OrderRowState != OrderStates.Assigned) { ordRowRec.OrderRowState = OrderStates.Assigned; dbCtx.Entry(ordRowRec).State = EntityState.Modified; } // salvo TUTTI i cambiamenti... var result = await dbCtx.SaveChangesAsync(); answ = result > 0; } } } catch (Exception exc) { Log.Error($"Eccezione durante ProdGroupUpsertBalance{Environment.NewLine}{exc}"); } } return answ; } /// /// Assegnazione in blocco degli item agli ODL corrispondenti /// /// /// /// internal async Task ProdItem2ODL_AssignAsync(List dbList, Dictionary<(int phaseId, int resId, string machine, int index), List> dictParts) { int totalCreated = 0; using (DataLayerContext dbCtx = new DataLayerContext()) { // 1. Recuperiamo tutti i ProdBatchID coinvolti per fare una sola query List batchIds = dbList.Select(o => o.ProdBatchID).Distinct().ToList(); if (batchIds != null && batchIds.Count > 0) { // 2. Carichiamo in memoria i ProdItem necessari (solo ID e Tag per risparmiare RAM) var itemsList = await dbCtx.DbSetProdItem .Where(x => batchIds.Contains(x.ProdBatchID ?? 0) && x.ProdItemTag != null && x.ProdItemTag != "") .Select(x => new { x.ProdItemID, x.ProdItemTag }) .ToListAsync(); // 1. Usiamo il "!" (null-forgiving operator) dopo x.ProdItemTag // perché il filtro .Where sopra garantisce che non sia null. var itemLookup = itemsList .GroupBy(x => x.ProdItemTag!) .ToDictionary( g => g.Key, g => g.First().ProdItemID, StringComparer.OrdinalIgnoreCase ); using var transaction = await dbCtx.Database.BeginTransactionAsync(); try { var relationsToInsert = new List(); foreach (var odl in dbList) { var key = (odl.PhaseID ?? 0, odl.ResourceID ?? 0, odl.ProdPlantCod, odl.Index); if (dictParts.TryGetValue(key, out List tagList)) { foreach (var tag in tagList) { // 3. Cerchiamo l'ID corrispondente al tag nel nostro lookup locale if (itemLookup.TryGetValue(tag, out int realItemId)) { relationsToInsert.Add(new ProductionItem2ODLModel { ProdODLID = odl.ProdODLID, ProdItemID = realItemId, DtAssign = DateTime.Now }); } else { //Log.Warning($"Tag {tag} non trovato nel database per i batch selezionati."); } } } } if (relationsToInsert.Any()) { await dbCtx.DbSetProdItem2ODL.AddRangeAsync(relationsToInsert); totalCreated = relationsToInsert.Count; await dbCtx.SaveChangesAsync(); } await transaction.CommitAsync(); } catch (Exception exc) { await transaction.RollbackAsync(); Log.Error($"Errore nel salvataggio relazioni ODL-Parts: {exc.Message}"); throw; } } } return totalCreated; } /// /// Esegue assegnazione bulk dei ProdItem ad un unico ProdBatch parent (per ora totale x RigaOrd) /// /// /// /// internal async Task ProdItemBulkAssignProdBatch(int OrderRowId, int ProdBatchId) { int totalUpdated = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { using var transaction = await dbCtx.Database.BeginTransactionAsync(); try { int rowsAffected = await dbCtx.DbSetProdItem .Where(p => p.OrderRowID == OrderRowId) .ExecuteUpdateAsync(setters => setters .SetProperty(p => p.ProdBatchID, ProdBatchId) ); totalUpdated += rowsAffected; await transaction.CommitAsync(); } catch (Exception exc) { await transaction.RollbackAsync(); Log.Error($"Eccezione durante ProdItemBulkAssignProdBatch{Environment.NewLine}{exc}"); //throw; } } return totalUpdated; } /// /// Esegue assegnazione bulk dei ProdItem ad un unico ProdGroup parent /// /// /// /// /// internal async Task ProdItemBulkAssignProdGroup(int OrderRowId, int ProdGroupId, Dictionary itemsToAssign) { int totalUpdated = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { using var transaction = await dbCtx.Database.BeginTransactionAsync(); try { foreach (var entry in itemsToAssign) { // Esegue un UPDATE diretto: UPDATE production_item SET ProdAssignID = x, EstimTime = y WHERE ProdItemTag = z int rowsAffected = await dbCtx.DbSetProdItem .Where(p => p.OrderRowID == OrderRowId && p.ProdItemTag == entry.Key) .ExecuteUpdateAsync(setters => setters .SetProperty(p => p.ProdGroupID, ProdGroupId) .SetProperty(p => p.EstimTime, entry.Value) ); totalUpdated += rowsAffected; } await transaction.CommitAsync(); } catch (Exception exc) { await transaction.RollbackAsync(); Log.Error($"Eccezione durante ProdItemBulkAssignProdGroup{Environment.NewLine}{exc}"); //throw; } } return totalUpdated; } /// /// Elenco ProdItem dato OrderRow /// /// /// internal async Task> ProdItemByOrderRow(int orderRowId) { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetProdItem .Where(x => x.OrderRowID == orderRowId) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProdItemByOrderRow{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Reset impostazione ProdItem x ripartire senza setting ProdGroup /// /// /// internal async Task ProdItemResetProdGroup(int orderRowID) { int numItem = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { numItem = await dbCtx.DbSetProdItem .Where(p => p.OrderRowID == orderRowID) .ExecuteUpdateAsync(setters => setters .SetProperty(p => p.ProdGroupID, (int?)null) .SetProperty(p => p.EstimTime, 0) ); } catch (Exception exc) { Log.Error($"Eccezione durante ProdItemResetProdGroup{Environment.NewLine}{exc}"); } } return numItem; } /// /// Elenco PODL non assegnati con struttura DTO appiattita /// /// internal async Task?> ProdOdlAssignGetAsync() { List? dbResults = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResults = await dbCtx.DbSetProdODL .Where(x => !x.DateAssign.HasValue) .Select(odl => new OdlAssignDto { Envir = odl.ProdBatchNav.Envir, ProdODLID = odl.ProdODLID, Description = odl.Description, EstimTime = odl.EstimTime, Index = odl.Index, OdlTag = odl.OdlTag, PhaseID = odl.PhaseID, ProdBatchID = odl.ProdBatchID, ProdPlantCod = odl.ProdPlantCod, Qty = odl.Qty, ResourceID = odl.ResourceID, ItemList = odl.Item2OdlNav.Select(i => new ItemAssignDto { ProdItemID = i.ProdItemID, OrderID = i.ProductionItemNav.OrderRowNav.OrderNav.OrderID, OrderRowID = i.ProductionItemNav.OrderRowNav.OrderRowID, OrderTag = i.ProductionItemNav.OrderRowNav.OrderNav.OrderCode, OrderRowTag = i.ProductionItemNav.OrderRowNav.OrderRowCode, ProdItemTag = i.ProductionItemNav.ProdItemTag ?? "***" }).ToList() }) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProductionOdlUnassignAsync{Environment.NewLine}{exc}"); } } return dbResults; } /// /// Recupero record ProdOdl dato Tag/uID /// /// /// internal async Task ProdOdlGetByUidAsync(string uID) { ProductionODLModel? answ = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { answ = await dbCtx .DbSetProdODL .Where(x => x.OdlTag == uID) .FirstOrDefaultAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProdOdlGetByUidAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiorna record ProdOdl (se trovato) con BOM (raw) ricevuta /// /// /// /// internal async Task ProdOdlUpdateBomAsync(string uID, string bomRaw) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetProdODL .Where(x => x.OdlTag == uID) .FirstOrDefault(); // se trovato --> salvo BOM e calcolo costi if (currRec != null) { currRec.RawBoM = bomRaw; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo... var result = dbCtx.SaveChanges(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante ProdOdlUpdateBomAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiorna record ProdOdl (se trovato) con ItemListRaw (raw) inviata x calcolo PROD /// /// /// /// internal async Task ProdOdlUpdateItemRawAsync(string uID, string itemListRaw) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetProdODL .Where(x => x.OdlTag == uID) .FirstOrDefault(); // se trovato --> salvo BOM e calcolo costi if (currRec != null) { currRec.RawItemRawList = itemListRaw; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo... var result = dbCtx.SaveChanges(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante ProdOdlUpdateItemRawAsync{Environment.NewLine}{exc}"); } } return answ; } /// /// Aggiorna record ProdOdl (se trovato) con RawMaterialList (raw) ricevuta da calcolo PROD /// /// /// /// internal async Task ProdOdlUpdateRawMaterialAsync(string uID, string materialListRaw) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { var currRec = dbCtx .DbSetProdODL .Where(x => x.OdlTag == uID) .FirstOrDefault(); // se trovato --> salvo BOM e calcolo costi if (currRec != null) { currRec.RawMaterials = materialListRaw; dbCtx.Entry(currRec).State = EntityState.Modified; } // salvo... var result = dbCtx.SaveChanges(); answ = result > 0; } catch (Exception exc) { Log.Error($"Eccezione durante ProdOdlUpdateRawMaterialAsync{Environment.NewLine}{exc}"); } } return answ; } internal async Task> ProdPlantGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetProdPlant .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProdPlantGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } /// /// Creazione di un Batch con relativo Tag e info x creazione ODL correlati /// /// /// internal async Task ProductionBatchCreateAsync(ProductionBatchModel newRec) { ProductionBatchModel dbResult = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { // Definiamo i parametri di input var pDesc = new MySqlParameter("@pDescription", newRec.Description ?? (object)DBNull.Value); var pDate = new MySqlParameter("@pDueDate", newRec.DueDate); var pPref = new MySqlParameter("@pPrefix", "BC."); var pYear = new MySqlParameter("@pYear", newRec.DueDate.Year); var pEnv = new MySqlParameter("@pEnv", newRec.Envir); // Eseguiamo la procedura e mappiamo il risultato direttamente sul modello // Nota: DbSetProdBatch deve essere configurato nel DbContext var result = await dbCtx .DbSetProdBatch .FromSqlRaw("CALL stp_ProdBatch_insert(@pDescription, @pDueDate, @pPrefix, @pYear, @pEnv)", pDesc, pDate, pPref, pYear, pEnv) .ToListAsync(); dbResult = result.FirstOrDefault() ?? new ProductionBatchModel(); } catch (Exception ex) { throw new Exception("Errore durante ProductionBatchCreateAsync", ex); } return dbResult; } } /// /// Insert sul DB di un elenco ODL con calcolo della relativa KEY a cui poter, successivamente, collegare i record child (items) /// /// /// /// internal async Task> ProductionOdlCreateAsync(List listOdl2ins) { //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // avvio transazione using var transaction = await dbCtx.Database.BeginTransactionAsync(); try { int cYear = DateTime.Today.Year; // insert in blocco dbCtx.DbSetProdODL.AddRange(listOdl2ins); // 3. Salvataggio massivo // EF Core 8 ottimizzerà gli insert in batch dove possibile await dbCtx.SaveChangesAsync(); // stored update Tags var pProdBatchID = new MySqlParameter("@pDueDate", listOdl2ins.FirstOrDefault()?.ProdBatchID ?? 0); var pPref = new MySqlParameter("@pPrefix", "ODL."); var pYear = new MySqlParameter("@pYear", cYear); await dbCtx.Database.ExecuteSqlRawAsync("CALL stp_ProdOdl_UpdateTag(@pProdBatchID, @pPrefix, @pYear)", pProdBatchID, pPref, pYear); // 4. Conferma transazione await transaction.CommitAsync(); // A questo punto, ogni oggetto in 'listOdl2ins' ha il ProdODLID aggiornato dal DB return listOdl2ins; } catch (Exception ex) { await transaction.RollbackAsync(); // Logga l'errore secondo le tue necessità throw new Exception("Errore durante la creazione massiva degli ODL", ex); } } } /// /// Elenco PODL non assegnati /// /// internal async Task?> ProductionOdlUnassignAsync() { List? dbRestults = null; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbRestults = await dbCtx .DbSetProdODL .Where(x => !x.DateAssign.HasValue) .Include(x => x.Item2OdlNav) //.ThenInclude(i => i.ProductionItemNav) //.ThenInclude(o => o.OrderRowNav) //.ThenInclude(o => o.OrderNav) .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante ProductionOdlUnassignAsync{Environment.NewLine}{exc}"); } } return dbRestults; } /// /// Esegue merge dei dati nella tab profili del DB con le info accessorie... /// /// /// /// /// internal async Task SaveProfileListAsync(string uID, Constants.EXECENVIRONMENTS execEnvironment, string rawContent) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // solo se ho qualcosa da controllare... if (!string.IsNullOrEmpty(rawContent)) { // in primis recupero profili attuali var dbList = await dbCtx .DbSetConfProfile .ToListAsync(); // ciclo sul contenuto ricevuto, se mancasse aggiungo! List list2check = JsonConvert.DeserializeObject>(rawContent) ?? new List(); List rec2ins = new List(); foreach (var item in list2check) { if (!dbList.Any(x => x.Code == item)) { rec2ins.Add(new ProfileModel() { Code = item, Description = $"{item} - NEW" }); } } // se ho dati li inserisco... if (rec2ins.Count > 0) { dbCtx.DbSetConfProfile.AddRange(rec2ins); // salvo... int numDone = await dbCtx.SaveChangesAsync(); answ = numDone > 0; } } } return answ; } /// /// Salvataggio info serializzate x soglie e dati opzionali sul DB /// /// /// /// /// /// /// internal async Task SaveProfileThreshAsync(string uID, Constants.EXECENVIRONMENTS execEnvironment, string rawThreshold, string rawData) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // solo se ho qualcosa da controllare... if (!string.IsNullOrEmpty(rawThreshold) || !string.IsNullOrEmpty(rawData)) { // in primis recupero profilo da aggiornare var dbRec = await dbCtx .DbSetConfProfile .Where(x => x.Code == uID) .FirstOrDefaultAsync(); // se ho record aggiorno... if (dbRec != null) { dbRec.ProfDataRaw = rawData; dbRec.ThreshDataRaw = rawThreshold; dbCtx.Entry(dbRec).State = EntityState.Modified; } // alrimenti creo + aggiorno else { dbCtx.DbSetConfProfile.Add(new ProfileModel() { Code = uID, Description = $"{uID} - NEW PROFILE", ProfDataRaw = rawData, ThreshDataRaw = rawThreshold }); } // salvo... int numDone = await dbCtx.SaveChangesAsync(); answ = numDone > 0; } } return answ; } /// /// Elenco da DB delel stats aggregate dato periodo inizio/fine /// /// /// /// internal async Task> StatsAggrGetAsync(DateTime dtStart, DateTime dtEnd) { List answ = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // recupero ed ordino per data-ora answ = await dbCtx .DbSetStatsAggr .Where(x => x.Hour >= dtStart && x.Hour <= dtEnd) .AsNoTracking() .OrderBy(x => x.Hour) .ToListAsync(); } return answ; } /// /// Range periodo per chiamate aggregate /// /// internal async Task StatsAggrRangeAsync() { Utils.DtUtils.Periodo answ = new Utils.DtUtils.Periodo(Utils.DtUtils.PeriodSet.Today); using (DataLayerContext dbCtx = new DataLayerContext()) { var query = dbCtx.DbSetStatsAggr.AsQueryable(); var minHour = await query.MinAsync(x => x.Hour); var maxHour = await query.MaxAsync(x => x.Hour); answ.Inizio = minHour; answ.Fine = maxHour; return answ; } } /// /// Esegue insert statistiche aggregate sul DB /// /// Elenco dei record da inserire /// Se true preventivamente elimina record nel periodo richiesto /// internal async Task StatsAggrUpsertAsync(List listRecords, bool removeOld) { int answ = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // in primis se richiesto calcolo range periodo e svuoto... if (removeOld) { var firstRec = listRecords.OrderBy(x => x.Hour).FirstOrDefault(); var lastRec = listRecords.OrderByDescending(x => x.Hour).FirstOrDefault(); if (firstRec != null && lastRec != null) { DateTime startDate = firstRec.Hour; DateTime endDate = lastRec.Hour; // uso direttamente ExecuteDelete await dbCtx .DbSetStatsAggr .Where(x => x.Hour >= startDate && x.Hour <= endDate) .ExecuteDeleteAsync(); } } // ora preparo inserimento massivo await dbCtx .DbSetStatsAggr .AddRangeAsync(listRecords); // salvo! answ = await dbCtx.SaveChangesAsync(); // libero memoria del changeTracker dbCtx.ChangeTracker.Clear(); } return answ; } /// /// Recupera dati stats di dettaglio dato filtro envir/tipo (opzionali) e periodo /// /// /// /// /// /// internal async Task> StatsDetailModelGetAsync(DateTime dtStart, DateTime dtEnd, string sEnvir = "", string sType = "") { List answ = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // recupero ed ordino per data-ora var query = dbCtx.DbSetStatsDet .Where(x => x.Hour >= dtStart && x.Hour <= dtEnd); if (!string.IsNullOrEmpty(sEnvir)) query = query.Where(x => x.Environment == sEnvir); if (!string.IsNullOrEmpty(sType)) query = query.Where(x => x.Type == sType); answ = await query .AsNoTracking() .OrderBy(x => x.Hour) .ThenBy(x => x.Environment) .ThenBy(x => x.Type) .ToListAsync(); } return answ; } /// /// Range periodo x chiamate detail eventualmente filtrate /// /// /// /// internal async Task StatsDetailModelRangeAsync(string sEnvir, string sType) { Utils.DtUtils.Periodo answ = new Utils.DtUtils.Periodo(Utils.DtUtils.PeriodSet.Today); using (DataLayerContext dbCtx = new DataLayerContext()) { var query = dbCtx.DbSetStatsDet.AsQueryable(); if (!string.IsNullOrEmpty(sEnvir)) query = query.Where(x => x.Environment == sEnvir); if (!string.IsNullOrEmpty(sType)) query = query.Where(x => x.Type == sType); var minHour = await query.MinAsync(x => x.Hour); var maxHour = await query.MaxAsync(x => x.Hour); answ.Inizio = minHour; answ.Fine = maxHour; return answ; } } /// /// Esegue insert statistiche di dettaglio sul DB /// /// Elenco dei record da inserire /// Se true preventivamente elimina record nel periodo richiesto /// internal async Task StatsDetailModelUpsertAsync(List listRecords, bool removeOld) { int answ = 0; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // in primis se richiesto calcolo range periodo e svuoto... if (removeOld) { var firstRec = listRecords.OrderBy(x => x.Hour).FirstOrDefault(); var lastRec = listRecords.OrderByDescending(x => x.Hour).FirstOrDefault(); if (firstRec != null && lastRec != null) { DateTime startDate = firstRec.Hour; DateTime endDate = lastRec.Hour; // uso direttamente ExecuteDelete await dbCtx .DbSetStatsDet .Where(x => x.Hour >= startDate && x.Hour <= endDate) .ExecuteDeleteAsync(); } } // ora preparo inserimento massivo await dbCtx .DbSetStatsDet .AddRangeAsync(listRecords); // salvo! answ = await dbCtx.SaveChangesAsync(); // libero memoria del changeTracker dbCtx.ChangeTracker.Clear(); } return answ; } /// /// Elenco completo Tags /// /// internal async Task> TagsGetAllAsync() { List dbResult = new List(); //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { try { dbResult = await dbCtx .DbSetTags .ToListAsync(); } catch (Exception exc) { Log.Error($"Eccezione durante TagsGetAllAsync{Environment.NewLine}{exc}"); } } return dbResult; } #if true internal bool UpdateCodGroup(List bomList) { bool answ = false; //using (DataLayerContext dbCtx = new DataLayerContext(_config)) using (DataLayerContext dbCtx = new DataLayerContext()) { // in primis calcolo i distinct dei CodGroup x eventuale insert preventivo List distCodGroups = bomList .Select(i => i.ClassCode) .Distinct() .Where(c => !string.IsNullOrWhiteSpace(c)) .ToList(); // recupero l'elenco degli itemGroup gestiti var itemGroupList = dbCtx .DbSetItemGroup .ToList(); // elenco da inserire... var codGroupsToInsert = distCodGroups .Where(x => !itemGroupList.Any(i => i.CodGroup == x)) .Select(x => new ItemGroupModel() { CodGroup = x, Description = x }) .ToList(); // se ci sono inserisco! if (codGroupsToInsert != null && codGroupsToInsert.Count > 0) { dbCtx .DbSetItemGroup .AddRange(codGroupsToInsert); // salvo... dbCtx.SaveChanges(); } } return answ; } #endif #endregion Internal Methods #region Private Fields private static IConfiguration _configuration; private static Logger Log = LogManager.GetCurrentClassLogger(); #endregion Private Fields } }