Review cache con FusionCache
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.36" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.36" />
|
||||
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.36" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.7" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.25" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.1" />
|
||||
<PackageVersion Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="6.0.17" />
|
||||
<PackageVersion Include="MongoDB.Driver" Version="2.19.0" />
|
||||
|
||||
@@ -342,9 +342,9 @@ namespace MP.Data.Controllers
|
||||
/// Elenco valori ammessi x Stati commessa (es Yacht Baglietto)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<ListValuesModel> AnagStatiComm()
|
||||
public Task<List<ListValuesModel>> AnagStatiCommAsync()
|
||||
{
|
||||
return ListValuesFilt("PODL", "StatoComm");
|
||||
return ListValuesFiltAsync("PODL", "StatoComm");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1522,6 +1522,27 @@ namespace MP.Data.Controllers
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Elenco valori ammessi x tabella/colonna Async
|
||||
/// </summary>
|
||||
/// <param name="tabName"></param>
|
||||
/// <param name="fieldName"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<List<ListValuesModel>> ListValuesFiltAsync(string tabName, string fieldName)
|
||||
{
|
||||
List<ListValuesModel> dbResult = new List<ListValuesModel>();
|
||||
using (var dbCtx = new MoonProContext(options))
|
||||
{
|
||||
dbResult = await dbCtx
|
||||
.DbSetListValues
|
||||
.Where(x => x.TableName == tabName && x.FieldName == fieldName)
|
||||
.AsNoTracking()
|
||||
.OrderBy(x => x.ordinal)
|
||||
.ToListAsync();
|
||||
}
|
||||
return dbResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Elenco Macchine dato operatore secondo gruppi (macchine/operatore)
|
||||
/// </summary>
|
||||
|
||||
+342
-90
@@ -1,6 +1,5 @@
|
||||
using EgwCoreLib.Utils;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using MP.Core.Conf;
|
||||
using MP.Core.DTO;
|
||||
using MP.Core.Objects;
|
||||
@@ -14,6 +13,7 @@ using NLog;
|
||||
using StackExchange.Redis;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
|
||||
namespace MP.SPEC.Data
|
||||
{
|
||||
@@ -21,11 +21,11 @@ namespace MP.SPEC.Data
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
public MpDataService(IConfiguration configuration, IMemoryCache memoryCache)
|
||||
public MpDataService(IConfiguration configuration, IFusionCache cache)
|
||||
{
|
||||
// salvataggio oggetti
|
||||
_configuration = configuration;
|
||||
_memoryCache = memoryCache;
|
||||
_cache = cache;
|
||||
// Verifica conf trace...
|
||||
traceEnabled = _configuration.GetValue<bool>("Otel:EnableTracing", false);
|
||||
Log.Info($"MpDataService | INIT | Trace enabled: {traceEnabled}");
|
||||
@@ -300,15 +300,16 @@ namespace MP.SPEC.Data
|
||||
LogTrace($"AnagKeyValGetAll Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<List<ListValuesModel>> AnagStatiComm()
|
||||
{
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "AnagStatiComm",
|
||||
memKey: "ANAG_STATI_COMM_MEM",
|
||||
redisKey: Utils.redisStatoCom,
|
||||
memoryTtl: TimeSpan.FromMinutes(5),
|
||||
dbFactory: () => Task.FromResult(dbController.AnagStatiComm() ?? new List<ListValuesModel>())
|
||||
);
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "AnagStatiCommAsync",
|
||||
cacheKey: Utils.redisStatoCom,
|
||||
expiration: TimeSpan.FromMinutes(5),
|
||||
fetchFunc: async () =>
|
||||
await dbController.AnagStatiCommAsync() ?? new List<ListValuesModel>()
|
||||
);
|
||||
}
|
||||
|
||||
#if false
|
||||
@@ -345,7 +346,7 @@ namespace MP.SPEC.Data
|
||||
|
||||
public async Task<List<ListValuesModel>> AnagTipoArtLV()
|
||||
{
|
||||
using var activity = ActivitySource.StartActivity("AnagStatiComm");
|
||||
using var activity = ActivitySource.StartActivity("AnagStatiCommAsync");
|
||||
string source = "DB";
|
||||
List<ListValuesModel>? result = new List<ListValuesModel>();
|
||||
// cerco in redis...
|
||||
@@ -438,16 +439,28 @@ namespace MP.SPEC.Data
|
||||
string redisKey = $"{Utils.redisArtList}:{azienda}:Tipo:{sKey}";
|
||||
string memKey = $"MEM:{redisKey}";
|
||||
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "ArticoliGetByTipoAsync",
|
||||
memKey: memKey,
|
||||
redisKey: redisKey,
|
||||
// ✅ TTL lungo (dato abbastanza statico)
|
||||
memoryTtl: TimeSpan.FromMinutes(5),
|
||||
dbFactory: async () =>
|
||||
await dbController.ArticoliGetByTipoAsync(tipo, azienda)
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "ArticoliGetByTipoAsync",
|
||||
cacheKey: redisKey,
|
||||
expiration: TimeSpan.FromMinutes(2),
|
||||
fetchFunc: async () =>
|
||||
await dbController.ArticoliGetByTipoAsync(tipo, azienda)
|
||||
?? new List<AnagArticoliModel>()
|
||||
);
|
||||
);
|
||||
|
||||
|
||||
#if false
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "ArticoliGetByTipoAsync",
|
||||
memKey: memKey,
|
||||
redisKey: redisKey,
|
||||
// ✅ TTL lungo (dato abbastanza statico)
|
||||
memoryTtl: TimeSpan.FromMinutes(5),
|
||||
dbFactory: async () =>
|
||||
await dbController.ArticoliGetByTipoAsync(tipo, azienda)
|
||||
?? new List<AnagArticoliModel>()
|
||||
);
|
||||
#endif
|
||||
}
|
||||
#if false
|
||||
public async Task<List<AnagArticoliModel>> ArticoliGetByTipoAsync(string tipo, string azienda = "*")
|
||||
@@ -496,18 +509,161 @@ namespace MP.SPEC.Data
|
||||
string memKey = $"ART_SEARCH_MEM:{azienda}:{sKey}:{numRecord}";
|
||||
string redisKey = $"{Utils.redisArtList}:{azienda}:{sKey}:{numRecord}";
|
||||
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "ArticoliGetSearchAsync",
|
||||
memKey: memKey,
|
||||
redisKey: redisKey,
|
||||
memoryTtl: TimeSpan.FromMinutes(2),
|
||||
dbFactory: async () =>
|
||||
await dbController.ArticoliGetSearchAsync(numRecord, azienda, searchVal)
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "ArticoliGetSearchAsync",
|
||||
cacheKey: redisKey,
|
||||
expiration: TimeSpan.FromMinutes(2),
|
||||
fetchFunc: async () =>
|
||||
await dbController.ArticoliGetSearchAsync(numRecord, azienda, searchVal)
|
||||
?? new List<AnagArticoliModel>()
|
||||
);
|
||||
);
|
||||
|
||||
#if false
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "ArticoliGetSearchAsync",
|
||||
memKey: memKey,
|
||||
redisKey: redisKey,
|
||||
memoryTtl: TimeSpan.FromMinutes(2),
|
||||
dbFactory: async () =>
|
||||
await dbController.ArticoliGetSearchAsync(numRecord, azienda, searchVal)
|
||||
?? new List<AnagArticoliModel>()
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementa gestione recupero cache da memoria o da obj esterno + cache memoria + tracking attività
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="cacheKey"></param>
|
||||
/// <param name="fetchFunc"></param>
|
||||
/// <param name="expiration"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<T> GetOrFetchAsync<T>(string operationName, string cacheKey, Func<Task<T>> fetchFunc, TimeSpan expiration, params string[] tags)
|
||||
{
|
||||
using var activity = ActivitySource.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);
|
||||
LogTrace($"{operationName} | {source} | {activity?.Duration.TotalMilliseconds:F4} ms");
|
||||
return result;
|
||||
}
|
||||
bool fromDb = false;
|
||||
var final = await _cache.GetOrSetAsync<T>(
|
||||
cacheKey,
|
||||
async _ =>
|
||||
{
|
||||
fromDb = true;
|
||||
return await fetchFunc();
|
||||
},
|
||||
opt =>
|
||||
{
|
||||
opt.SetDuration(expiration)
|
||||
.SetFailSafe(true);
|
||||
|
||||
//if (tags != null && tags.Length > 0)
|
||||
// opt.SetTags(tags);
|
||||
|
||||
});
|
||||
|
||||
source = fromDb ? "DB" : "REDIS";
|
||||
activity?.SetTag("data.source", source);
|
||||
LogTrace($"{operationName} | {source} | {activity?.Duration.TotalMilliseconds:F4} ms");
|
||||
return final!;
|
||||
}
|
||||
|
||||
#if false
|
||||
private async Task<T> GetOrFetchAsync<T>(string operationName, string cacheKey, Func<Task<T>> fetchFunc, TimeSpan expiration)
|
||||
{
|
||||
using var activity = ActivitySource.StartActivity(operationName);
|
||||
|
||||
string source;
|
||||
T result;
|
||||
|
||||
// ✅ 1. Tenta MEMORY / DISTRIBUTED (senza factory)
|
||||
var memTry = await _cache.TryGetAsync<T>(cacheKey);
|
||||
|
||||
if (memTry.HasValue)
|
||||
{
|
||||
result = memTry.Value!;
|
||||
source = "MEMORY";
|
||||
}
|
||||
else
|
||||
{
|
||||
bool fromDb = false;
|
||||
|
||||
// ✅ 2. fallback con factory
|
||||
result = await _cache.GetOrSetAsync<T>(
|
||||
cacheKey,
|
||||
async _ =>
|
||||
{
|
||||
fromDb = true;
|
||||
return await fetchFunc();
|
||||
},
|
||||
options => options.SetDuration(expiration)
|
||||
)!;
|
||||
|
||||
source = fromDb ? "DB" : "REDIS";
|
||||
}
|
||||
|
||||
// ✅ tracing e log
|
||||
activity?.SetTag("data.source", source);
|
||||
|
||||
if (result is System.Collections.ICollection coll)
|
||||
activity?.SetTag("result.count", coll.Count);
|
||||
else
|
||||
activity?.SetTag("result.count", result != null ? 1 : 0);
|
||||
|
||||
LogTrace($"{operationName} | {source} | {activity?.Duration.TotalMilliseconds:F4} ms");
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Cancellazione FusionCache (totale)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> FlushCacheAsync()
|
||||
{
|
||||
bool fatto = false;
|
||||
await _cache.ClearAsync();
|
||||
fatto = true;
|
||||
return fatto;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cancellazione FusionCache dato elenco tags
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> FlushCacheByTagsAsync(List<string> listTags)
|
||||
{
|
||||
bool fatto = false;
|
||||
foreach (var item in listTags)
|
||||
{
|
||||
await _cache.RemoveByTagAsync(item);
|
||||
}
|
||||
fatto = true;
|
||||
return fatto;
|
||||
}
|
||||
/// <summary>
|
||||
/// Cancellazione FusionCache dato singolo tag
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> FlushCacheByTagAsync(string tag)
|
||||
{
|
||||
bool fatto = false;
|
||||
await _cache.RemoveByTagAsync(tag);
|
||||
fatto = true;
|
||||
return fatto;
|
||||
}
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Helper per gestione cache a 3 livelli: MEMORY, REDIS e DB con opzioni
|
||||
/// </summary>
|
||||
@@ -527,12 +683,20 @@ namespace MP.SPEC.Data
|
||||
string source = "NA";
|
||||
T result;
|
||||
|
||||
string groupKey = memKey.Split(':')[0]; // es: "MACCHINE_MEM"
|
||||
|
||||
_memoryKeys.AddOrUpdate(
|
||||
groupKey,
|
||||
_ => new HashSet<string> { memKey },
|
||||
(_, set) => { set.Add(memKey); return set; }
|
||||
);
|
||||
|
||||
// ✅ default serializer (fallback)
|
||||
serialize ??= (obj) => JsonConvert.SerializeObject(obj);
|
||||
deserialize ??= (str) => JsonConvert.DeserializeObject<T>(str)!;
|
||||
|
||||
// ✅ 1. MEMORY
|
||||
if (_memoryCache.TryGetValue(memKey, out T cached))
|
||||
if (_cache.TryGetValue(memKey, out T cached))
|
||||
{
|
||||
result = cached;
|
||||
source = "MEMORY";
|
||||
@@ -540,24 +704,17 @@ namespace MP.SPEC.Data
|
||||
else
|
||||
{
|
||||
// ✅ 2. MISS → factory
|
||||
result = await _memoryCache.GetOrCreateAsync(memKey, async entry =>
|
||||
result = await _cache.GetOrCreateAsync(memKey, async entry =>
|
||||
{
|
||||
entry.AbsoluteExpirationRelativeToNow = memoryTtl;
|
||||
|
||||
// 👉 REDIS
|
||||
try
|
||||
{
|
||||
var rawData = await redisDb.StringGetAsync(redisKey);
|
||||
var rawData = await redisDb.StringGetAsync(redisKey);
|
||||
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
source = "REDIS";
|
||||
return deserialize(rawData!);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
LogTrace($"Redis error on {operationName}: {ex.Message}");
|
||||
source = "REDIS";
|
||||
return deserialize(rawData!);
|
||||
}
|
||||
|
||||
// 👉 DB
|
||||
@@ -567,18 +724,11 @@ namespace MP.SPEC.Data
|
||||
|
||||
var safeResult = dbResult == null ? default! : dbResult;
|
||||
|
||||
try
|
||||
{
|
||||
await redisDb.StringSetAsync(
|
||||
redisKey,
|
||||
serialize(safeResult),
|
||||
getRandTOut(redisLongTimeCache)
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogTrace($"Redis SET error on {operationName}: {ex.Message}");
|
||||
}
|
||||
await redisDb.StringSetAsync(
|
||||
redisKey,
|
||||
serialize(safeResult),
|
||||
getRandTOut(redisLongTimeCache)
|
||||
);
|
||||
|
||||
return safeResult;
|
||||
})!;
|
||||
@@ -595,8 +745,64 @@ namespace MP.SPEC.Data
|
||||
LogTrace($"{operationName} | {source} | {activity?.Duration.TotalMilliseconds}ms");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalidazione di una chiave in memoria e Redis
|
||||
/// </summary>
|
||||
/// <param name="memKey"></param>
|
||||
/// <param name="redisKey"></param>
|
||||
/// <returns></returns>
|
||||
public async Task InvalidateCacheAsync(string memKey, string redisKey)
|
||||
{
|
||||
// ✅ memoria
|
||||
_cache.Remove(memKey);
|
||||
|
||||
// ✅ redis
|
||||
await redisDb.KeyDeleteAsync(redisKey);
|
||||
|
||||
LogTrace($"Cache invalidated | {memKey}");
|
||||
}
|
||||
private readonly ConcurrentDictionary<string, HashSet<string>> _memoryKeys = new();
|
||||
/// <summary>
|
||||
/// Invalidazione cache da pattern
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
/// <returns></returns>
|
||||
public async Task InvalidateCacheByPatternAsync(string pattern)
|
||||
{
|
||||
// ✅ MEMORY (pattern match semplice)
|
||||
var keysToRemove = _memoryKeys.Keys
|
||||
.Where(k => k.Contains(pattern.Replace("*", "")))
|
||||
.ToList();
|
||||
|
||||
foreach (var key in keysToRemove)
|
||||
{
|
||||
if (_memoryKeys.TryRemove(key, out var subKeys))
|
||||
{
|
||||
foreach (var k in subKeys)
|
||||
{
|
||||
_cache.Remove(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ REDIS
|
||||
var masterEndpoint = redisConn.GetEndPoints()
|
||||
.Where(ep => redisConn.GetServer(ep).IsConnected && !redisConn.GetServer(ep).IsReplica)
|
||||
.FirstOrDefault();
|
||||
var server = redisConn.GetServer(masterEndpoint);
|
||||
|
||||
var redisKeys = server.Keys(pattern: pattern);
|
||||
|
||||
foreach (var rk in redisKeys)
|
||||
{
|
||||
await redisDb.KeyDeleteAsync(rk);
|
||||
}
|
||||
|
||||
LogTrace($"Cache invalidated by pattern | {pattern}");
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Aggiornamento record selezionato
|
||||
@@ -1798,7 +2004,7 @@ namespace MP.SPEC.Data
|
||||
LogTrace($"MacchineGetFilt | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
|
||||
return result;
|
||||
}
|
||||
private readonly IMemoryCache _memoryCache;
|
||||
private readonly IFusionCache _cache;
|
||||
/// <summary>
|
||||
/// Elenco di tutte le macchine filtrate x gruppo
|
||||
/// </summary>
|
||||
@@ -1811,16 +2017,27 @@ namespace MP.SPEC.Data
|
||||
string redisKey = $"{Utils.redisMacList}:{keyGrp}";
|
||||
string memKey = $"MACCHINE_MEM:{keyGrp}";
|
||||
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "MacchineGetFiltAsync",
|
||||
memKey: memKey,
|
||||
redisKey: redisKey,
|
||||
// ✅ TTL coerente con il tuo requisito (prima avevi 1 minuto)
|
||||
memoryTtl: TimeSpan.FromMinutes(1),
|
||||
dbFactory: async () =>
|
||||
await dbController.MacchineGetFiltAsync(codGruppo)
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "MacchineGetFiltAsync",
|
||||
cacheKey: redisKey,
|
||||
expiration: TimeSpan.FromMinutes(5),
|
||||
fetchFunc: async () =>
|
||||
await dbController.MacchineGetFiltAsync(codGruppo)
|
||||
?? new List<MacchineModel>()
|
||||
);
|
||||
);
|
||||
|
||||
#if false
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "MacchineGetFiltAsync",
|
||||
memKey: memKey,
|
||||
redisKey: redisKey,
|
||||
// ✅ TTL coerente con il tuo requisito (prima avevi 1 minuto)
|
||||
memoryTtl: TimeSpan.FromMinutes(1),
|
||||
dbFactory: async () =>
|
||||
await dbController.MacchineGetFiltAsync(codGruppo)
|
||||
?? new List<MacchineModel>()
|
||||
);
|
||||
#endif
|
||||
}
|
||||
#if false
|
||||
public async Task<List<MacchineModel>> MacchineGetFiltAsync(string codGruppo)
|
||||
@@ -2196,23 +2413,41 @@ namespace MP.SPEC.Data
|
||||
string redisKey = Utils.redisOdlCurrByMac;
|
||||
string memKey = $"MEM:{redisKey}";
|
||||
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "OdlGetCurrentAsync",
|
||||
memKey: memKey,
|
||||
redisKey: redisKey,
|
||||
// ✅ TTL molto corto (come avevi: 3 secondi)
|
||||
memoryTtl: TimeSpan.FromSeconds(3),
|
||||
dbFactory: async () =>
|
||||
{
|
||||
var rawData = await dbController.OdlGetCurrentAsync();
|
||||
var dbResult = rawData
|
||||
.Select(x => x.IdxMacchina)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "OdlGetCurrentAsync",
|
||||
cacheKey: redisKey,
|
||||
expiration: TimeSpan.FromSeconds(3),
|
||||
fetchFunc: async () =>
|
||||
{
|
||||
var rawData = await dbController.OdlGetCurrentAsync();
|
||||
var dbResult = rawData
|
||||
.Select(x => x.IdxMacchina)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
return dbResult ?? new List<string>();
|
||||
}
|
||||
);
|
||||
return dbResult ?? new List<string>();
|
||||
}
|
||||
);
|
||||
|
||||
#if false
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "OdlGetCurrentAsync",
|
||||
memKey: memKey,
|
||||
redisKey: redisKey,
|
||||
// ✅ TTL molto corto (come avevi: 3 secondi)
|
||||
memoryTtl: TimeSpan.FromSeconds(3),
|
||||
dbFactory: async () =>
|
||||
{
|
||||
var rawData = await dbController.OdlGetCurrentAsync();
|
||||
var dbResult = rawData
|
||||
.Select(x => x.IdxMacchina)
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
return dbResult ?? new List<string>();
|
||||
}
|
||||
);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if false
|
||||
@@ -2658,18 +2893,16 @@ namespace MP.SPEC.Data
|
||||
/// <returns></returns>
|
||||
public async Task<List<PODLExpModel>> POdlToKitListGetFiltAsync(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate)
|
||||
{
|
||||
string currKey = $"{Utils.redisPOdlList}_kit:{codGruppo}:{idxMacchina}:{keyRichPart}:{lanciato}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}";
|
||||
string redisKey = $"{Utils.redisPOdlList}_kit:{codGruppo}:{idxMacchina}:{keyRichPart}:{lanciato}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}";
|
||||
|
||||
// ✅ stessa chiave per memoria (puoi anche prefissare)
|
||||
string memKey = $"MEM:{currKey}";
|
||||
string memKey = $"MEM:{redisKey}";
|
||||
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "POdlToKitListGetFiltAsync",
|
||||
memKey: memKey,
|
||||
redisKey: currKey,
|
||||
// ✅ TTL RAM breve (coerente con redisShortTimeCache)
|
||||
memoryTtl: TimeSpan.FromSeconds(redisShortTimeCache),
|
||||
dbFactory: async () =>
|
||||
return await GetOrFetchAsync(
|
||||
operationName: "POdlToKitListGetFiltAsync",
|
||||
cacheKey: redisKey,
|
||||
expiration: TimeSpan.FromSeconds(redisShortTimeCache),
|
||||
fetchFunc: async () =>
|
||||
await dbController.ListPODL_KitFiltAsync(
|
||||
lanciato,
|
||||
keyRichPart,
|
||||
@@ -2678,7 +2911,26 @@ namespace MP.SPEC.Data
|
||||
startDate,
|
||||
endDate
|
||||
) ?? new List<PODLExpModel>()
|
||||
);
|
||||
);
|
||||
|
||||
#if false
|
||||
return await GetOrCreateCachedAsync(
|
||||
operationName: "POdlToKitListGetFiltAsync",
|
||||
memKey: memKey,
|
||||
redisKey: redisKey,
|
||||
// ✅ TTL RAM breve (coerente con redisShortTimeCache)
|
||||
memoryTtl: TimeSpan.FromSeconds(redisShortTimeCache),
|
||||
dbFactory: async () =>
|
||||
await dbController.ListPODL_KitFiltAsync(
|
||||
lanciato,
|
||||
keyRichPart,
|
||||
idxMacchina,
|
||||
codGruppo,
|
||||
startDate,
|
||||
endDate
|
||||
) ?? new List<PODLExpModel>()
|
||||
);
|
||||
#endif
|
||||
}
|
||||
#if false
|
||||
public async Task<List<PODLExpModel>> POdlToKitListGetFiltAsync(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate)
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>MP.SPEC</RootNamespace>
|
||||
<Version>8.16.2605.2619</Version>
|
||||
<Version>8.16.2605.2708</Version>
|
||||
<UserSecretsId>1800a78a-6ff1-40f9-b490-87fb8bfc1394</UserSecretsId>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
@@ -42,6 +42,7 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.Negotiate" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Web" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" />
|
||||
<PackageReference Include="Newtonsoft.Json" />
|
||||
<PackageReference Include="NLog.Targets.OpenTelemetryProtocol" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" />
|
||||
@@ -52,6 +53,9 @@
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.SqlClient" />
|
||||
<PackageReference Include="OpenTelemetry.Instrumentation.StackExchangeRedis" />
|
||||
<PackageReference Include="System.Text.Encodings.Web" />
|
||||
<PackageReference Include="ZiggyCreatures.FusionCache" />
|
||||
<PackageReference Include="ZiggyCreatures.FusionCache.Backplane.StackExchangeRedis" />
|
||||
<PackageReference Include="ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
+21
-3
@@ -1,5 +1,6 @@
|
||||
using Microsoft.AspNetCore.Authentication.Negotiate;
|
||||
using Microsoft.AspNetCore.StaticFiles;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using MP.AppAuth.Services;
|
||||
using MP.Data.Services;
|
||||
@@ -12,7 +13,9 @@ using NLog.Web;
|
||||
using OpenTelemetry.Resources;
|
||||
using OpenTelemetry.Trace;
|
||||
using StackExchange.Redis;
|
||||
|
||||
using ZiggyCreatures.Caching.Fusion;
|
||||
using ZiggyCreatures.Caching.Fusion.Backplane.StackExchangeRedis;
|
||||
using ZiggyCreatures.Caching.Fusion.Serialization.NewtonsoftJson;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@@ -30,7 +33,7 @@ string connStringRedis = configuration.GetConnectionString("Redis") ?? "localhos
|
||||
//string connStringRedis = ConfMan.GetConnectionString("RedisAdmin");
|
||||
string redisSrvAddr = connStringRedis.Substring(0, connStringRedis.IndexOf(":"));
|
||||
// avvio oggetto shared x redis...
|
||||
var redisMultiplexer = ConnectionMultiplexer.Connect(connStringRedis);
|
||||
IConnectionMultiplexer redisMultiplexer = ConnectionMultiplexer.Connect(connStringRedis);
|
||||
|
||||
|
||||
// ====================================================================
|
||||
@@ -135,9 +138,24 @@ builder.Services.AddRazorComponents()
|
||||
builder.Services.AddRazorPages();
|
||||
|
||||
// memory + redis preliminare
|
||||
builder.Services.AddMemoryCache();
|
||||
builder.Services.AddSingleton<IConnectionMultiplexer>(redisMultiplexer);
|
||||
|
||||
// ✅ Distributed cache (necessario per FusionCache)
|
||||
builder.Services.AddStackExchangeRedisCache(options =>
|
||||
{
|
||||
options.Configuration = connStringRedis;
|
||||
});
|
||||
|
||||
// ✅ FusionCache
|
||||
builder.Services.AddFusionCache()
|
||||
.WithDistributedCache(sp => sp.GetRequiredService<IDistributedCache>())
|
||||
.WithSerializer(new FusionCacheNewtonsoftJsonSerializer())
|
||||
.WithBackplane(new RedisBackplane(new RedisBackplaneOptions
|
||||
{
|
||||
ConnectionMultiplexerFactory = () => Task.FromResult(redisMultiplexer)
|
||||
}));
|
||||
|
||||
|
||||
// altri servizi
|
||||
builder.Services.AddSingleton<MpDataService>();
|
||||
builder.Services.AddSingleton<ListSelectDataSrv>();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MAPOSPEC </i>
|
||||
<h4>Versione: 8.16.2605.2619</h4>
|
||||
<h4>Versione: 8.16.2605.2708</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2605.2619
|
||||
8.16.2605.2708
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2605.2619</version>
|
||||
<version>8.16.2605.2708</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/MP.SPEC.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
"Microsoft.AspNetCore": "Warning",
|
||||
"ZiggyCreatures.Caching.Fusion": "Warning"
|
||||
}
|
||||
},
|
||||
"NLog": {
|
||||
|
||||
Reference in New Issue
Block a user