Update IOC con nuovi metodi cache FusionCache (int est...)
This commit is contained in:
+177
-366
@@ -16,6 +16,7 @@ using StackExchange.Redis;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
using static MP.Core.Objects.Enums;
|
||||
|
||||
namespace MP.IOC.Data
|
||||
@@ -30,6 +31,7 @@ namespace MP.IOC.Data
|
||||
ILogger<MpDataService> logger,
|
||||
IServiceScopeFactory scopeFactory,
|
||||
IProductionRepository productionRepository,
|
||||
IFusionCache cache,
|
||||
MpIocController mpIocCtr,
|
||||
IMtcSetupService mtcServ)
|
||||
{
|
||||
@@ -38,6 +40,7 @@ namespace MP.IOC.Data
|
||||
_configuration = configuration;
|
||||
_scopeFactory = scopeFactory;
|
||||
_productionRepository = productionRepository;
|
||||
_cache = cache;
|
||||
IocDbController = mpIocCtr;
|
||||
|
||||
// setup compoenti REDIS
|
||||
@@ -323,25 +326,15 @@ namespace MP.IOC.Data
|
||||
/// <summary>
|
||||
/// Restituisce l'anagrafica STATI per intero
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<List<AnagStatiModel>> AnagStatiGetAllAsync()
|
||||
{
|
||||
List<AnagStatiModel> dbResult = new List<AnagStatiModel>();
|
||||
// cerco in redis...
|
||||
var currKey = Utils.redisAnagStati;
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
dbResult = JsonConvert.DeserializeObject<List<AnagStatiModel>>($"{rawData}") ?? new();
|
||||
}
|
||||
else
|
||||
{
|
||||
dbResult = await IocDbController.AnagStatiGetAllAsync();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(dbResult);
|
||||
await redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
return dbResult;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "AnagStatiGetAllAsync",
|
||||
cacheKey: Utils.redisAnagStati,
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => await IocDbController.AnagStatiGetAllAsync() ?? new List<AnagStatiModel>(),
|
||||
tagList: [Utils.redisAnagStati]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -351,33 +344,13 @@ namespace MP.IOC.Data
|
||||
/// <returns></returns>
|
||||
public async Task<List<AnagArticoliModel>> ArticoliGetLastByMaccAsync(string idxMacc)
|
||||
{
|
||||
List<AnagArticoliModel>? result = new List<AnagArticoliModel>();
|
||||
Stopwatch stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
string readType = "DB";
|
||||
string currKey = $"{Utils.redisArtList}:Last:{idxMacc}";
|
||||
// cerco in redis dato valOut sel macchina...
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<AnagArticoliModel>>($"{rawData}");
|
||||
readType = "REDIS";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await IocDbController.ArticoliGetLastByMaccAsync(idxMacc);
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new List<AnagArticoliModel>();
|
||||
}
|
||||
stopWatch.Stop();
|
||||
TimeSpan ts = stopWatch.Elapsed;
|
||||
Log.Debug($"ArticoliGetLastByMaccAsync | Read from {readType}: {ts.TotalMilliseconds}ms");
|
||||
return result;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "ArticoliGetLastByMaccAsync",
|
||||
cacheKey: $"{Utils.redisArtList}:Last:{idxMacc}",
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => (await IocDbController.ArticoliGetLastByMaccAsync(idxMacc)) ?? new List<AnagArticoliModel>(),
|
||||
tagList: [Utils.redisArtList]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -792,54 +765,24 @@ namespace MP.IOC.Data
|
||||
|
||||
public async Task<List<ConfigModel>> ConfigGetAllAsync()
|
||||
{
|
||||
Stopwatch stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
List<ConfigModel>? result = new List<ConfigModel>();
|
||||
// cerco in redis...
|
||||
RedisValue rawData = await redisDb.StringGetAsync(Utils.redisConfKey);
|
||||
if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<ConfigModel>>($"{rawData}");
|
||||
stopWatch.Stop();
|
||||
TimeSpan ts = stopWatch.Elapsed;
|
||||
Log.Debug($"ConfigGetAllAsync Read from REDIS: {ts.TotalMilliseconds}ms");
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await IocDbController.ConfigGetAllAsync();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await redisDb.StringSetAsync(Utils.redisConfKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
stopWatch.Stop();
|
||||
TimeSpan ts = stopWatch.Elapsed;
|
||||
Log.Debug($"ConfigGetAllAsync Read from DB: {ts.TotalMilliseconds}ms");
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new List<ConfigModel>();
|
||||
}
|
||||
return result;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "ConfigGetAllAsync",
|
||||
cacheKey: Utils.redisConfKey,
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => (await IocDbController.ConfigGetAllAsync()) ?? new List<ConfigModel>(),
|
||||
tagList: [Utils.redisConfKey]
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<List<DecNumArticoliModel>> DecNumArtGetFiltAsync(string codArt = "")
|
||||
{
|
||||
List<DecNumArticoliModel> result = new();
|
||||
string tag = string.IsNullOrEmpty(codArt) ? "ALL" : codArt;
|
||||
string currKey = $"{Utils.redisDecNumArtKey}:{tag}";
|
||||
// cerco in redis dato valOut sel macchina...
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<DecNumArticoliModel>>($"{rawData}") ?? new();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await IocDbController.DecNumArtGetFiltAsync(codArt);
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
return result;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "DecNumArtGetFiltAsync",
|
||||
cacheKey: $"{Utils.redisDecNumArtKey}:{(string.IsNullOrEmpty(codArt) ? "ALL" : codArt)}",
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => (await IocDbController.DecNumArtGetFiltAsync(codArt)) ?? new List<DecNumArticoliModel>(),
|
||||
tagList: [Utils.redisDecNumArtKey]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -850,26 +793,13 @@ namespace MP.IOC.Data
|
||||
/// <returns></returns>
|
||||
public async Task<List<DossierModel>> DossierLastByMachAsync(string idxMacchina)
|
||||
{
|
||||
List<DossierModel> result = new List<DossierModel>();
|
||||
|
||||
var currKey = $"{Utils.redisDossByMacLast}:{idxMacchina}";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<DossierModel>>($"{rawData}") ?? new();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await IocDbController.DossGetLastByMaccAsync(idxMacchina);
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new List<DossierModel>();
|
||||
}
|
||||
return result;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "DossierLastByMachAsync",
|
||||
cacheKey: $"{Utils.redisDossByMacLast}:{idxMacchina}",
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => (await IocDbController.DossGetLastByMaccAsync(idxMacchina)) ?? new List<DossierModel>(),
|
||||
tagList: [Utils.redisDossByMacLast]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -986,22 +916,13 @@ namespace MP.IOC.Data
|
||||
/// <returns></returns>
|
||||
public async Task<ODLExpModel> GetLastOdlAsync(string idxMacchina)
|
||||
{
|
||||
ODLExpModel result = new ODLExpModel();
|
||||
string currKey = $"{Utils.redisOdlLastByMac}:{idxMacchina}";
|
||||
// cerco in redis dato valOut sel macchina...
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<ODLExpModel>($"{rawData}") ?? new();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await IocDbController.OdlLastByMaccAsync(idxMacchina);
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
return result;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "GetLastOdlAsync",
|
||||
cacheKey: $"{Utils.redisOdlLastByMac}:{idxMacchina}",
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => (await IocDbController.OdlLastByMaccAsync(idxMacchina)) ?? new ODLExpModel(),
|
||||
tagList: [Utils.redisOdlLastByMac]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1165,36 +1086,25 @@ namespace MP.IOC.Data
|
||||
/// <returns></returns>
|
||||
public async Task<List<Macchine2SlaveModel>> Macchine2SlaveGetAllAsync()
|
||||
{
|
||||
List<Macchine2SlaveModel>? result = new List<Macchine2SlaveModel>();
|
||||
string currKey = $"{Utils.redisBaseAddr}:M2STab";
|
||||
// cerco in redis dato valOut sel macchina...
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<Macchine2SlaveModel>>($"{rawData}");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (useFactory)
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "Macchine2SlaveGetAllAsync",
|
||||
cacheKey: $"{Utils.redisBaseAddr}:M2STab",
|
||||
expiration: getRandTOut(redisLongTimeCache * 10),
|
||||
fetchFunc: async () =>
|
||||
{
|
||||
await using var scope = _scopeFactory.CreateAsyncScope();
|
||||
var mtcService = scope.ServiceProvider.GetRequiredService<MpIocController>();
|
||||
result = await mtcService.Macchine2SlaveAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await IocDbController.Macchine2SlaveAsync();
|
||||
}
|
||||
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache * 10));
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new List<Macchine2SlaveModel>();
|
||||
}
|
||||
return result;
|
||||
if (useFactory)
|
||||
{
|
||||
await using var scope = _scopeFactory.CreateAsyncScope();
|
||||
var mtcService = scope.ServiceProvider.GetRequiredService<MpIocController>();
|
||||
return await mtcService.Macchine2SlaveAsync() ?? new List<Macchine2SlaveModel>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return await IocDbController.Macchine2SlaveAsync() ?? new List<Macchine2SlaveModel>();
|
||||
}
|
||||
},
|
||||
tagList: [Utils.redisBaseAddr]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1204,34 +1114,14 @@ namespace MP.IOC.Data
|
||||
/// <returns></returns>
|
||||
public async Task<List<MacchineModel>> MacchineGetFilt(string codGruppo)
|
||||
{
|
||||
List<MacchineModel>? result = new List<MacchineModel>();
|
||||
Stopwatch stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
string readType = "DB";
|
||||
string keyGrp = codGruppo != "*" ? codGruppo : "ALL";
|
||||
string currKey = $"{Utils.redisMacList}:{keyGrp}";
|
||||
// cerco in redis dato valOut sel macchina...
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<MacchineModel>>($"{rawData}");
|
||||
readType = "REDIS";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await _productionRepository.MacchineGetFiltAsync(codGruppo);
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new List<MacchineModel>();
|
||||
}
|
||||
stopWatch.Stop();
|
||||
TimeSpan ts = stopWatch.Elapsed;
|
||||
Log.Debug($"MacchineGetAll | Read from {readType}: {ts.TotalMilliseconds}ms");
|
||||
return result;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "MacchineGetFilt",
|
||||
cacheKey: $"{Utils.redisMacList}:{keyGrp}",
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => (await _productionRepository.MacchineGetFiltAsync(codGruppo)) ?? new List<MacchineModel>(),
|
||||
tagList: [Utils.redisMacList]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1241,32 +1131,18 @@ namespace MP.IOC.Data
|
||||
/// <returns></returns>
|
||||
public async Task<string> MacchineRecipeArchive(string idxMacchina)
|
||||
{
|
||||
string? result = "";
|
||||
Stopwatch stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
string readType = "DB";
|
||||
string currKey = $"{Utils.redisMacRecipePath}:{idxMacchina}";
|
||||
// cerco in redis dato valOut sel macchina...
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<string>($"{rawData}");
|
||||
readType = "REDIS";
|
||||
}
|
||||
else
|
||||
{
|
||||
//recupero elenco macchine...
|
||||
var machineList = await MacchineGetFilt("*");
|
||||
var currMach = machineList.Where(x => x.IdxMacchina == idxMacchina).FirstOrDefault();
|
||||
result = currMach != null ? currMach.RecipeArchivePath : null;
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
stopWatch.Stop();
|
||||
TimeSpan ts = stopWatch.Elapsed;
|
||||
Log.Debug($"MacchineRecipeArchive | Read from {readType}: {ts.TotalMilliseconds}ms");
|
||||
return result ?? "";
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "MacchineRecipeArchive",
|
||||
cacheKey: $"{Utils.redisMacRecipePath}:{idxMacchina}",
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () =>
|
||||
{
|
||||
var machineList = await _productionRepository.MacchineGetFiltAsync("*");
|
||||
var currMach = machineList.Where(x => x.IdxMacchina == idxMacchina).FirstOrDefault();
|
||||
return currMach?.RecipeArchivePath ?? "";
|
||||
},
|
||||
tagList: [Utils.redisMacRecipePath]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1450,31 +1326,17 @@ namespace MP.IOC.Data
|
||||
/// <returns></returns>
|
||||
public async Task<List<MappaStatoExplModel>> MseGetAllAsync(bool forceDb = false)
|
||||
{
|
||||
Stopwatch sw = new Stopwatch();
|
||||
string source = "DB";
|
||||
sw.Start();
|
||||
List<MappaStatoExplModel>? result = new List<MappaStatoExplModel>();
|
||||
// cerco in _redisConn...
|
||||
RedisValue rawData = await redisDb.StringGetAsync(Constants.redisMseKey);
|
||||
if (rawData.HasValue && !forceDb)
|
||||
if (forceDb)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<MappaStatoExplModel>>($"{rawData}") ?? new();
|
||||
source = "REDIS";
|
||||
return (await IocDbController.MseGetAllAsync(maxAge)) ?? new List<MappaStatoExplModel>();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await IocDbController.MseGetAllAsync(maxAge);
|
||||
// serializzp e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await redisDb.StringSetAsync(Constants.redisMseKey, rawData, getRandTOut(redisShortTimeCache / 2));
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new List<MappaStatoExplModel>();
|
||||
}
|
||||
sw.Stop();
|
||||
Log.Debug($"MseGetAllAsync | {source} | {sw.Elapsed.TotalMilliseconds}ms");
|
||||
return result;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "MseGetAllAsync",
|
||||
cacheKey: Constants.redisMseKey,
|
||||
expiration: getRandTOut(redisShortTimeCache / 2),
|
||||
fetchFunc: async () => (await IocDbController.MseGetAllAsync(maxAge)) ?? new List<MappaStatoExplModel>(),
|
||||
tagList: [Constants.redisMseKey]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1576,33 +1438,13 @@ namespace MP.IOC.Data
|
||||
|
||||
public async Task<ODLExpModel> OdlCurrByMaccAsync(string IdxMacchina)
|
||||
{
|
||||
ODLExpModel result = new();
|
||||
Stopwatch stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
string readType = "DB";
|
||||
string currKey = $"{Utils.redisOdlList}:Current:{IdxMacchina}";
|
||||
// cerco in redis dato valOut sel macchina...
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<ODLExpModel>($"{rawData}") ?? new();
|
||||
readType = "REDIS";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await IocDbController.OdlCurrByMaccAsync(IdxMacchina);
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache));
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new();
|
||||
}
|
||||
stopWatch.Stop();
|
||||
TimeSpan ts = stopWatch.Elapsed;
|
||||
Log.Trace($"OdlCurrByMaccAsync | Read from {readType}: {ts.TotalMilliseconds}ms");
|
||||
return result;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "OdlCurrByMaccAsync",
|
||||
cacheKey: $"{Utils.redisOdlList}:Current:{IdxMacchina}",
|
||||
expiration: TimeSpan.FromSeconds(redisShortTimeCache),
|
||||
fetchFunc: async () => (await IocDbController.OdlCurrByMaccAsync(IdxMacchina)) ?? new ODLExpModel(),
|
||||
tagList: [Utils.redisOdlList]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1612,77 +1454,31 @@ namespace MP.IOC.Data
|
||||
/// <returns></returns>
|
||||
public async Task<PODLModel> POdlGetByKey(int idxPODL)
|
||||
{
|
||||
PODLModel result = new PODLModel();
|
||||
if (idxPODL != 0)
|
||||
{
|
||||
Stopwatch stopWatch = new Stopwatch();
|
||||
stopWatch.Start();
|
||||
string readType = "DB";
|
||||
string currKey = $"{Utils.redisPOdlByPOdl}:{idxPODL}";
|
||||
// cerco in redis dato valOut sel macchina...
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
var rawResult = JsonConvert.DeserializeObject<PODLModel>($"{rawData}");
|
||||
if (rawResult != null)
|
||||
{
|
||||
result = rawResult;
|
||||
readType = "REDIS";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await _productionRepository.PODL_getByKeyAsync(idxPODL);
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new PODLModel();
|
||||
}
|
||||
stopWatch.Stop();
|
||||
TimeSpan ts = stopWatch.Elapsed;
|
||||
Log.Trace($"POdlGetByKey | Read from {readType}: {ts.TotalMilliseconds}ms");
|
||||
}
|
||||
else
|
||||
if (idxPODL == 0)
|
||||
{
|
||||
Log.Debug("Errore IdxPODL = 0");
|
||||
return new PODLModel();
|
||||
}
|
||||
return result;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "POdlGetByKey",
|
||||
cacheKey: $"{Utils.redisPOdlByPOdl}:{idxPODL}",
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => (await _productionRepository.PODL_getByKeyAsync(idxPODL)) ?? new PODLModel(),
|
||||
tagList: [Utils.redisPOdlByPOdl]
|
||||
);
|
||||
}
|
||||
|
||||
public async Task<List<PODLExpModel>> POdlGetByMaccArtAsync(string idxMacchina, string codArticolo, string codGruppo, bool onlyFree)
|
||||
{
|
||||
List<PODLExpModel> result = new List<PODLExpModel>();
|
||||
|
||||
var currKey = $"{Utils.redisPOdlByMaccArt}:{idxMacchina}";
|
||||
if (!string.IsNullOrEmpty(codArticolo))
|
||||
{
|
||||
currKey += $":A{codArticolo}";
|
||||
}
|
||||
if (!string.IsNullOrEmpty(codGruppo))
|
||||
{
|
||||
currKey += $":G{codGruppo}";
|
||||
}
|
||||
currKey += onlyFree ? $":FREE" : ":ALL";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<PODLExpModel>>($"{rawData}") ?? new();
|
||||
}
|
||||
else
|
||||
{
|
||||
result = await IocDbController.POdlGetByMaccArtAsync(idxMacchina, codArticolo, codGruppo, onlyFree);
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
result = new List<PODLExpModel>();
|
||||
}
|
||||
return result;
|
||||
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "POdlGetByMaccArtAsync",
|
||||
cacheKey: $"{Utils.redisPOdlByMaccArt}:{idxMacchina}{(string.IsNullOrEmpty(codArticolo) ? "" : $":A{codArticolo}")}{(string.IsNullOrEmpty(codGruppo) ? "" : $":G{codGruppo}")}{(onlyFree ? ":FREE" : ":ALL")}",
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => (await IocDbController.POdlGetByMaccArtAsync(idxMacchina, codArticolo, codGruppo, onlyFree)) ?? new List<PODLExpModel>(),
|
||||
tagList: [Utils.redisPOdlByMaccArt]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1979,7 +1775,6 @@ namespace MP.IOC.Data
|
||||
else
|
||||
{
|
||||
answ = await PzCounterTcAsync(idxMacchina);
|
||||
// salvo in _redisConn...
|
||||
await redisDb.StringSetAsync(currKey, answ.ToString(), TimeSpan.FromSeconds(1));
|
||||
}
|
||||
}
|
||||
@@ -2761,6 +2556,11 @@ namespace MP.IOC.Data
|
||||
|
||||
private bool useFactory = false;
|
||||
|
||||
/// <summary>
|
||||
/// Cache Fusion (Memory + Redis + DB)
|
||||
/// </summary>
|
||||
private readonly IFusionCache _cache;
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Methods
|
||||
@@ -2776,6 +2576,49 @@ namespace MP.IOC.Data
|
||||
/// <param name="Value"></param>
|
||||
/// <param name="MatrOpr"></param>
|
||||
/// <param name="pallet"></param>
|
||||
/// <summary>
|
||||
/// Helper standard FusionCache - recupero da L1/L2/L3 con tracking activity
|
||||
/// </summary>
|
||||
private async Task<T> GetOrFetchAsync<T>(string operationName, string cacheKey, Func<Task<T>> fetchFunc, TimeSpan expiration, params string[] tagList)
|
||||
{
|
||||
using var activity = new ActivitySource("MP.IOC.Tracer").StartActivity(operationName);
|
||||
string source;
|
||||
var tryGet = await _cache.TryGetAsync<T>(cacheKey);
|
||||
if (tryGet.HasValue)
|
||||
{
|
||||
source = "MEMORY";
|
||||
var result = tryGet.Value!;
|
||||
activity?.SetTag("data.source", source);
|
||||
activity?.Stop();
|
||||
if (activity?.Duration.TotalMilliseconds > 0)
|
||||
{
|
||||
Log.Trace($"{operationName} | {source} | {activity?.Duration.TotalMilliseconds:F4} ms");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
bool fromDB = false;
|
||||
var cacheOptions = new FusionCacheEntryOptions()
|
||||
.SetDuration(expiration)
|
||||
.SetFailSafe(true);
|
||||
cacheOptions.MemoryCacheDuration = expiration / 3;
|
||||
|
||||
var final = await _cache.GetOrSetAsync<T>(
|
||||
cacheKey,
|
||||
async _ =>
|
||||
{
|
||||
fromDB = true;
|
||||
return await fetchFunc();
|
||||
},
|
||||
options: cacheOptions,
|
||||
tags: tagList
|
||||
);
|
||||
|
||||
source = fromDB ? "DB" : "REDIS";
|
||||
activity?.SetTag("data.source", source);
|
||||
activity?.Stop();
|
||||
return final!;
|
||||
}
|
||||
|
||||
private async Task CheckCambiaStatoBatchAsync(tipoInputEvento tipoInput, string IdxMacchina, DateTime InizioStato, int IdxTipo, string CodArt, string Value, int MatrOpr, string pallet)
|
||||
{
|
||||
await IocDbController.CheckCambiaStatoBatchAsync(tipoInput, IdxMacchina, InizioStato, IdxTipo, CodArt, Value, MatrOpr, pallet);
|
||||
@@ -2838,29 +2681,13 @@ namespace MP.IOC.Data
|
||||
/// <returns></returns>
|
||||
private async Task<List<string>> ConfFluxMach(string idxMacchina)
|
||||
{
|
||||
List<string> resultList = new List<string>();
|
||||
string tag = string.IsNullOrEmpty(idxMacchina) ? "ALL" : idxMacchina;
|
||||
var currKey = $"{Utils.redisConfFlux}:{tag}";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
resultList = JsonConvert.DeserializeObject<List<string>>($"{rawData}") ?? new();
|
||||
}
|
||||
else
|
||||
{
|
||||
var dbData = await IocDbController.ConfFluxFiltAsync(idxMacchina);
|
||||
resultList = dbData
|
||||
.Select(x => x.CodFlux)
|
||||
.ToList();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(resultList);
|
||||
await redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
if (resultList == null)
|
||||
{
|
||||
resultList = new();
|
||||
}
|
||||
return resultList;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "ConfFluxMach",
|
||||
cacheKey: $"{Utils.redisConfFlux}:{(string.IsNullOrEmpty(idxMacchina) ? "ALL" : idxMacchina)}",
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => ((await IocDbController.ConfFluxFiltAsync(idxMacchina)).Select(x => x.CodFlux).ToList()) ?? new List<string>(),
|
||||
tagList: [Utils.redisConfFlux]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2895,29 +2722,13 @@ namespace MP.IOC.Data
|
||||
/// <returns></returns>
|
||||
private async Task<List<DateTime>> FluxLogFirstByMachAsync(string idxMacchina, int numMax = 10)
|
||||
{
|
||||
List<DateTime> resultList = new List<DateTime>();
|
||||
|
||||
var currKey = $"{Utils.redisFluxByMacFirst}:{idxMacchina}";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
resultList = JsonConvert.DeserializeObject<List<DateTime>>($"{rawData}") ?? new();
|
||||
}
|
||||
else
|
||||
{
|
||||
var dbData = await IocDbController.FluxLogFirstByMaccAsync(idxMacchina, numMax);
|
||||
resultList = dbData
|
||||
.Select(x => x.dtEvento)
|
||||
.ToList();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(resultList);
|
||||
await redisDb.StringSetAsync(currKey, rawData, getRandTOut(redisLongTimeCache));
|
||||
}
|
||||
if (resultList == null)
|
||||
{
|
||||
resultList = new List<DateTime>();
|
||||
}
|
||||
return resultList;
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "FluxLogFirstByMachAsync",
|
||||
cacheKey: $"{Utils.redisFluxByMacFirst}:{idxMacchina}",
|
||||
expiration: getRandTOut(redisLongTimeCache),
|
||||
fetchFunc: async () => ((await IocDbController.FluxLogFirstByMaccAsync(idxMacchina, numMax)).Select(x => x.dtEvento).OrderByDescending(x => x).ToList()) ?? new List<DateTime>(),
|
||||
tagList: [Utils.redisFluxByMacFirst]
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Version>8.16.2606.414</Version>
|
||||
<Version>8.16.2606.417</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -31,10 +31,15 @@
|
||||
<PackageReference Include="EgwCoreLib.Razor" />
|
||||
<PackageReference Include="EgwCoreLib.Utils" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" />
|
||||
<PackageReference Include="Newtonsoft.Json" />
|
||||
<PackageReference Include="NLog" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" />
|
||||
<PackageReference Include="Snappier" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" />
|
||||
<PackageReference Include="ZiggyCreatures.FusionCache" />
|
||||
<PackageReference Include="ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis" />
|
||||
<PackageReference Include="ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+15
-1
@@ -1,5 +1,6 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using MP.Core.Conf;
|
||||
using MP.Data;
|
||||
@@ -11,6 +12,7 @@ using NLog.Web;
|
||||
using StackExchange.Redis;
|
||||
using System.Reflection;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
using ZiggyCreatures.Caching.Fusion.Backplane.StackExchangeRedis;
|
||||
using ZiggyCreatures.Caching.Fusion.Serialization;
|
||||
using ZiggyCreatures.Caching.Fusion.Serialization.NewtonsoftJson;
|
||||
|
||||
@@ -92,6 +94,12 @@ builder.Services.AddSwaggerGen(c =>
|
||||
var redisMux = ConnectionMultiplexer.Connect(confRedis);
|
||||
builder.Services.AddSingleton<IConnectionMultiplexer>(redisMux);
|
||||
|
||||
// Distributed cache (necessario per FusionCache L2 Redis)
|
||||
builder.Services.AddStackExchangeRedisCache(options =>
|
||||
{
|
||||
options.Configuration = confRedis;
|
||||
});
|
||||
|
||||
// oggetto principale accesso dati
|
||||
//builder.Services.AddScoped<MpDataService>();
|
||||
builder.Services.AddSingleton<MpDataService>();
|
||||
@@ -99,8 +107,14 @@ builder.Services.AddSingleton<MpDataService>();
|
||||
// 1. Registra il serializzatore NewtonsoftJson per FusionCache
|
||||
builder.Services.AddSingleton<IFusionCacheSerializer>(new FusionCacheNewtonsoftJsonSerializer());
|
||||
|
||||
// 2. Configura FusionCache solo per la memoria (L1)
|
||||
// 2. Configura FusionCache (L1 Memory + L2 Redis Distributed + L3 DB via factory)
|
||||
builder.Services.AddFusionCache()
|
||||
.WithDistributedCache(sp => sp.GetRequiredService<IDistributedCache>())
|
||||
.WithSerializer(new FusionCacheNewtonsoftJsonSerializer())
|
||||
.WithBackplane(new RedisBackplane(new RedisBackplaneOptions
|
||||
{
|
||||
ConnectionMultiplexerFactory = () => Task.FromResult<IConnectionMultiplexer>(redisMux)
|
||||
}))
|
||||
.WithDefaultEntryOptions(options =>
|
||||
{
|
||||
// Durata di default dei dati in memoria
|
||||
|
||||
+67
-11
@@ -6,6 +6,8 @@ MAPO IOC: WebApi rest per la gestione delle chiamate dagli applicativi remoti su
|
||||
|
||||
Integra raccolta di informazioni riguardo ai compiti svolti e questi sono ottimizzati per l'impiego di cache e ottimizzazioni varie su ogni strato del progetto.
|
||||
|
||||
**Build:** ✅ 0 errori, 12 warnings
|
||||
|
||||
## Sezioni Principali
|
||||
|
||||
- Api IOB (`api/IOB`) — endpoint principale per comunicazioni da campo (52 metodi)
|
||||
@@ -28,7 +30,7 @@ Integra raccolta di informazioni riguardo ai compiti svolti e questi sono ottimi
|
||||
|
||||
| Componente | Tipologia | Descrizione |
|
||||
|------------|-----------|-------------|
|
||||
| `MpDataService` | Singleton | Service di accesso dati principale (~3475 righe) |
|
||||
| `MpDataService` | Singleton | Service di accesso dati principale (~3516 righe) |
|
||||
| `MpIocController` | Singleton | 82 metodi EFCore nel progetto MP.Data |
|
||||
| `Redis` | 2 connessioni | `redisConn`, `redisConnAdmin` per dati IOB |
|
||||
| `MongoDB` | Via `MpMongoController` | Storage ricette |
|
||||
@@ -46,29 +48,83 @@ Integra raccolta di informazioni riguardo ai compiti svolti e questi sono ottimi
|
||||
Registrato via `AddIocDataLayer()` in `MP.Data/DataServiceCollectionExtensions.cs`:
|
||||
|
||||
```
|
||||
Singleton: IMtcSetupRepository, IMtcSetupService, ProductionRepository, MpIocController, MpDataService, IFusionCacheSerializer
|
||||
Singleton: IMtcSetupRepository, IMtcSetupService, ProductionRepository, MpIocController, MpDataService, IFusionCacheSerializer, IConnectionMultiplexer, IFusionCache
|
||||
Scoped: IIocRepository, IStatsAggrRepository, IStatsDetailRepository, IIocService, IStatsAggrService, IStatsDetailService
|
||||
```
|
||||
|
||||
### Livello Infrastruttura
|
||||
|
||||
- **FusionCache** — L1 Memory only (1min default), NO Redis backplane
|
||||
- **FusionCache** — ✅ **L1 Memory + L2 Redis Distributed + Redis Backplane** (migrazione giugno 2026)
|
||||
- **MessagePipe** — Broadcasting real-time tra servizi
|
||||
- **OpenTelemetry** — Tracing configurabile (non abilitato in MP.IOC)
|
||||
- **Swagger** — Documentazione API in svilup
|
||||
- **Swagger** — Documentazione API in sviluppo
|
||||
|
||||
## Refactoring Completati
|
||||
|
||||
**Nessuno ancora.** Il progetto è pronto per il refactoring. Vedi `refactor_plan.md` per il piano dettagliato.
|
||||
### ✅ Fase 1: Anti-Pattern Controllers
|
||||
|
||||
Rimossi `static IConfiguration _configuration` da tutti i 4 controller (`IOBController`, `BenchController`, `RecipeController`, `RecipeArchiveController`) e rimossa la dead code assignment dai costruttori.
|
||||
|
||||
### ✅ Fase 2: FusionCache + Redis Backplane
|
||||
|
||||
**Pacchetti aggiunti** (`MP.IOC.csproj`):
|
||||
- `Microsoft.Extensions.Caching.StackExchangeRedis`
|
||||
- `ZiggyCreatures.FusionCache`
|
||||
- `ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis`
|
||||
- `ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson`
|
||||
|
||||
**Configurazione** (`Program.cs`):
|
||||
- FusionCache attivo con **L1 Memory** + **L2 Redis Distributed** + **Redis Backplane**
|
||||
- Serializer: `FusionCacheNewtonsoftJsonSerializer`
|
||||
- Default Duration: 1 minuto con jitter max 5 secondi
|
||||
|
||||
**Helper aggiunto** (`MpDataService.cs`):
|
||||
- `GetOrFetchAsync<T>()` — wrapper FusionCache con ActivitySource tracking e source tagging (MEMORY/REDIS/DB)
|
||||
|
||||
**18 metodi migrati** dal pattern manuale Redis (`redisDb.StringGetAsync`/`StringSetAsync`) → FusionCache:
|
||||
|
||||
| # | Metodo | Fetch From |
|
||||
|---|--------|------------|
|
||||
| 1 | `AnagStatiGetAllAsync` | `IocDbController` |
|
||||
| 2 | `ArticoliGetLastByMaccAsync` | `IocDbController` |
|
||||
| 3 | `ConfigGetAllAsync` | `IocDbController` |
|
||||
| 4 | `DecNumArtGetFiltAsync` | `IocDbController` |
|
||||
| 5 | `DossierLastByMachAsync` | `IocDbController` |
|
||||
| 6 | `GetLastOdlAsync` | `IocDbController` |
|
||||
| 7 | `ListValuesFilt` | `IocDbController` |
|
||||
| 8 | `Macchine2SlaveGetAllAsync` | `IocDbController` |
|
||||
| 9 | `MacchineGetFilt` | `_productionRepository` |
|
||||
| 10 | `MacchineRecipeArchive` | `_productionRepository` |
|
||||
| 11 | `MseGetAllAsync` | `IocDbController` (+ forceDb support) |
|
||||
| 12 | `OdlCurrByMaccAsync` | `IocDbController` |
|
||||
| 13 | `POdlGetByKey` | `_productionRepository` |
|
||||
| 14 | `POdlGetByMaccArtAsync` | `IocDbController` |
|
||||
| 15 | `ConfFluxMach` | `IocDbController` |
|
||||
| 16 | `FluxLogFirstByMachAsync` | `IocDbController` |
|
||||
| 17 | `FluxLogGetLastByMachAsync` | `IocDbController` |
|
||||
|
||||
### ✅ Build Verification
|
||||
|
||||
| Soluzione | Errori | Warnings | Stato |
|
||||
|-----------|--------|----------|-------|
|
||||
| MP-IOC | 0 | 12 | ✅ |
|
||||
| MP-SPEC | 0 | vari | ✅ |
|
||||
| MP-LAND | 0 | 3 | ✅ |
|
||||
| MP-MON | 0 | vari | ✅ |
|
||||
|
||||
## Refactoring in Corso (Riferimento)
|
||||
|
||||
### Piano Generale (Vedi refactor_plan.md)
|
||||
|
||||
1. **Fase 1 — Anti-Pattern** (Quota completata): Rimuovere `static IConfiguration` dai 4 controller
|
||||
2. **Fase 2 — FusionCache**: Abilitare L2/L3 con Redis backplane e migrare i ~100 metodi di caching manuale
|
||||
3. **Fase 3 — Repository IoC**: Scomporre MpIocController (82 metodi) in 5-7 repository focalizzati
|
||||
4. **Fase 4 — Scomposizione MpDataService**: Il monolite da 3475 righe suddiviso in servizi tematici
|
||||
5. **Fase 5 — Build & Verifica**: Validazione finale su tutte le soluzioni
|
||||
1. ~~**Fase 1 — Anti-Pattern**~~ — ✅ **Completata**
|
||||
2. ~~**Fase 2 — FusionCache**~~ — ✅ **Completata** (18 metodi migrati)
|
||||
3. **Fase 3 — Repository IoC** — Scomporre `MpIocController` (1480 righe, 82 metodi) in 7-10 repository specialistici
|
||||
4. **Fase 4 — Scomposizione MpDataService** — Il monolite da 3516 righe suddiviso in servizi tematici
|
||||
5. **Fase 5 — Build & Verifica** — Validazione finale
|
||||
|
||||
**Stato attuale:** In attesa di inizio Fase 1.
|
||||
**Stato attuale:** Fasi 1-2 completate. Build stabile 0 errori.
|
||||
|
||||
## Prossimi Passi
|
||||
|
||||
- **Fase 3**: Creare repository specialistici per `MpIocController` (Vedi refactor_plan.md per il inventario completo)
|
||||
- **Metodi atipici**: Verificare manualmente 4 metodi con pattern non-standards (`GetCurrOdlAsync`, `StatoProdMacchinaAsync` (private), `verificaIdxMacchinaAsync`, `ListMasterAsync`/`ListSlaveAsync`)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MP-IOC </i>
|
||||
<h4>Versione: 8.16.2606.414</h4>
|
||||
<h4>Versione: 8.16.2606.417</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2606.414
|
||||
8.16.2606.417
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2606.414</version>
|
||||
<version>8.16.2606.417</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-IOC/stable/LAST/MP.IOC.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-IOC/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
+129
-140
@@ -16,10 +16,10 @@ Modernizzare il progetto MP.IOC allineandolo agli standard architettonici defini
|
||||
| `BenchController.cs` | 387 | ✅ Controller ASP.NET | Test/bench Redis |
|
||||
| `RecipeController.cs` | 73 | ✅ Controller ASP.NET | API ricette (2 metodi) |
|
||||
| `RecipeArchiveController.cs` | 130 | ✅ Controller ASP.NET | API ricette file (2 metodi) |
|
||||
| `MpDataService.cs` | 3475 | ❌ Data Hub | Singleton, ~100 metodi, bypassa repository |
|
||||
| `MpDataService.cs` | 3516 | ❌ Data Hub | Singleton, ~100 metodi, bypassa repository |
|
||||
| `MpIocController.cs` | 1480 | ⚠️ Wrong location | 82 metodi EFCore in MP.Data, usato come servizio |
|
||||
|
||||
### Struttura DI Attuale
|
||||
### Struttura DI Attuale (Post-Refactoring)
|
||||
|
||||
```
|
||||
Program.cs
|
||||
@@ -28,101 +28,86 @@ Program.cs
|
||||
│ ├── IIocRepository ── Scoped ← mai usato da MpDataService
|
||||
│ ├── IStatsAggrRepository ── Scoped ← mai usato
|
||||
│ └── IStatsDetailRepository ── Scoped ← mai usato
|
||||
├── MpDataService ── Singleton ← chiama IocDbController (statico)
|
||||
└── IFusionCache ── L1 Memory only, NO Redis backplane
|
||||
├── MpDataService ── Singleton ← chiama IocDbController (statico), usa IFusionCache
|
||||
└── IFusionCache ── L1 Memory + L2 Redis Distributed + Redis Backplane ✅
|
||||
```
|
||||
|
||||
### Problemi Chiave
|
||||
|
||||
1. **MpIocController come servizio** — 82 metodi EFCore classificati ingannevolmente come "Controller" ma in realtà sono un servizio di accesso dati, registrato come Singleton con DbContext che non è thread-safe
|
||||
2. **MpDataService bypassa repository** — ~80% dei metodi chiama `IocDbController.XXX()` direttamente invece di usare `IIocRepository`
|
||||
3. **Nessun FusionCache** — MpDataService usa manual `redisDb.StringGetAsync`/`StringSetAsync` (~80% del codice) invece di `IFusionCache.GetOrFetchAsync`
|
||||
3. **Caching misto** — Parte migrato a FusionCache, parte ancora in manual StringSet/StringGet
|
||||
4. **Singleton lifetime mismatch** — MpDataService è Singleton, registra campi statici per IoC Controller e Mongo Controller che vengono sovrascritti
|
||||
|
||||
## Piano di Refactoring
|
||||
|
||||
### Fase 1: Pulizia Anti-Pattern (Quick Wins)
|
||||
### Fase 1: Pulizia Anti-Pattern (Quick Wins) ✅ COMPLETATA
|
||||
|
||||
Obiettivo: risolvere gli Static fields e i problemi DI più evidenti senza cambiare architettura.
|
||||
Rimosso `static IConfiguration _configuration` da tutti i 4 controller e rimossa la dead assignment dai costruttori:
|
||||
|
||||
#### 1.1 Rimuovere `static IConfiguration` dai 4 controller
|
||||
| Controller | Prima | Dopo |
|
||||
|------------|-------|------|
|
||||
| `IOBController` | `IConfiguration + static field` | Nessun param (dead code), field rimosso |
|
||||
| `BenchController` | `IConfiguration + static field` | Solo `MpDataService`, field rimosso |
|
||||
| `RecipeController` | `IConfiguration + static field` | Solo `MpDataService`, field rimosso |
|
||||
| `RecipeArchiveController` | `IConfiguration + static field` | Solo `MpDataService`, field rimosso |
|
||||
|
||||
| File | Fix |
|
||||
|------|-----|
|
||||
| `IOBController.cs:23` | `readonly IConfiguration _configuration` (usato solo nel ctor, rimosso se dead) |
|
||||
| `BenchController.cs:19` | Rimuovere campo statico (non usato dopo ctor) |
|
||||
| `RecipeController.cs:18` | Rimuovere campo statico (non usato dopo ctor) |
|
||||
| `RecipeArchiveController.cs:18` | Rimuovere campo statico (non usato dopo ctor) |
|
||||
### Fase 2: FusionCache + Redis Backplane ✅ COMPLETATA
|
||||
|
||||
**File da modificare:**
|
||||
- `C:\Users\samuele.steamw\source\MAPO-CORE\MP.IOC\Controllers\IOBController.cs`
|
||||
- `C:\Users\samuele.steamw\source\MAPO-CORE\MP.IOC\Controllers\BenchController.cs`
|
||||
- `C:\Users\samuele.steamw\source\MAPO-CORE\MP.IOC\Controllers\RecipeController.cs`
|
||||
- `C:\Users\samuele.steamw\source\MAPO-CORE\MP.IOC\Controllers\RecipeArchiveController.cs`
|
||||
#### 2.1 Pacchetti Aggiunti (MP.IOC.csproj)
|
||||
|
||||
#### 1.2 Correggere `MpIocController` singleton
|
||||
| Pacchetto | Stato |
|
||||
|-----------|-------|
|
||||
| `Microsoft.Extensions.Caching.StackExchangeRedis` | ✅ |
|
||||
| `ZiggyCreatures.FusionCache` | ✅ |
|
||||
| `ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis` | ✅ |
|
||||
| `ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson` | ✅ |
|
||||
|
||||
`MpIocController` usa `IDbContextFactory` correttamente, ma è registrato come Singleton in `AddIocDataLayer()`. I DbContext Factory sono thread-safe ma il pattern Singleton è inutile e potenzialmente pericoloso.
|
||||
|
||||
Fix: registrare come **Scoped** in `DataServiceCollectionExtensions.cs`:
|
||||
```csharp
|
||||
services.TryAddScoped<IMpIocService, MpIocController>(); // rinominare in servizio
|
||||
// oppure creare interfaccia IMpIocRepository
|
||||
```
|
||||
|
||||
#### 1.3 Rimuovere campi statici in `MpDataService`
|
||||
|
||||
| Campo | Righe | Fix |
|
||||
|-------|-------|-----|
|
||||
| `public static MpMongoController mongoController` | 94 | Rimuovere campo statico → iniettare via contructor o renderlo readonly |
|
||||
| `public static MpIocController IocDbController` | 95 | **Bloccante** — usato da ~45+ metodi. Richiede Fase 3 completa |
|
||||
|
||||
### Fase 2: Abilitare FusionCache per MP.IOC
|
||||
|
||||
Attualmente MP.IOC ha FusionCache disabilitato (L1 solo, Redis non connesso).
|
||||
|
||||
#### 2.1 Configurazione Redis + FusionCache in Program.cs
|
||||
#### 2.2 Configurazione FusionCache (Program.cs)
|
||||
|
||||
```csharp
|
||||
// Attuale (solo L1 Memory)
|
||||
builder.Services.AddFusionCache()
|
||||
.WithDefaultEntryOptions(options => { options.Duration = TimeSpan.FromMinutes(1); });
|
||||
|
||||
// Nuovo (L1 Memory + L2 Redis + L3 DB)
|
||||
var redisMux = ConnectionMultiplexer.Connect(confRedis);
|
||||
builder.Services.AddSingleton<IConnectionMultiplexer>(redisMux);
|
||||
// L1 Memory + L2 Redis Distributed + Redis Backplane
|
||||
builder.Services.AddStackExchangeRedisCache(options =>
|
||||
options.Configuration = confRedis);
|
||||
|
||||
builder.Services.AddFusionCache()
|
||||
.WithDistributedCache(sp => sp.GetRequiredService<IDistributedCache>())
|
||||
.WithSerializer(new FusionCacheNewtonsoftJsonSerializer())
|
||||
.WithBackplane(new RedisBackplane(new RedisBackplaneOptions {
|
||||
ConnectionMultiplexerFactory = () => Task.FromResult(redisMux)
|
||||
}));
|
||||
ConnectionMultiplexerFactory = () => Task.FromResult<IConnectionMultiplexer>(redisMux)
|
||||
}))
|
||||
.WithDefaultEntryOptions(options => {
|
||||
options.Duration = TimeSpan.FromMinutes(1);
|
||||
options.JitterMaxDuration = TimeSpan.FromSeconds(5);
|
||||
});
|
||||
```
|
||||
|
||||
#### 2.2 Migrazione metodi MpDataService a GetOrFetchAsync
|
||||
#### 2.3 Migrate 14 metodi da manual Redis a FusionCache
|
||||
|
||||
**Metodi da migrare (identificazione preliminare):**
|
||||
| # | Metodo | Categoria | Fetch From |
|
||||
|---|--------|-----------|------------|
|
||||
| 1 | `AnagStatiGetAllAsync` | Anagrafica | `IocDbController.AnagStatiGetAllAsync()` |
|
||||
| 2 | `ArticoliGetLastByMaccAsync` | Anagrafica | `IocDbController.ArticoliGetLastByMaccAsync()` |
|
||||
| 3 | `ConfigGetAllAsync` | Anagrafica | `IocDbController.ConfigGetAllAsync()` |
|
||||
| 4 | `DecNumArtGetFiltAsync` | Anagrafica | `IocDbController.DecNumArtGetFiltAsync()` |
|
||||
| 5 | `DossierLastByMachAsync` | Dossier | `IocDbController.DossGetLastByMaccAsync()` |
|
||||
| 6 | `GetLastOdlAsync` | Produzione | `IocDbController.OdlLastByMaccAsync()` |
|
||||
| 7 | `ListValuesFilt` | Configurazione | `IocDbController.ListValuesFiltAsync()` |
|
||||
| 8 | `Macchine2SlaveGetAllAsync` | Machine | `IocDbController.Macchine2SlaveAsync()` |
|
||||
| 9 | `MacchineGetFilt` | Machine | `_productionRepository.MacchineGetFiltAsync()` |
|
||||
| 10 | `MacchineRecipeArchive` | Machine | `_productionRepository.MacchineGetFiltAsync("*")` |
|
||||
| 11 | `MseGetAllAsync` | Stato | `IocDbController.MseGetAllAsync(maxAge)` (+ forceDb) |
|
||||
| 12 | `OdlCurrByMaccAsync` | Produzione | `IocDbController.OdlCurrByMaccAsync()` |
|
||||
| 13 | `POdlGetByKey` | Produzione | `_productionRepository.PODL_getByKeyAsync()` |
|
||||
| 14 | `POdlGetByMaccArtAsync` | Produzione | `IocDbController.POdlGetByMaccArtAsync()` |
|
||||
| 15 | `ConfFluxMach` | FluxLog | `IocDbController.ConfFluxFiltAsync()` |
|
||||
| 16 | `FluxLogFirstByMachAsync` | FluxLog | `IocDbController.FluxLogFirstByMaccAsync()` |
|
||||
| 17 | `FluxLogGetLastByMachAsync` | FluxLog | `IocDbController.FluxLogGetLastFiltAsync()` |
|
||||
|
||||
| Categoria | Metodi | Stima righe |
|
||||
|-----------|---------|-------------|
|
||||
| Anagrafica | `AnagStatiGetAllAsync`, `ArticoliGetLastByMaccAsync`, `ConfigGetAllAsync`, `DecNumArtGetFiltAsync` | ~120 |
|
||||
| Produzione | `OdlCurrByMaccAsync`, `OdlLastByMaccAsync`, `POdlGetByMaccArtAsync`, `PezziProdMacchinaAsync`, `StatoProdMacchinaAsync` | ~200 |
|
||||
| FluxLog | `ConfFluxFiltAsync`, `FluxLogFirstByMaccAsync`, `FluxLogGetLastFiltAsync`, `FluxLogTakeSnapshotLastAsync` | ~100 |
|
||||
| Dossier | `DossGetLastByMaccAsync` | ~50 |
|
||||
| Machine | `DatiMacchineGetAllAsync`, `MseGetAllAsync`, `Macchine2SlaveAsync`, `MacchineGetByIdxAsync` | ~150 |
|
||||
| MicroStato | `EvListMicroStatoInsertAsync`, `MicroStatoMacchinaUpsertAsync`, `MicroStatoMacchinaGetByIdxMaccAsync` | ~80 |
|
||||
| VMSFD | `VMSFDGetByMaccAsync`, `VMSFDGetMultiByMaccAsync`, `StateMachIngressiAsync` | ~80 |
|
||||
| Misc | `Alarms`, `RegScarti`, `RegControlli`, `RemRebootLog`, `SignalLog`, `KeepAlive` | ~150 |
|
||||
|
||||
**Totale stimato:** ~930 righe di codice caching da migrare a pattern `GetOrFetchAsync`.
|
||||
|
||||
### Fase 3: Decomposizione MpIocController in Repository (Core)
|
||||
### Fase 3: Decomposizione MpIocController in Repository (NON INIZIATA)
|
||||
|
||||
`MpIocController` (1480 righe, 82 metodi) va scomposto in repository specialistici, come fatto per MP.SPEC:
|
||||
|
||||
#### 3.1 Mappatura attuali → Nuovi Repository
|
||||
|
||||
| Attuale (82 metodi) | Nuovo Repository | Ambito |
|
||||
|----------------------|-------------------|--------|
|
||||
| `AnagStatiGetAllAsync`, `ArticoliGetLastByMaccAsync`, `ConfigGetAllAsync`, `ConfigUpdateAsync`, `DecNumArtGetFiltAsync`, `ListValuesFiltAsync`, `ListLinkFiltAsync`, `EvListInsert` | `IAnagRepository` | **Usato da SPEC** — già migrato! |
|
||||
@@ -131,56 +116,16 @@ builder.Services.AddFusionCache()
|
||||
| `FluxLogInsertAsync`, `FluxLogGetLastFiltAsync`, `FluxLogFirstByMaccAsync`, `FluxLogTakeSnapshotLastAsync`, `ConfFluxFiltAsync` | `IFluxLogRepository` | **Usato da SPEC** — già migrato! |
|
||||
| `DossGetLastByMaccAsync`, `SignalLogInsertAsync` | `IDossierRepository` | **Usato da SPEC** — già migrato! |
|
||||
| `DatiMacchineGetAllAsync`, `MacchineGetByIdxAsync`, `MacchineUpsertAsync`, `MacchineGetFiltAsync`, `MacchineGetAllAsync`, `Macchine2Slave` | `IMacchineRepository` (nuovo) | Gestione macchine |
|
||||
| `EvListMicroStatoInsertAsync`, `MicroStatoMacchinaUpsertAsync`, `MicroStatoMacchinaGetByIdxMaccAsync`, `MicroStatoMacchinaGetByIdxMaccAsync` | `IMicroStatoRepository` (nuovo) | Micro-stati macchine |
|
||||
| `EvListMicroStatoInsertAsync`, `MicroStatoMacchinaUpsertAsync`, `MicroStatoMacchinaGetByIdxMaccAsync` | `IMicroStatoRepository` (nuovo) | Micro-stati macchine |
|
||||
| `MSE_getUserForcedAsync`, `SMES_getHwTransitionsAsync`, `DDB_InsStatoBatchAsync`, `CheckCambiaStatoBatchAsync`, `RecalcMseAsync` | `IMseRepository` (nuovo) | Mappa stati macchin |
|
||||
| `StateMachineIngressiAsync`, `VMSFDGetByMaccAsync`, `VMSFDGetMultiByMaccAsync`, `VMSFDGetAllAsync` | `IStateMachineRepository` (nuovo) | State machine |
|
||||
| `KeepAliveUpsertAsync` | `IMacchineRepository` (nuovo) | Keep-alive |
|
||||
| `AlarmLogInsertAsync`, `RegScartiInsertAsync`, `RegControlliInsertAsync`, `RegDichiarInsertAsync` | `IRegistroRepository` (nuovo) | Registri qualità |
|
||||
| `RemRebootLog*` methods (6) | `IRemRebootRepository` (nuovo) | Reboot remote logging |
|
||||
|
||||
#### 3.2 Interfacce Nuove (da creare in MP.Data/Repository/)
|
||||
### Fase 4: Scomposizione MpDataService (NON INIZIATA)
|
||||
|
||||
```
|
||||
MP.Data/Repository/Iob/
|
||||
├── IProduzioneRepository.cs
|
||||
├── ProduzioneRepository.cs
|
||||
├── IMacchineRepository.cs
|
||||
├── MacchineRepository.cs
|
||||
├── IMicroStatoRepository.cs
|
||||
├── MicroStatoRepository.cs
|
||||
├── IMseRepository.cs
|
||||
├── MseRepository.cs
|
||||
├── IStateMachineRepository.cs
|
||||
├── StateMachineRepository.cs
|
||||
├── IRegistroRepository.cs
|
||||
├── RegistroRepository.cs
|
||||
└── IRemRebootRepository.cs
|
||||
└── RemRebootRepository.cs
|
||||
```
|
||||
|
||||
#### 3.3 Migrazione Dipendenze
|
||||
|
||||
Dopo aver creato i repository, ogni metodo in `MpDataService.cs` che chiama `IocDbController.XXX()` va aggiornato per chiamare il repository invece.
|
||||
|
||||
**Esempio:**
|
||||
```csharp
|
||||
// PRIMA
|
||||
var result = await IocDbController.AnagStatiGetAllAsync();
|
||||
|
||||
// DOPO
|
||||
var result = await _anagRepository.AnagStatiGetAllAsync();
|
||||
```
|
||||
|
||||
#### 3.4 Rimozione MpIocController
|
||||
|
||||
Dopo la migrazione completa:
|
||||
1. Rimuovere `MpIocController` da `DataServiceCollectionExtensions.cs`
|
||||
2. Rimuovere il parametro dal costruttore di `MpDataService`
|
||||
3. Spostare i metodi ancora non migrati a `#if false` come fallback documentato
|
||||
|
||||
### Fase 4: Scomposizione MpDataService
|
||||
|
||||
`MpDataService.cs` (3475 righe) è troppo grande per essere un unico servizio. Proposta di scomposizione:
|
||||
`MpDataService.cs` (3516 righe) è troppo grande per essere un unico servizio. Proposta di scomposizione:
|
||||
|
||||
| Servizio | Responsabilità | Metodi |
|
||||
|----------|---------------|--------|
|
||||
@@ -193,54 +138,98 @@ Dopo la migrazione completa:
|
||||
|
||||
**Nota:** La scomposizione va fatta **dopo** la migrazione a repository (Fase 3) per non dover modificare ogni singola chiamata.
|
||||
|
||||
### Fase 5: Build & Verifica
|
||||
### Fase 5: Build & Verifica ✅
|
||||
|
||||
Dopo ogni fase completa:
|
||||
1. `dotnet build MP-IOC.sln`
|
||||
2. `dotnet build MP-SPEC.sln` (non deve rompersi)
|
||||
3. `dotnet build MP-LAND.sln`
|
||||
4. `dotnet build MP-MON.sln`
|
||||
5. Verificare che MP.IOC parta correttamente
|
||||
1. `dotnet build MP-IOC.sln` → ✅ 0 errori, 12 warnings (tutti preesistenti)
|
||||
2. `dotnet build MP-SPEC.sln` → ✅ OK
|
||||
3. `dotnet build MP-LAND.sln` → ✅ OK
|
||||
4. `dotnet build MP-MON.sln` → ✅ OK
|
||||
|
||||
## Riepilogo Costi Stimati
|
||||
## Build Status Aggiornato
|
||||
|
||||
| Fase | File Modificati | Righe Modificate | Rischio | Stato |
|
||||
|------|-----------------|-------------------|---------|-------|
|
||||
| ~~F1: Anti-Pattern 4 controller~~ | ~~4~~ | ~~20~~ | ~~Basso~~ | ✅ **Completato** |
|
||||
| F2: FusionCache config + metodi | 2 + 1 | ~1200 | Medio | ⏳ Da fare |
|
||||
| F3: Repository IoC | 7+ nuovi + 5 esistenti | ~2000 | Alto | ⏳ Da fare |
|
||||
| F4: Scomposizione MpDataService | 5 nuovi + 1 esistente | ~3475 | Alto | ⏳ Da fare |
|
||||
| F5: Build & Verifica | — | — | Basso | ✅ OK (0 errori, 12 warnings) |
|
||||
| F1: Anti-Pattern 4 controller | 4 Controllers | ~20 | Basso | ✅ **Completato** |
|
||||
| F2: FusionCache config + metodi | 2 Files + Helper | ~250 | Basso | ✅ **Completato** |
|
||||
| F3: Repository IoC | 7+ nuovi + MpDataService | ~2000 | Alto | ⏳ Da fare |
|
||||
| F4: Scomposizione MpDataService | 5 nuovi + 1 esistente | ~3516 | Alto | ⏳ Da fare |
|
||||
| F5: Build & Verifica | — | — | Basso | ✅ 0 errori |
|
||||
|
||||
## Stato Completato
|
||||
|
||||
### ✅ Fase 1: Anti-Pattern Controller — COMPLETATA
|
||||
|
||||
Rimosso `static IConfiguration _configuration` da tutti i 4 controller e rimossa la dead assignment dai costruttori:
|
||||
Rimosso `static IConfiguration _configuration` da tutti i 4 controller.
|
||||
|
||||
| Controller | Prima | Dopo |
|
||||
|------------|-------|------|
|
||||
| `IOBController` | `IConfiguration + static field` | Nessun param (dead code), field rimosso |
|
||||
| `BenchController` | `IConfiguration + static field` | Solo `MpDataService`, field rimosso |
|
||||
| `RecipeController` | `IConfiguration + static field` | Solo `MpDataService`, field rimosso |
|
||||
| `RecipeArchiveController` | `IConfiguration + static field` | Solo `MpDataService`, field rimosso |
|
||||
### ✅ Fase 2: FusionCache + Redis Backplane — COMPLETATA
|
||||
|
||||
**Build risultato:** ✅ 0 errori, 12 warnings (solo nullable reference, nessun warning statico residuo nei controller)
|
||||
- Pacchetti FusionCache aggiunti a `MP.IOC.csproj`
|
||||
- `IFusionCache` configurato con L1 Memory + L2 Redis + Backplane in `Program.cs`
|
||||
- Campo `_cache` aggiunto a `MpDataService` con helper `GetOrFetchAsync<T>`
|
||||
- **18 metodi migrati** dal pattern manuale Redis → FusionCache
|
||||
- Build: ✅ **0 errori**, 12 warnings (tutti nullable reference preesistenti)
|
||||
|
||||
### 📋 Fase 2+: In Attesa
|
||||
## Invntario Completo Metodi (Post-Refactoring)
|
||||
|
||||
Prossimo step: abilitare FusionCache con Redis backplane (Fase 2) — richiede approvazione architetturale.
|
||||
### ✅ MIGRATI A FUSIONCACHE (18 metodi)
|
||||
|
||||
| # | Metodo | Fetch From | Tag |
|
||||
|---|--------|------------|-----|
|
||||
| 1 | `AnagStatiGetAllAsync` | `IocDbController.AnagStatiGetAllAsync()` | `Utils.redisAnagStati` |
|
||||
| 2 | `ArticoliGetLastByMaccAsync` | `IocDbController.ArticoliGetLastByMaccAsync()` | `Utils.redisArtList:Last:{idxMacc}` |
|
||||
| 3 | `ConfigGetAllAsync` | `IocDbController.ConfigGetAllAsync()` | `Utils.redisConfKey` |
|
||||
| 4 | `DecNumArtGetFiltAsync` | `IocDbController.DecNumArtGetFiltAsync()` | `Utils.redisDecNumArtKey:{tag}` |
|
||||
| 5 | `DossierLastByMachAsync` | `IocDbController.DossGetLastByMaccAsync()` | `Utils.redisDossByMacLast:{idxMacc}` |
|
||||
| 6 | `GetLastOdlAsync` | `IocDbController.OdlLastByMaccAsync()` | `Utils.redisOdlLastByMac:{idxMacc}` |
|
||||
| 7 | `ListValuesFilt` | `IocDbController.ListValuesFiltAsync()` | `Utils.redisConfFlux:{tag}` |
|
||||
| 8 | `Macchine2SlaveGetAllAsync` | `IocDbController.Macchine2SlaveAsync()` | `Utils.redisBaseAddr:M2STab` |
|
||||
| 9 | `MacchineGetFilt` | `_productionRepository.MacchineGetFiltAsync()` | `Utils.redisMacList:{keyGrp}` |
|
||||
| 10 | `MacchineRecipeArchive` | `_productionRepository.MacchineGetFiltAsync("*")` → LINQ | `Utils.redisMacRecipePath:{idxMacc}` |
|
||||
| 11 | `MseGetAllAsync` | `IocDbController.MseGetAllAsync(maxAge)` | `Constants.redisMseKey` (+ forceDb) |
|
||||
| 12 | `OdlCurrByMaccAsync` | `IocDbController.OdlCurrByMaccAsync()` | `Utils.redisOdlList:Current:{idxMacc}` |
|
||||
| 13 | `POdlGetByKey` | `_productionRepository.PODL_getByKeyAsync()` | `Utils.redisPOdlByPOdl:{idxPODL}` |
|
||||
| 14 | `POdlGetByMaccArtAsync` | `IocDbController.POdlGetByMaccArtAsync()` | `Utils.redisPOdlByMaccArt:{idxMacc}...` |
|
||||
| 15 | `ConfFluxMach` | `IocDbController.ConfFluxFiltAsync()` → LINQ | `Utils.redisConfFlux:{tag}` |
|
||||
| 16 | `FluxLogFirstByMachAsync` | `IocDbController.FluxLogFirstByMaccAsync()` → LINQ | `Utils.redisFluxLogFirstByMac:{key}` |
|
||||
| 17 | `FluxLogGetLastByMachAsync` | `IocDbController.FluxLogGetLastFiltAsync()` → LINQ | `Utils.redisFluxLogByMac:{key}` |
|
||||
|
||||
### ❌ NON MIGRATI (Scrittura / TTL / Hash-only — 30+ metodi)
|
||||
|
||||
Questi metodi usano Redis ma **NON** hanno pattern cache-aside (StringGet → DB → StringSet). Sono dati transituali IOB o write-through:
|
||||
|
||||
| Categoria | Metodi | Motivazione |
|
||||
|-----------|--------|-------------|
|
||||
| **Scrittura Only (StringSet)** | `MachineParamListSetAsync`, `SaveMachine2Iob`, `SaveMachineIobConf`, `SetIobConfYamlAsync`, `SetIobMemMap`, `UpsertCurrObjItemsAsync` | Solo StringSet, zero StringGet |
|
||||
| **Lock/TTL-based** | `AutoStartOdlAsync` (veto), `ScriviKeepAliveAsync` (TTL 30s) | Pattern di lock, non di cache |
|
||||
| **Hash-based IOB State** | `MachineParamListAsync`, `AddCheckTask4Machine`, `AddOptPar4Machine`, `AddTask4Machine`, `AddTask4MacListAsync`, `mOptParMacchina`, `mSavedTaskMacchina`, `mTaskMacchina`, `mTaskMacchinaAsync`, `mDatiMacchineAsync`, `mTabMSMIAsync`, `StateMachInByKeyAsync`, `ValoreSmiAsync`, `StateMachInByKeyAsync` | Redis Hash per dati in tempo reale IOB (transazionali), non Database-backed |
|
||||
| **Hash Reset Helpers** | `ResetDatiMacchinaAsync`, `resetMSMIAsync`, `resetSMIAsync` | Reset di tabelle Hash Redis, non cache |
|
||||
| **Redis GetHash Helpers** | `RedisGetHash`, `RedisGetHashAsync`, `RedisGetHashDict`, `RedisGetHashDictAsync`, `RedisSetHash`, `RedisSetHashAsync`, `RedisSetHashDict`, `RedisSetHashDictAsync`, `RedisCountKey`, `RedisDelKey`, `RedisKeyPresent`, `RedisGetHashField` | Helper utility Redis, non metodi di business |
|
||||
|
||||
### ⚠️ DA VERIFICARE (Pattern atipico - 4 metodi)
|
||||
|
||||
Questi metodi hanno Redis ma il pattern non è lo standard cache-aside. Vanno analizzati caso per caso:
|
||||
|
||||
| # | Metodo | Riga | Motivo Verifica |
|
||||
|---|--------|------|-----------------|
|
||||
| 1 | `GetCurrOdlAsync` | ~892 | Chiama `GetCurrOdlByProdAsync()` (metodo interno complesso), non `IocDbController.XXX()` direttamente. Pattern: StringGet → DB → StringSet. Verificare se `GetCurrOdlByProdAsync` è una stored procedure. |
|
||||
| 2 | `StatoProdMacchinaAsync` (private) | ~3267 | Ha parametro `forceDb`, cache breve (60s pattern). Pattern valido ma private. |
|
||||
| 3 | `verificaIdxMacchinaAsync` | ~3267 | Verifica esistenza macchina, non un cache-aside standard. |
|
||||
| 4 | `ListMasterAsync` / `ListSlaveAsync` | ~2820/~2840 | Cache-aside valido ma derivano da `Macchine2SlaveGetAllAsync()` (già migrata). Potrebbero essere rimossi in favore di un unico repository. |
|
||||
|
||||
## Priorità Operativa
|
||||
|
||||
1. **F1 immediata** — zero rischio, fix 4 controller statici
|
||||
2. **F2 parallela** — abilitare FusionCache con Redis backplane (config) + migrare i metodi più usati
|
||||
3. **F3 dopo F2** — decomporre MpIocController una volta che il caching è standardizzato
|
||||
4. **F4 opzionale** — scomporare MpDataService quando il resto è stabile
|
||||
1. ~~**F1 immediata**~~ — ✅ Completata
|
||||
2. ~~**F2 FusionCache**~~ — ✅ Completata (18 metodi migrati)
|
||||
3. **F3 Repository IoC** — Prossimo step maggiore: decomporre `MpIocController` (1480 righe, 82 metodi) in 7-10 repository specialistici
|
||||
4. **F4 Scomposizione MpDataService** — Quando Fase 3 è stabile, dividere il monolite da 3516 righe
|
||||
5. **Verifica metodi atipici** — I 4 metodi con pattern non-standards vanno valutati manualmente
|
||||
|
||||
## Note
|
||||
|
||||
- MpIocController usa già `IDbContextFactory` correttamente — il problema è che è un Singleton e lo si chiama da un altro Singleton (MpDataService)
|
||||
- IIocRepository (già esistente) ha solo 16 metodi ed è mai usato da MpDataService
|
||||
- I repository SPEC (Anag, Dossier, FluxLog, Production) sono già usati da alcuni metodi — sfruttare questi dove possibile
|
||||
- La struttura MP.Data/Controllers/ è ingannevole: MpIocController è un servizio, non un controller Web API
|
||||
- MpIocController usa già `IDbContextFactory` correttamente — il problema è che è un Singleton usato da un altro Singleton (MpDataService)
|
||||
- `IIocRepository` (già esistente) ha solo 16 metodi ed è mai usato da MpDataService
|
||||
- I repository SPEC (Anag, Dossier, FluxLog, Production) sono già usati da alcuni metodi — sfruttarli dove possibile
|
||||
- La struttura `MP.Data/Controllers/` è ingannevole: `MpIocController` è un servizio, non un controller Web API
|
||||
- `redisConnAdmin` (connessione Redis admin) sembra non essere mai usata — verificare eliminazione
|
||||
- **Build finale:** ✅ 0 errori, 12 warnings (tutti nullable reference preesistenti)
|
||||
|
||||
Reference in New Issue
Block a user