diff --git a/Directory.Packages.props b/Directory.Packages.props
index 71c44dc1..aee39b65 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -24,7 +24,7 @@
-
+
diff --git a/MP.Data/Controllers/MpSpecController.cs b/MP.Data/Controllers/MpSpecController.cs
index 9847a5ad..cb8d41ef 100644
--- a/MP.Data/Controllers/MpSpecController.cs
+++ b/MP.Data/Controllers/MpSpecController.cs
@@ -342,9 +342,9 @@ namespace MP.Data.Controllers
/// Elenco valori ammessi x Stati commessa (es Yacht Baglietto)
///
///
- public List AnagStatiComm()
+ public Task> AnagStatiCommAsync()
{
- return ListValuesFilt("PODL", "StatoComm");
+ return ListValuesFiltAsync("PODL", "StatoComm");
}
///
@@ -1522,6 +1522,27 @@ namespace MP.Data.Controllers
return dbResult;
}
+ ///
+ /// Elenco valori ammessi x tabella/colonna Async
+ ///
+ ///
+ ///
+ ///
+ public async Task> ListValuesFiltAsync(string tabName, string fieldName)
+ {
+ List dbResult = new List();
+ 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;
+ }
+
///
/// Elenco Macchine dato operatore secondo gruppi (macchine/operatore)
///
diff --git a/MP.SPEC/Data/MpDataService.cs b/MP.SPEC/Data/MpDataService.cs
index 4014b17d..8370b7cf 100644
--- a/MP.SPEC/Data/MpDataService.cs
+++ b/MP.SPEC/Data/MpDataService.cs
@@ -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("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> 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())
- );
+ return await GetOrFetchAsync(
+ operationName: "AnagStatiCommAsync",
+ cacheKey: Utils.redisStatoCom,
+ expiration: TimeSpan.FromMinutes(5),
+ fetchFunc: async () =>
+ await dbController.AnagStatiCommAsync() ?? new List()
+ );
}
#if false
@@ -345,7 +346,7 @@ namespace MP.SPEC.Data
public async Task> AnagTipoArtLV()
{
- using var activity = ActivitySource.StartActivity("AnagStatiComm");
+ using var activity = ActivitySource.StartActivity("AnagStatiCommAsync");
string source = "DB";
List? result = new List();
// 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()
- );
+ );
+
+
+#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()
+ );
+#endif
}
#if false
public async Task> 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()
- );
+ );
+
+#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()
+ );
+#endif
}
+ ///
+ /// Implementa gestione recupero cache da memoria o da obj esterno + cache memoria + tracking attività
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private async Task GetOrFetchAsync(string operationName, string cacheKey, Func> fetchFunc, TimeSpan expiration, params string[] tags)
+ {
+ using var activity = ActivitySource.StartActivity(operationName);
+ string source;
+ var tryGet = await _cache.TryGetAsync(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(
+ 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 GetOrFetchAsync(string operationName, string cacheKey, Func> fetchFunc, TimeSpan expiration)
+ {
+ using var activity = ActivitySource.StartActivity(operationName);
+
+ string source;
+ T result;
+
+ // ✅ 1. Tenta MEMORY / DISTRIBUTED (senza factory)
+ var memTry = await _cache.TryGetAsync(cacheKey);
+
+ if (memTry.HasValue)
+ {
+ result = memTry.Value!;
+ source = "MEMORY";
+ }
+ else
+ {
+ bool fromDb = false;
+
+ // ✅ 2. fallback con factory
+ result = await _cache.GetOrSetAsync(
+ 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
+
+ ///
+ /// Cancellazione FusionCache (totale)
+ ///
+ ///
+ public async Task FlushCacheAsync()
+ {
+ bool fatto = false;
+ await _cache.ClearAsync();
+ fatto = true;
+ return fatto;
+ }
+
+ ///
+ /// Cancellazione FusionCache dato elenco tags
+ ///
+ ///
+ public async Task FlushCacheByTagsAsync(List listTags)
+ {
+ bool fatto = false;
+ foreach (var item in listTags)
+ {
+ await _cache.RemoveByTagAsync(item);
+ }
+ fatto = true;
+ return fatto;
+ }
+ ///
+ /// Cancellazione FusionCache dato singolo tag
+ ///
+ ///
+ public async Task FlushCacheByTagAsync(string tag)
+ {
+ bool fatto = false;
+ await _cache.RemoveByTagAsync(tag);
+ fatto = true;
+ return fatto;
+ }
+
+#if false
///
/// Helper per gestione cache a 3 livelli: MEMORY, REDIS e DB con opzioni
///
@@ -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 { memKey },
+ (_, set) => { set.Add(memKey); return set; }
+ );
+
// ✅ default serializer (fallback)
serialize ??= (obj) => JsonConvert.SerializeObject(obj);
deserialize ??= (str) => JsonConvert.DeserializeObject(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;
- }
+ }
+ ///
+ /// Invalidazione di una chiave in memoria e Redis
+ ///
+ ///
+ ///
+ ///
+ public async Task InvalidateCacheAsync(string memKey, string redisKey)
+ {
+ // ✅ memoria
+ _cache.Remove(memKey);
+
+ // ✅ redis
+ await redisDb.KeyDeleteAsync(redisKey);
+
+ LogTrace($"Cache invalidated | {memKey}");
+ }
+ private readonly ConcurrentDictionary> _memoryKeys = new();
+ ///
+ /// Invalidazione cache da pattern
+ ///
+ ///
+ ///
+ 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
///
/// 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;
///
/// Elenco di tutte le macchine filtrate x gruppo
///
@@ -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()
- );
+ );
+
+#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()
+ );
+#endif
}
#if false
public async Task> 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();
- }
- );
+ return dbResult ?? new List();
+ }
+ );
+
+#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();
+ }
+ );
+#endif
}
#if false
@@ -2658,18 +2893,16 @@ namespace MP.SPEC.Data
///
public async Task> 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()
- );
+ );
+
+#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()
+ );
+#endif
}
#if false
public async Task> POdlToKitListGetFiltAsync(bool lanciato, string keyRichPart, string idxMacchina, string codGruppo, DateTime startDate, DateTime endDate)
diff --git a/MP.SPEC/MP.SPEC.csproj b/MP.SPEC/MP.SPEC.csproj
index 8ce4107b..66b09440 100644
--- a/MP.SPEC/MP.SPEC.csproj
+++ b/MP.SPEC/MP.SPEC.csproj
@@ -5,7 +5,7 @@
enable
enable
MP.SPEC
- 8.16.2605.2619
+ 8.16.2605.2708
1800a78a-6ff1-40f9-b490-87fb8bfc1394
en
@@ -42,6 +42,7 @@
+
@@ -52,6 +53,9 @@
+
+
+
diff --git a/MP.SPEC/Program.cs b/MP.SPEC/Program.cs
index 5b8365ff..4209457e 100644
--- a/MP.SPEC/Program.cs
+++ b/MP.SPEC/Program.cs
@@ -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(redisMultiplexer);
+// ✅ Distributed cache (necessario per FusionCache)
+builder.Services.AddStackExchangeRedisCache(options =>
+{
+ options.Configuration = connStringRedis;
+});
+
+// ✅ FusionCache
+builder.Services.AddFusionCache()
+ .WithDistributedCache(sp => sp.GetRequiredService())
+ .WithSerializer(new FusionCacheNewtonsoftJsonSerializer())
+ .WithBackplane(new RedisBackplane(new RedisBackplaneOptions
+ {
+ ConnectionMultiplexerFactory = () => Task.FromResult(redisMultiplexer)
+ }));
+
+
// altri servizi
builder.Services.AddSingleton();
builder.Services.AddSingleton();
diff --git a/MP.SPEC/Resources/ChangeLog.html b/MP.SPEC/Resources/ChangeLog.html
index dca5987a..59e7e6d9 100644
--- a/MP.SPEC/Resources/ChangeLog.html
+++ b/MP.SPEC/Resources/ChangeLog.html
@@ -1,6 +1,6 @@
Modulo MAPOSPEC
- Versione: 8.16.2605.2619
+ Versione: 8.16.2605.2708
Note di rilascio:
-
diff --git a/MP.SPEC/Resources/VersNum.txt b/MP.SPEC/Resources/VersNum.txt
index 5ea9eaa9..933735f6 100644
--- a/MP.SPEC/Resources/VersNum.txt
+++ b/MP.SPEC/Resources/VersNum.txt
@@ -1 +1 @@
-8.16.2605.2619
+8.16.2605.2708
diff --git a/MP.SPEC/Resources/manifest.xml b/MP.SPEC/Resources/manifest.xml
index b079c1c6..02e7a70d 100644
--- a/MP.SPEC/Resources/manifest.xml
+++ b/MP.SPEC/Resources/manifest.xml
@@ -1,6 +1,6 @@
-
- 8.16.2605.2619
+ 8.16.2605.2708
https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/MP.SPEC.zip
https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/ChangeLog.html
false
diff --git a/MP.SPEC/appsettings.json b/MP.SPEC/appsettings.json
index bf3f1ae9..712e6fcb 100644
--- a/MP.SPEC/appsettings.json
+++ b/MP.SPEC/appsettings.json
@@ -2,7 +2,8 @@
"Logging": {
"LogLevel": {
"Default": "Information",
- "Microsoft.AspNetCore": "Warning"
+ "Microsoft.AspNetCore": "Warning",
+ "ZiggyCreatures.Caching.Fusion": "Warning"
}
},
"NLog": {