Fix integrazione preliminare servizi utils.stats x IOC:
- aggiunta migrations - correzioni versione ef6 da ef8 - correzioni init varie
This commit is contained in:
@@ -4,19 +4,14 @@ using Microsoft.Extensions.Configuration;
|
||||
using MP.Core.DTO;
|
||||
using MP.Core.Objects;
|
||||
using MP.Data.DbModels;
|
||||
using MP.Data.DTO;
|
||||
using MP.Data.Translate;
|
||||
using NLog;
|
||||
using Org.BouncyCastle.Crypto;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using ZXing;
|
||||
using static EgwCoreLib.Utils.DtUtils;
|
||||
using static MP.Core.Objects.Enums;
|
||||
|
||||
namespace MP.Data.Controllers
|
||||
{
|
||||
@@ -531,7 +526,7 @@ namespace MP.Data.Controllers
|
||||
}
|
||||
return fatto;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Elenco da tabella Config
|
||||
/// </summary>
|
||||
@@ -682,7 +677,7 @@ namespace MP.Data.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Effettua salvataggio snapshot parametri (con stored) + svuota eventuale cache redisConn
|
||||
/// Effettua salvataggio snapshot parametri (con stored) + svuota eventuale cache _redisConn
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina">macchina</param>
|
||||
/// <param name="maxSec">Num massimo secondi per recuperare dati correnti</param>
|
||||
@@ -705,7 +700,7 @@ namespace MP.Data.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Effettua salvataggio snapshot parametri (con stored) + svuota eventuale cache redisConn
|
||||
/// Effettua salvataggio snapshot parametri (con stored) + svuota eventuale cache _redisConn
|
||||
/// </summary>
|
||||
/// <param name="idxMacchina">macchina</param>
|
||||
/// <param name="dtMin">Data min x selezione</param>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
// <Auto-Generated>
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
// <Auto-Generated>
|
||||
// This is here so CodeMaid doesn't reorganize this document
|
||||
// </Auto-Generated>
|
||||
namespace MP.Data.DbModels.Utils
|
||||
@@ -24,17 +28,9 @@ namespace MP.Data.DbModels.Utils
|
||||
/// </summary>
|
||||
public double AvgDuration { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// Durata massima esecuzione
|
||||
/// Durata minima esecuzione
|
||||
/// </summary>
|
||||
public double MaxDuration { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// Durata 5 perc esecuzione
|
||||
/// </summary>
|
||||
public double Perc05Duration { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// Durata 95 perc esecuzione
|
||||
/// </summary>
|
||||
public double Perc95Duration { get; set; } = 0;
|
||||
public double MinDuration { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// Durata massima esecuzione
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
// <Auto-Generated>
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.ComponentModel.DataAnnotations.Schema;
|
||||
|
||||
// <Auto-Generated>
|
||||
// This is here so CodeMaid doesn't reorganize this document
|
||||
// </Auto-Generated>
|
||||
namespace MP.Data.DbModels.Utils
|
||||
@@ -32,13 +36,9 @@ namespace MP.Data.DbModels.Utils
|
||||
/// </summary>
|
||||
public double AvgDuration { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// Durata 5 perc esecuzione
|
||||
/// Durata minima esecuzione
|
||||
/// </summary>
|
||||
public double Perc05Duration { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// Durata 95 perc esecuzione
|
||||
/// </summary>
|
||||
public double Perc95Duration { get; set; } = 0;
|
||||
public double MinDuration { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// Durata massima esecuzione
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using MP.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace MP.Data.Migrations.MoonPro_Utils
|
||||
{
|
||||
[DbContext(typeof(MoonPro_UtilsContext))]
|
||||
[Migration("20260407082127_InitDb")]
|
||||
partial class InitDb
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.UseCollation("SQL_Latin1_General_CP1_CI_AS")
|
||||
.HasAnnotation("ProductVersion", "6.0.36")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
|
||||
|
||||
modelBuilder.Entity("MP.Data.DbModels.Utils.StatsAggregatedModel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
||||
|
||||
b.Property<double>("AvgDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<DateTime>("Hour")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<double>("MaxDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<double>("MinDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<long>("NoReply")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("RequestCount")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Hour")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx_statsaggr_hour");
|
||||
|
||||
b.ToTable("stats_aggr");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MP.Data.DbModels.Utils.StatsDetailModel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
||||
|
||||
b.Property<double>("AvgDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<string>("Environment")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<DateTime>("Hour")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<double>("MaxDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<double>("MinDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<long>("NoReply")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("RequestCount")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Hour", "Environment", "Type")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx_statsdet_hour_env_type")
|
||||
.HasFilter("[Environment] IS NOT NULL AND [Type] IS NOT NULL");
|
||||
|
||||
b.ToTable("stats_detail");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace MP.Data.Migrations.MoonPro_Utils
|
||||
{
|
||||
public partial class InitDb : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "stats_aggr",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Hour = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
RequestCount = table.Column<long>(type: "bigint", nullable: false),
|
||||
AvgDuration = table.Column<double>(type: "float", nullable: false),
|
||||
MinDuration = table.Column<double>(type: "float", nullable: false),
|
||||
MaxDuration = table.Column<double>(type: "float", nullable: false),
|
||||
NoReply = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_stats_aggr", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "stats_detail",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
Environment = table.Column<string>(type: "nvarchar(450)", nullable: true),
|
||||
Type = table.Column<string>(type: "nvarchar(450)", nullable: true),
|
||||
Hour = table.Column<DateTime>(type: "datetime2", nullable: false),
|
||||
RequestCount = table.Column<long>(type: "bigint", nullable: false),
|
||||
AvgDuration = table.Column<double>(type: "float", nullable: false),
|
||||
MinDuration = table.Column<double>(type: "float", nullable: false),
|
||||
MaxDuration = table.Column<double>(type: "float", nullable: false),
|
||||
NoReply = table.Column<long>(type: "bigint", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_stats_detail", x => x.Id);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "idx_statsaggr_hour",
|
||||
table: "stats_aggr",
|
||||
column: "Hour",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "idx_statsdet_hour_env_type",
|
||||
table: "stats_detail",
|
||||
columns: new[] { "Hour", "Environment", "Type" },
|
||||
unique: true,
|
||||
filter: "[Environment] IS NOT NULL AND [Type] IS NOT NULL");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "stats_aggr");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "stats_detail");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
// <auto-generated />
|
||||
using System;
|
||||
using MP.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace MP.Data.Migrations.MoonPro_Utils
|
||||
{
|
||||
[DbContext(typeof(MoonPro_UtilsContext))]
|
||||
partial class MoonPro_UtilsContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.UseCollation("SQL_Latin1_General_CP1_CI_AS")
|
||||
.HasAnnotation("ProductVersion", "6.0.36")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128);
|
||||
|
||||
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder, 1L, 1);
|
||||
|
||||
modelBuilder.Entity("MP.Data.DbModels.Utils.StatsAggregatedModel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
||||
|
||||
b.Property<double>("AvgDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<DateTime>("Hour")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<double>("MaxDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<double>("MinDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<long>("NoReply")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("RequestCount")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Hour")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx_statsaggr_hour");
|
||||
|
||||
b.ToTable("stats_aggr");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("MP.Data.DbModels.Utils.StatsDetailModel", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"), 1L, 1);
|
||||
|
||||
b.Property<double>("AvgDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<string>("Environment")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.Property<DateTime>("Hour")
|
||||
.HasColumnType("datetime2");
|
||||
|
||||
b.Property<double>("MaxDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<double>("MinDuration")
|
||||
.HasColumnType("float");
|
||||
|
||||
b.Property<long>("NoReply")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<long>("RequestCount")
|
||||
.HasColumnType("bigint");
|
||||
|
||||
b.Property<string>("Type")
|
||||
.HasColumnType("nvarchar(450)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("Hour", "Environment", "Type")
|
||||
.IsUnique()
|
||||
.HasDatabaseName("idx_statsdet_hour_env_type")
|
||||
.HasFilter("[Environment] IS NOT NULL AND [Type] IS NOT NULL");
|
||||
|
||||
b.ToTable("stats_detail");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,8 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MP.Core.DTO;
|
||||
using MP.Data.DbModels;
|
||||
using MP.Data.DTO;
|
||||
using MP.Data.DbModels.Utils;
|
||||
using NLog;
|
||||
using System;
|
||||
|
||||
#nullable disable
|
||||
// <Auto-Generated>
|
||||
@@ -34,10 +31,10 @@ namespace MP.Data
|
||||
{
|
||||
}
|
||||
|
||||
public MoonPro_UtilsContext(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
}
|
||||
//public MoonPro_UtilsContext(IConfiguration configuration)
|
||||
//{
|
||||
// _configuration = configuration;
|
||||
//}
|
||||
|
||||
public MoonPro_UtilsContext(DbContextOptions<MoonPro_UtilsContext> options) : base(options)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MP.Data.Repository
|
||||
{
|
||||
public abstract class BaseRepository : IBaseRepository
|
||||
{
|
||||
#region Protected Fields
|
||||
|
||||
protected readonly IDbContextFactory<MoonPro_UtilsContext> _ctxFactory;
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Constructors
|
||||
|
||||
protected BaseRepository(IDbContextFactory<MoonPro_UtilsContext> ctxFactory)
|
||||
=> _ctxFactory = ctxFactory;
|
||||
|
||||
#endregion Protected Constructors
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Creazione dbcontext per singola transazione
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected async Task<MoonPro_UtilsContext> CreateContextAsync()
|
||||
=> await _ctxFactory.CreateDbContextAsync();
|
||||
|
||||
#if false
|
||||
/// <summary>
|
||||
/// Salvataggio dati asincrono
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected async Task<bool> SaveChangesAsync(DataLayerContext ctx)
|
||||
=> await ctx.SaveChangesAsync() > 0;
|
||||
#endif
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#if false
|
||||
protected readonly DataLayerContext _dbCtx;
|
||||
protected BaseRepository(DataLayerContext db) => _dbCtx = db;
|
||||
public async Task<bool> SaveChangesAsync() => await _dbCtx.SaveChangesAsync() > 0;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace MP.Data.Repository
|
||||
{
|
||||
public interface IBaseRepository
|
||||
{
|
||||
//Task<DataLayerContext> CreateContextAsync();
|
||||
//Task<bool> SaveChangesAsync(DataLayerContext ctx);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,8 @@
|
||||
using EgwCoreLib.Utils;
|
||||
using MP.Data.DbModels.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MP.Data.Repository.Utils
|
||||
{
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
using EgwCoreLib.Utils;
|
||||
using MP.Data.DbModels.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MP.Data.Repository.Utils
|
||||
{
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
using EgwCoreLib.Utils;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MP.Data.DbModels.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MP.Data.Repository.Utils
|
||||
{
|
||||
@@ -6,7 +12,7 @@ namespace MP.Data.Repository.Utils
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
public StatsAggrRepository(IDbContextFactory<DataLayerContext> ctxFactory) : base(ctxFactory)
|
||||
public StatsAggrRepository(IDbContextFactory<MoonPro_UtilsContext> ctxFactory) : base(ctxFactory)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -58,11 +64,20 @@ namespace MP.Data.Repository.Utils
|
||||
{
|
||||
DateTime startDate = firstRec.Hour;
|
||||
DateTime endDate = lastRec.Hour;
|
||||
// uso direttamente ExecuteDelete
|
||||
|
||||
// uso direttamente ExecuteDelete quando in EFCore8...
|
||||
#if false
|
||||
await dbCtx
|
||||
.DbSetStatsAggr
|
||||
.Where(x => x.Hour >= startDate && x.Hour <= endDate)
|
||||
.ExecuteDeleteAsync();
|
||||
#endif
|
||||
var items = await dbCtx.DbSetStatsAggr
|
||||
.Where(x => x.Hour >= startDate && x.Hour <= endDate)
|
||||
.ToListAsync();
|
||||
|
||||
dbCtx.DbSetStatsAggr.RemoveRange(items);
|
||||
await dbCtx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
using EgwCoreLib.Utils;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MP.Data.DbModels.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MP.Data.Repository.Utils
|
||||
{
|
||||
@@ -6,7 +12,7 @@ namespace MP.Data.Repository.Utils
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
public StatsDetailRepository(IDbContextFactory<DataLayerContext> ctxFactory) : base(ctxFactory)
|
||||
public StatsDetailRepository(IDbContextFactory<MoonPro_UtilsContext> ctxFactory) : base(ctxFactory)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -79,11 +85,20 @@ namespace MP.Data.Repository.Utils
|
||||
{
|
||||
DateTime startDate = firstRec.Hour;
|
||||
DateTime endDate = lastRec.Hour;
|
||||
// uso direttamente ExecuteDelete
|
||||
await dbCtx
|
||||
.DbSetStatsDet
|
||||
.Where(x => x.Hour >= startDate && x.Hour <= endDate)
|
||||
.ExecuteDeleteAsync();
|
||||
|
||||
// uso direttamente ExecuteDelete quando in EFCore8...
|
||||
#if false
|
||||
await dbCtx
|
||||
.DbSetStatsDet
|
||||
.Where(x => x.Hour >= startDate && x.Hour <= endDate)
|
||||
.ExecuteDeleteAsync();
|
||||
#endif
|
||||
var items = await dbCtx.DbSetStatsDet
|
||||
.Where(x => x.Hour >= startDate && x.Hour <= endDate)
|
||||
.ToListAsync();
|
||||
|
||||
dbCtx.DbSetStatsDet.RemoveRange(items);
|
||||
await dbCtx.SaveChangesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+196
-39
@@ -1,5 +1,4 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MP.Core.Objects;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
@@ -8,7 +7,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MP.Data.Services
|
||||
@@ -16,39 +15,20 @@ namespace MP.Data.Services
|
||||
/// <summary>
|
||||
/// Classe di partenza x costruzione servizi di accesso dati + cache
|
||||
/// </summary>
|
||||
public class BaseServ:IDisposable
|
||||
public class BaseServ : IDisposable
|
||||
{
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
private bool _disposed = false;
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Free managed resources here
|
||||
redisDb = null;
|
||||
}
|
||||
|
||||
// Free unmanaged resources here
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Public Constructors
|
||||
|
||||
public BaseServ(IConfiguration configuration, IConnectionMultiplexer redConn)
|
||||
{
|
||||
_configuration = configuration;
|
||||
|
||||
// Verifica conf trace...
|
||||
_traceEnabled = _configuration.GetValue<bool>("Otel:EnableTracing", false);
|
||||
|
||||
// setup componenti REDIS
|
||||
this.redisConn = redConn;
|
||||
redisDb = redisConn.GetDatabase();
|
||||
_redisConn = redConn;
|
||||
_redisDb = _redisConn.GetDatabase();
|
||||
|
||||
// json serializer... FIX errore loop circolare https://www.ryadel.com/en/jsonserializationexception-self-referencing-loop-detected-error-fix-entity-framework-asp-net-core/
|
||||
JSSettings = new JsonSerializerSettings()
|
||||
@@ -64,6 +44,12 @@ namespace MP.Data.Services
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupero info IOB x TAB (da info registrate IOB-WIN--> MP-IO)
|
||||
/// </summary>
|
||||
@@ -75,9 +61,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
IOB_data? result = new IOB_data();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = RedHashMpIO($"hM2IOB:{IdxMacchina}");
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
RedisValue rawData = _redisDb.StringGet(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
@@ -87,7 +73,7 @@ namespace MP.Data.Services
|
||||
else
|
||||
{
|
||||
Log.Error($"Errore: non trovato valore <IOB_data> valido in REDIS | key: {currKey}");
|
||||
Log.Info($"REDIS | conf: {redisConn.Configuration}");
|
||||
Log.Info($"REDIS | conf: {_redisConn.Configuration}");
|
||||
Log.Info($" --> Valore trovato:{Environment.NewLine}{rawData}");
|
||||
}
|
||||
if (result == null)
|
||||
@@ -111,11 +97,11 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
Dictionary<string, string> result = new Dictionary<string, string>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = RedHashMpIO($"IOB:{IdxMacchina}:MachIobConf");
|
||||
try
|
||||
{
|
||||
result = redisDb
|
||||
result = _redisDb
|
||||
.HashGetAll(currKey)
|
||||
.ToDictionary(x => $"{x.Name}", x => $"{x.Value}");
|
||||
source = "REDIS";
|
||||
@@ -145,11 +131,11 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
string result = "";
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = RedHashMpIO($"IOB:{IdxMacchina}:ConfYaml");
|
||||
try
|
||||
{
|
||||
result = redisDb.StringGet(currKey);
|
||||
result = _redisDb.StringGet(currKey);
|
||||
source = "REDIS";
|
||||
}
|
||||
catch (Exception exc)
|
||||
@@ -170,20 +156,31 @@ namespace MP.Data.Services
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto per collezione dati Activity (span in Uptrace)
|
||||
/// </summary>
|
||||
protected static readonly ActivitySource ActivitySource = new ActivitySource("MP.IOC");
|
||||
|
||||
protected static IConfiguration _configuration = null!;
|
||||
protected JsonSerializerSettings? JSSettings;
|
||||
protected string MpIoNS = "";
|
||||
|
||||
/// <summary>
|
||||
/// Abilitazione operazioni tracing generiche
|
||||
/// </summary>
|
||||
protected readonly bool _traceEnabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto per connessione a REDIS
|
||||
/// </summary>
|
||||
protected IConnectionMultiplexer redisConn = null!;
|
||||
protected IConnectionMultiplexer _redisConn = null!;
|
||||
|
||||
//ISubscriber sub = redisConn.GetSubscriber();
|
||||
//ISubscriber sub = _redisConn.GetSubscriber();
|
||||
/// <summary>
|
||||
/// Oggetto DB redis da impiegare x chiamate R/W
|
||||
/// </summary>
|
||||
protected IDatabase redisDb = null!;
|
||||
protected IDatabase _redisDb = null!;
|
||||
|
||||
protected JsonSerializerSettings? JSSettings;
|
||||
protected string MpIoNS = "";
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
@@ -225,6 +222,128 @@ namespace MP.Data.Services
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Helper avvio attività per la funzione tracciata
|
||||
/// </summary>
|
||||
/// <param name="methodName"></param>
|
||||
/// <returns></returns>
|
||||
protected static Activity? StartActivity([CallerMemberName] string? methodName = null)
|
||||
{
|
||||
var activity = ActivitySource.StartActivity(methodName ?? "UNDEF");
|
||||
activity?.SetTag("host.name", Environment.MachineName);
|
||||
return activity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalida una o più chiavi/pattern in Redis
|
||||
/// </summary>
|
||||
protected async Task ClearCacheAsync(params string[] patterns)
|
||||
{
|
||||
foreach (var pattern in patterns)
|
||||
{
|
||||
// Chiamata al tuo metodo esistente
|
||||
await ExecFlushRedisPatternAsync((RedisValue)pattern);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Free managed resources here
|
||||
_redisDb = null;
|
||||
}
|
||||
|
||||
// Free unmanaged resources here
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Metodo di flush dati cache Redis
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task ExecFlushRedisPatternAsync(RedisValue pattern)
|
||||
{
|
||||
// Qui inserisci la tua logica attuale (es. via Lua script o Keys/Scan)
|
||||
// Esempio rapido via server scan:
|
||||
var endpoints = _redisConn.GetEndPoints();
|
||||
foreach (var endpoint in endpoints)
|
||||
{
|
||||
var server = _redisConn.GetServer(endpoint);
|
||||
await foreach (var key in server.KeysAsync(_redisDb.Database, pattern))
|
||||
{
|
||||
await _redisDb.KeyDeleteAsync(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper generale di lettura da cache o da funzione (DB) con caching successivo
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="factory"></param>
|
||||
/// <param name="expiration"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task<T> GetOrSetCacheAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiration = null, [CallerMemberName] string? caller = null)
|
||||
{
|
||||
using var activity = StartActivity();
|
||||
string source = "DB";
|
||||
|
||||
// 1. Provo Redis
|
||||
var cached = await _redisDb.StringGetAsync(key);
|
||||
if (cached.HasValue)
|
||||
{
|
||||
source = "REDIS";
|
||||
var cachedResult = JsonConvert.DeserializeObject<T>(cached!)!;
|
||||
|
||||
activity?.SetTag("data.source", source);
|
||||
LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms", NLog.LogLevel.Trace, caller);
|
||||
|
||||
return cachedResult;
|
||||
}
|
||||
|
||||
// 2. Chiamo il factory (DB)
|
||||
T result = await factory();
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
// 3. Salva in Redis per la prossima volta
|
||||
var serialized = JsonConvert.SerializeObject(result, JSSettings);
|
||||
await _redisDb.StringSetAsync(key, serialized, expiration ?? LongCache);
|
||||
}
|
||||
|
||||
// sistemo activity tracking data
|
||||
activity?.SetTag("data.source", source);
|
||||
activity?.Stop();
|
||||
|
||||
// log in console
|
||||
LogTrace($"GetOrSetCacheAsync | {source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds:N3}ms", NLog.LogLevel.Trace, caller);
|
||||
|
||||
return result!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper trace messaggio log (SE abilitato)
|
||||
/// </summary>
|
||||
/// <param name="traceMsg"></param>
|
||||
/// <param name="reqLevel"></param>
|
||||
/// <param name="methodName"></param>
|
||||
protected void LogTrace(string traceMsg, NLog.LogLevel? reqLevel = null, [CallerMemberName] string? methodName = null)
|
||||
{
|
||||
if (!_traceEnabled)
|
||||
return;
|
||||
|
||||
reqLevel ??= NLog.LogLevel.Debug;
|
||||
|
||||
// Loggo!
|
||||
Log.Log(reqLevel, $"{methodName} | {traceMsg}");
|
||||
}
|
||||
|
||||
protected string RedHashMpIO(string keyName)
|
||||
{
|
||||
string result = keyName;
|
||||
@@ -240,11 +359,44 @@ namespace MP.Data.Services
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper generale per la telemetria e gestione eccezioni
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task<T> TraceAsync<T>(string name, Func<Activity?, Task<T>> body, object? parameters = null)
|
||||
{
|
||||
using var activity = ActivitySource.StartActivity(name);
|
||||
try
|
||||
{
|
||||
if (parameters != null)
|
||||
{
|
||||
activity?.SetTag("params", JsonConvert.SerializeObject(parameters));
|
||||
}
|
||||
var result = await body(activity);
|
||||
activity?.SetStatus(ActivityStatusCode.Ok);
|
||||
activity?.Stop();
|
||||
LogTrace($"TraceAsync | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms", methodName: name);
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
|
||||
//Log.Error(ex, "Errore in {MethodName}", name);
|
||||
LogTrace($"Errore in {name}", NLog.LogLevel.Error, name);
|
||||
throw; // Riesponi l'eccezione per il tracking globale
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private bool _disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Durata cache lunga IN SECONDI
|
||||
@@ -258,6 +410,11 @@ namespace MP.Data.Services
|
||||
|
||||
private Random rnd = new Random();
|
||||
|
||||
/// <summary>
|
||||
/// Path base chiavi REDIS
|
||||
/// </summary>
|
||||
protected readonly string _redisBaseKey = "MP:IOC";
|
||||
|
||||
#endregion Private Fields
|
||||
}
|
||||
}
|
||||
@@ -59,9 +59,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<DbSizeModel> result = new List<DbSizeModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:DbInfo:ALL";
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
RedisValue rawData = _redisDb.StringGet(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<DbSizeModel>>($"{rawData}");
|
||||
@@ -72,7 +72,7 @@ namespace MP.Data.Services
|
||||
result = dbController.AllDbInfo();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, UltraFastCache);
|
||||
_redisDb.StringSet(currKey, rawData, UltraFastCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -95,7 +95,7 @@ namespace MP.Data.Services
|
||||
string currKey = $"{redisBaseKey}:IobList";
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
string? rawData = redisDb.StringGet(currKey);
|
||||
string? rawData = _redisDb.StringGet(currKey);
|
||||
if (!string.IsNullOrEmpty(rawData) && rawData.Length > 2)
|
||||
{
|
||||
source = "REDIS";
|
||||
@@ -123,9 +123,9 @@ namespace MP.Data.Services
|
||||
//ordino x idxmacchina...
|
||||
dbResult = dbResult.OrderBy(x => x.IdxMacchina).ToList();
|
||||
|
||||
// serializzo in cache redisConn
|
||||
// serializzo in cache _redisConn
|
||||
rawData = JsonConvert.SerializeObject(dbResult, JSSettings);
|
||||
redisDb.StringSet(currKey, rawData, UltraLongCache);
|
||||
_redisDb.StringSet(currKey, rawData, UltraLongCache);
|
||||
}
|
||||
if (dbResult == null)
|
||||
{
|
||||
@@ -147,9 +147,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<RemoteRebootLogModel> result = new List<RemoteRebootLogModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:RemRebLog:ALL";
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
RedisValue rawData = _redisDb.StringGet(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
@@ -161,7 +161,7 @@ namespace MP.Data.Services
|
||||
result = dbController.RemRebootLogGetAll();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, UltraFastCache);
|
||||
_redisDb.StringSet(currKey, rawData, UltraFastCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -183,9 +183,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<RemoteRebootLogModel> result = new List<RemoteRebootLogModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:RemRebLog:LAST";
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
RedisValue rawData = _redisDb.StringGet(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
@@ -197,7 +197,7 @@ namespace MP.Data.Services
|
||||
result = dbController.RemRebootLogGetLast();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, UltraFastCache);
|
||||
_redisDb.StringSet(currKey, rawData, UltraFastCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
|
||||
@@ -63,9 +63,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<AnagArticoliModel>? result = new List<AnagArticoliModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:Art:{azienda}:{searchVal}:{numRecord}";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<AnagArticoliModel>>($"{rawData}");
|
||||
@@ -79,7 +79,7 @@ namespace MP.Data.Services
|
||||
#endif
|
||||
// serializzp e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await redisDb.StringSetAsync(currKey, rawData, LongCache);
|
||||
await _redisDb.StringSetAsync(currKey, rawData, LongCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -100,9 +100,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<ConfigModel>? result = new List<ConfigModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:Conf";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<ConfigModel>>($"{rawData}");
|
||||
@@ -113,7 +113,7 @@ namespace MP.Data.Services
|
||||
result = dbController.ConfigGetAll();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, LongCache);
|
||||
_redisDb.StringSet(currKey, rawData, LongCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -156,9 +156,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<LinkMenuModel>? result = new List<LinkMenuModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:Menu:ALL";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
result = JsonConvert.DeserializeObject<List<LinkMenuModel>>($"{rawData}");
|
||||
@@ -169,7 +169,7 @@ namespace MP.Data.Services
|
||||
result = await Task.FromResult(dbController.ListLinkAll());
|
||||
// serializzp e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
|
||||
await _redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -191,9 +191,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<LinkMenuModel>? result = new List<LinkMenuModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:Menu:{tipoLink}";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
@@ -205,7 +205,7 @@ namespace MP.Data.Services
|
||||
result = await Task.FromResult(dbController.ListLinkFilt(tipoLink));
|
||||
// serializzp e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
|
||||
await _redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -227,9 +227,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<MacchineModel>? result = new List<MacchineModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:Macc:{MatrOpr}";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
@@ -241,7 +241,7 @@ namespace MP.Data.Services
|
||||
result = await Task.FromResult(dbController.MacchineByMatrOper(MatrOpr));
|
||||
// serializzp e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
|
||||
await _redisDb.StringSetAsync(currKey, rawData, UltraLongCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -287,31 +287,31 @@ namespace MP.Data.Services
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Esegue flush memoria redisConn dato pat2Flush
|
||||
/// Esegue flush memoria _redisConn dato pat2Flush
|
||||
/// </summary>
|
||||
/// <param name="pat2Flush"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> ExecFlushRedisPattern(RedisValue pat2Flush)
|
||||
{
|
||||
bool answ = false;
|
||||
var masterEndpoint = redisConn.GetEndPoints()
|
||||
.Where(ep => redisConn.GetServer(ep).IsConnected && !redisConn.GetServer(ep).IsReplica)
|
||||
var masterEndpoint = _redisConn.GetEndPoints()
|
||||
.Where(ep => _redisConn.GetServer(ep).IsConnected && !_redisConn.GetServer(ep).IsReplica)
|
||||
.FirstOrDefault();
|
||||
|
||||
// sepattern è "*" elimino intero DB...
|
||||
if (masterEndpoint != null && (pat2Flush.Equals(new RedisValue("*")) || pat2Flush == RedisValue.Null))
|
||||
{
|
||||
redisConn.GetServer(masterEndpoint).FlushDatabase(database: redisDb.Database);
|
||||
_redisConn.GetServer(masterEndpoint).FlushDatabase(database: _redisDb.Database);
|
||||
}
|
||||
else
|
||||
{
|
||||
var server = redisConn.GetServer(masterEndpoint);
|
||||
var keys = server.Keys(database: redisDb.Database, pattern: pat2Flush, pageSize: 1000);
|
||||
var server = _redisConn.GetServer(masterEndpoint);
|
||||
var keys = server.Keys(database: _redisDb.Database, pattern: pat2Flush, pageSize: 1000);
|
||||
|
||||
var deleteTasks = new List<Task>();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
deleteTasks.Add(redisDb.KeyDeleteAsync(key));
|
||||
deleteTasks.Add(_redisDb.KeyDeleteAsync(key));
|
||||
if (deleteTasks.Count >= 1000)
|
||||
{
|
||||
await Task.WhenAll(deleteTasks);
|
||||
|
||||
@@ -688,7 +688,7 @@ namespace MP.Data.Services
|
||||
protected IConnectionMultiplexer redisConn = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto DB redisConn da impiegare x chiamate R/W
|
||||
/// Oggetto DB _redisConn da impiegare x chiamate R/W
|
||||
/// </summary>
|
||||
protected IDatabase redisDb = null!;
|
||||
|
||||
@@ -745,7 +745,7 @@ namespace MP.Data.Services
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recupero HashSet redisConn come Dictionary
|
||||
/// Recupero HashSet _redisConn come Dictionary
|
||||
/// </summary>
|
||||
/// <param name="currKey"></param>
|
||||
/// <param name="dict"></param>
|
||||
|
||||
@@ -60,9 +60,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<ConfigModel>? result = new List<ConfigModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:Conf";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
@@ -74,7 +74,7 @@ namespace MP.Data.Services
|
||||
result = dbController.ConfigGetAll();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, LongCache);
|
||||
_redisDb.StringSet(currKey, rawData, LongCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -103,9 +103,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<ODLExpModel>? result = new List<ODLExpModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:ODL:{IdxMacchina}:{CodArt}";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
@@ -117,7 +117,7 @@ namespace MP.Data.Services
|
||||
result = await Task.FromResult(dbController.ListODLFilt(inCorso, CodArt, keyRichPart, Reparto, IdxMacchina, startDate, endDate));
|
||||
// serializzp e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
await redisDb.StringSetAsync(currKey, rawData, LongCache);
|
||||
await _redisDb.StringSetAsync(currKey, rawData, LongCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace MP.Data.Services
|
||||
//Dictionary<string, List<EventDto>>? result = null;
|
||||
//// cerco in redis...
|
||||
//string currKey = $"{redisBaseKey}:PlannerData:{dtStart:yyyyMMdd}:{dtEnd:yyyyMMdd}";
|
||||
//RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
//RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
////if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
//if (rawData.HasValue && rawData.Length() > 2)
|
||||
//{
|
||||
@@ -80,7 +80,7 @@ namespace MP.Data.Services
|
||||
// result = dataSimController.PlannerGetEvents(dtStart, dtEnd);
|
||||
// // serializzo e salvo...
|
||||
// rawData = JsonConvert.SerializeObject(result);
|
||||
// await redisDb.StringSetAsync(currKey, rawData, LongCache);
|
||||
// await _redisDb.StringSetAsync(currKey, rawData, LongCache);
|
||||
//}
|
||||
//activity?.SetTag("data.source", source);
|
||||
//LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms");
|
||||
|
||||
@@ -91,7 +91,7 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<ConfigModel>? result = new List<ConfigModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:Conf";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
@@ -176,7 +176,7 @@ namespace MP.Data.Services
|
||||
string source = "DB";
|
||||
sw.Start();
|
||||
List<MacchineModel>? result = new List<MacchineModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{Constants.redisMacchine}:ALL";
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
if (rawData.HasValue)
|
||||
@@ -207,7 +207,7 @@ namespace MP.Data.Services
|
||||
string source = "DB";
|
||||
sw.Start();
|
||||
List<MacchineModel>? result = new List<MacchineModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{Constants.redisMacchine}:{CodGruppo}";
|
||||
RedisValue rawData = redisDb.StringGet(currKey);
|
||||
if (rawData.HasValue)
|
||||
@@ -326,7 +326,7 @@ namespace MP.Data.Services
|
||||
string source = "DB";
|
||||
sw.Start();
|
||||
List<MappaStatoExplModel>? result = new List<MappaStatoExplModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
RedisValue rawData = redisDb.StringGet(Constants.redisMseKey);
|
||||
if (rawData.HasValue && !forceDb)
|
||||
{
|
||||
@@ -426,7 +426,7 @@ namespace MP.Data.Services
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Esegue flush memoria redisConn dato pat2Flush
|
||||
/// Esegue flush memoria _redisConn dato pat2Flush
|
||||
/// </summary>
|
||||
/// <param name="pat2Flush"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
+196
-196
File diff suppressed because it is too large
Load Diff
@@ -62,9 +62,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<ConfigModel>? result = new List<ConfigModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:Conf";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
@@ -76,7 +76,7 @@ namespace MP.Data.Services
|
||||
result = dbController.ConfigGetAll();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, LongCache);
|
||||
_redisDb.StringSet(currKey, rawData, LongCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -122,9 +122,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<LingueModel>? result = new List<LingueModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:Lang";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
@@ -136,7 +136,7 @@ namespace MP.Data.Services
|
||||
result = dbController.LingueGetAll();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, UltraLongCache);
|
||||
_redisDb.StringSet(currKey, rawData, UltraLongCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -174,9 +174,9 @@ namespace MP.Data.Services
|
||||
Stopwatch sw = new Stopwatch();
|
||||
sw.Start();
|
||||
List<VocabolarioModel>? result = new List<VocabolarioModel>();
|
||||
// cerco in redisConn...
|
||||
// cerco in _redisConn...
|
||||
string currKey = $"{redisBaseKey}:Voc";
|
||||
RedisValue rawData = await redisDb.StringGetAsync(currKey);
|
||||
RedisValue rawData = await _redisDb.StringGetAsync(currKey);
|
||||
//if (!string.IsNullOrEmpty($"{rawData}"))
|
||||
if (rawData.HasValue)
|
||||
{
|
||||
@@ -188,7 +188,7 @@ namespace MP.Data.Services
|
||||
result = dbController.VocabolarioGetAll();
|
||||
// serializzo e salvo...
|
||||
rawData = JsonConvert.SerializeObject(result);
|
||||
redisDb.StringSet(currKey, rawData, UltraLongCache);
|
||||
_redisDb.StringSet(currKey, rawData, UltraLongCache);
|
||||
}
|
||||
if (result == null)
|
||||
{
|
||||
@@ -254,31 +254,31 @@ namespace MP.Data.Services
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Esegue flush memoria redisConn dato pat2Flush
|
||||
/// Esegue flush memoria _redisConn dato pat2Flush
|
||||
/// </summary>
|
||||
/// <param name="pat2Flush"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<bool> ExecFlushRedisPattern(RedisValue pat2Flush)
|
||||
{
|
||||
bool answ = false;
|
||||
var masterEndpoint = redisConn.GetEndPoints()
|
||||
.Where(ep => redisConn.GetServer(ep).IsConnected && !redisConn.GetServer(ep).IsReplica)
|
||||
var masterEndpoint = _redisConn.GetEndPoints()
|
||||
.Where(ep => _redisConn.GetServer(ep).IsConnected && !_redisConn.GetServer(ep).IsReplica)
|
||||
.FirstOrDefault();
|
||||
|
||||
// sepattern è "*" elimino intero DB...
|
||||
if (masterEndpoint != null && (pat2Flush.Equals(new RedisValue("*")) || pat2Flush == RedisValue.Null))
|
||||
{
|
||||
redisConn.GetServer(masterEndpoint).FlushDatabase(database: redisDb.Database);
|
||||
_redisConn.GetServer(masterEndpoint).FlushDatabase(database: _redisDb.Database);
|
||||
}
|
||||
else
|
||||
{
|
||||
var server = redisConn.GetServer(masterEndpoint);
|
||||
var keys = server.Keys(database: redisDb.Database, pattern: pat2Flush, pageSize: 1000);
|
||||
var server = _redisConn.GetServer(masterEndpoint);
|
||||
var keys = server.Keys(database: _redisDb.Database, pattern: pat2Flush, pageSize: 1000);
|
||||
|
||||
var deleteTasks = new List<Task>();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
deleteTasks.Add(redisDb.KeyDeleteAsync(key));
|
||||
deleteTasks.Add(_redisDb.KeyDeleteAsync(key));
|
||||
if (deleteTasks.Count >= 1000)
|
||||
{
|
||||
await Task.WhenAll(deleteTasks);
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
namespace MP.Data.Services.Utils
|
||||
using EgwCoreLib.Utils;
|
||||
using MP.Data.DbModels.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MP.Data.Services.Utils
|
||||
{
|
||||
public interface IStatsAggrService
|
||||
{
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
namespace MP.Data.Services.Utils
|
||||
using EgwCoreLib.Utils;
|
||||
using MP.Data.DbModels.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MP.Data.Services.Utils
|
||||
{
|
||||
public interface IStatsDetailService
|
||||
{
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
namespace MP.Data.Services.Utils
|
||||
using EgwCoreLib.Utils;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MP.Data.DbModels.Utils;
|
||||
using MP.Data.Repository.Utils;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MP.Data.Services.Utils
|
||||
{
|
||||
public class StatsAggrService : BaseServ, IStatsAggrService
|
||||
{
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
namespace MP.Data.Services.Utils
|
||||
using EgwCoreLib.Utils;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MP.Data.DbModels.Utils;
|
||||
using MP.Data.Repository.Utils;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
namespace MP.Data.Services.Utils
|
||||
{
|
||||
public class StatsDetailService : BaseServ, IStatsDetailService
|
||||
{
|
||||
|
||||
+10
-3
@@ -1,5 +1,7 @@
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using MP.Data;
|
||||
using MP.Data.Repository.Utils;
|
||||
using MP.Data.Services.Utils;
|
||||
using MP.IOC.Data;
|
||||
@@ -60,11 +62,16 @@ logger.Info("YARP reverse proxy configured");
|
||||
builder.Services.AddSingleton<PreserveBodyTransformer>();
|
||||
builder.Services.AddSingleton<RouteStatsManager>();
|
||||
builder.Services.AddHostedService<MetricsFlushService>();
|
||||
builder.Services.AddHostedService<MetricsDbFlushService>();
|
||||
|
||||
// da rivedere
|
||||
#if false
|
||||
builder.Services.AddHostedService<MetricsDbFlushService>();
|
||||
#endif
|
||||
|
||||
// MP.Data DbContext for Stats repositories
|
||||
builder.Services.AddDbContextFactory<DataLayerContext>(options =>
|
||||
options.UseSqlServer(builder.Configuration.GetConnectionString("MP.Utils")));
|
||||
string utilsConnString = builder.Configuration.GetConnectionString("MP.Utils") ?? "Server=localhost;Database=MoonPro_Utils; integrated security=True; MultipleActiveResultSets=True; App=MP.IOC;";
|
||||
builder.Services.AddDbContextFactory<MoonPro_UtilsContext>(options =>
|
||||
options.UseSqlServer(utilsConnString));
|
||||
|
||||
// MP.Data Services Utils - Statistiche DB
|
||||
builder.Services.AddScoped<IStatsAggrRepository, StatsAggrRepository>();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MP-IOC </i>
|
||||
<h4>Versione: 6.16.2604.410</h4>
|
||||
<h4>Versione: 6.16.2604.710</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
6.16.2604.410
|
||||
6.16.2604.710
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>6.16.2604.410</version>
|
||||
<version>6.16.2604.710</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>
|
||||
|
||||
@@ -0,0 +1,372 @@
|
||||
using MP.Data;
|
||||
using Newtonsoft.Json;
|
||||
using NLog;
|
||||
using StackExchange.Redis;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace MP.IOC.Services
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Classe base per i servizi che fornisce funzionalità comuni come
|
||||
/// - connessione a Redis
|
||||
/// - configurazione
|
||||
/// - gestione dei messaggi
|
||||
/// - strategie di caching.
|
||||
///
|
||||
/// Questa classe agisce come modello per altri servizi derivati.
|
||||
/// </summary>
|
||||
public class BaseServ
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Inizializza una nuova istanza della classe BaseServ.
|
||||
/// Configura la connessione Redis, carica le impostazioni di configurazione e inizializza il serializzatore JSON.
|
||||
/// </summary>
|
||||
/// <param name="Configuration">Oggetto di configurazione per recuperare le impostazioni dell'applicazione.</param>
|
||||
/// <param name="RedisConn">Multiplexer di connessione Redis per operazioni sul database.</param>
|
||||
public BaseServ(IConfiguration Configuration, IConnectionMultiplexer RedisConn)
|
||||
{
|
||||
_config = Configuration;
|
||||
_redisConn = RedisConn;
|
||||
_redisDb = _redisConn.GetDatabase();
|
||||
// configuro la base key x la cache Redis, con verifica contenga Cache finale
|
||||
_redisBaseKey = _config.GetValue<string>("ServerConf:RedisBaseKey") ?? "Lux:Cache";
|
||||
// aggiungo cache se non finisse per ":cache"
|
||||
if (!_redisBaseKey.EndsWith(":Cache"))
|
||||
{
|
||||
_redisBaseKey += ":Cache";
|
||||
}
|
||||
|
||||
// Configurazione serializzatore JSON per risolvere errore di loop circolare
|
||||
JSSettings = new JsonSerializerSettings()
|
||||
{
|
||||
ReferenceLoopHandling = ReferenceLoopHandling.Ignore
|
||||
};
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
|
||||
#region Public Properties
|
||||
|
||||
/// <summary>
|
||||
/// Pipe dei messaggi per la comunicazione riguardo update calcolo BOM
|
||||
/// </summary>
|
||||
public MessagePipe PipeBom { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Pipe dei messaggi per ritorno HwList da Engine di calcolo verso interfaccia utente.
|
||||
/// I messaggi vengono inviati sul canale Redis definito da ChannelHwList.
|
||||
/// </summary>
|
||||
public MessagePipe PipeHwList { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Pipe dei messaggi per ritorno HwOptions calcolate da Engine di calcolo verso interfaccia utente.
|
||||
/// I messaggi vengono inviati sul canale Redis definito da ChannelHwOpt.
|
||||
/// </summary>
|
||||
public MessagePipe PipeHwOpt { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Pipe dei messaggi per ritorno PNG calcolati da Engine di calcolo verso interfaccia utente.
|
||||
/// I messaggi vengono inviati sul canale Redis definito da ChannelPng.
|
||||
/// </summary>
|
||||
public MessagePipe PipePng { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Canale informazioni relativo ad attività relative alla gesitone PROD:
|
||||
/// - carico macchine
|
||||
/// - scheduling
|
||||
/// </summary>
|
||||
public MessagePipe PipeProd { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Pipe dei messaggi per ritorno info elementi del profile
|
||||
/// </summary>
|
||||
public MessagePipe PipeProfElement { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Pipe dei messaggi per ritorno ProfileListAsync calcolate da Engine di calcolo verso interfaccia utente.
|
||||
/// I messaggi vengono inviati sul canale Redis definito da ChannelProfList.
|
||||
/// </summary>
|
||||
public MessagePipe PipeProfList { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Pipe dei messaggi per ritorno Shape calcolate da Engine di calcolo verso interfaccia utente.
|
||||
/// I messaggi vengono inviati sul canale Redis definito da ChannelShape.
|
||||
/// </summary>
|
||||
public MessagePipe PipeShape { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Pipe dei messaggi per ritorno SVG calcolati da Engine di calcolo verso interfaccia utente.
|
||||
/// I messaggi vengono inviati sul canale Redis definito da ChannelSvg.
|
||||
/// </summary>
|
||||
public MessagePipe PipeSvg { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Pipe dei messaggi per la comunicazione riguardo update generico UI
|
||||
/// </summary>
|
||||
public MessagePipe PipeUpdate { get; set; } = null!;
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Fields
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto per collezione dati Activity (span in Uptrace)
|
||||
/// </summary>
|
||||
protected static readonly ActivitySource ActivitySource = new ActivitySource("Lux.DATA");
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto logger utilizzato per registrare eventi e errori a livello di classe.
|
||||
/// Utile per il monitoraggio del comportamento dell'applicazione e la risoluzione di problemi.
|
||||
/// </summary>
|
||||
protected static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto di configurazione statico per accedere alle impostazioni dell'applicazione (es. stringhe di connessione).
|
||||
/// Condiviso tra tutte le istanze di BaseServ.
|
||||
/// </summary>
|
||||
protected readonly IConfiguration _config = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Path base chiavi REDIS
|
||||
/// </summary>
|
||||
protected readonly string _redisBaseKey = "Lux:Cache";
|
||||
|
||||
/// <summary>
|
||||
/// Oggetto per la connessione a Redis utilizzato per operazioni di lettura/scrittura.
|
||||
/// </summary>
|
||||
protected readonly IConnectionMultiplexer _redisConn = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Database Redis utilizzato per le operazioni di lettura/scrittura
|
||||
/// nb: ottenuto tramite _redisConn.GetDatabase()
|
||||
/// </summary>
|
||||
protected readonly IDatabase _redisDb = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Abilitazione operazioni tracing generiche
|
||||
/// </summary>
|
||||
protected readonly bool _traceEnabled = false;
|
||||
|
||||
/// <summary>
|
||||
/// Impostazioni del serializzatore JSON utilizzato per gestire oggetti con riferimenti circolari
|
||||
/// (es. oggetti che si fanno riferimento reciprocamente).
|
||||
/// </summary>
|
||||
protected JsonSerializerSettings? JSSettings;
|
||||
|
||||
#endregion Protected Fields
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
/// <summary>
|
||||
/// Durata della cache breve (circa 1 minuto + variazione del +/-10%)
|
||||
/// </summary>
|
||||
protected TimeSpan FastCache
|
||||
{
|
||||
get => TimeSpan.FromSeconds(cacheTtlShort * rnd.Next(900, 1100) / 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Durata della cache lunga (+ variazione del +/-10%)
|
||||
/// </summary>
|
||||
protected TimeSpan LongCache
|
||||
{
|
||||
get => TimeSpan.FromSeconds(cacheTtlLong * rnd.Next(900, 1100) / 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Durata della cache molto breve (circa 10 secondi + variazione del +/-10%)
|
||||
/// </summary>
|
||||
protected TimeSpan UltraFastCache
|
||||
{
|
||||
get => TimeSpan.FromSeconds(cacheTtlShort / 6 * rnd.Next(900, 1100) / 1000);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Durata della cache molto lunga (+ variazione del +/-10%)
|
||||
/// </summary>
|
||||
protected TimeSpan UltraLongCache
|
||||
{
|
||||
get => TimeSpan.FromSeconds(cacheTtlLong * 10 * rnd.Next(900, 1100) / 1000);
|
||||
}
|
||||
|
||||
#endregion Protected Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
/// <summary>
|
||||
/// Helper avvio attività per la funzione tracciata
|
||||
/// </summary>
|
||||
/// <param name="methodName"></param>
|
||||
/// <returns></returns>
|
||||
protected static Activity? StartActivity([CallerMemberName] string? methodName = null)
|
||||
{
|
||||
var activity = ActivitySource.StartActivity(methodName ?? "UNDEF");
|
||||
activity?.SetTag("host.name", Environment.MachineName);
|
||||
return activity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invalida una o più chiavi/pattern in Redis
|
||||
/// </summary>
|
||||
protected async Task ClearCacheAsync(params string[] patterns)
|
||||
{
|
||||
foreach (var pattern in patterns)
|
||||
{
|
||||
// Chiamata al tuo metodo esistente
|
||||
await ExecFlushRedisPatternAsync((RedisValue)pattern);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Metodo di flush dati cache Redis
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task ExecFlushRedisPatternAsync(RedisValue pattern)
|
||||
{
|
||||
// Qui inserisci la tua logica attuale (es. via Lua script o Keys/Scan)
|
||||
// Esempio rapido via server scan:
|
||||
var endpoints = _redisConn.GetEndPoints();
|
||||
foreach (var endpoint in endpoints)
|
||||
{
|
||||
var server = _redisConn.GetServer(endpoint);
|
||||
await foreach (var key in server.KeysAsync(_redisDb.Database, pattern))
|
||||
{
|
||||
await _redisDb.KeyDeleteAsync(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper generale di lettura da cache o da funzione (DB) con caching successivo
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="factory"></param>
|
||||
/// <param name="expiration"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task<T> GetOrSetCacheAsync<T>(string key, Func<Task<T>> factory, TimeSpan? expiration = null, [CallerMemberName] string? caller = null)
|
||||
{
|
||||
using var activity = StartActivity();
|
||||
string source = "DB";
|
||||
|
||||
// 1. Provo Redis
|
||||
var cached = await _redisDb.StringGetAsync(key);
|
||||
if (cached.HasValue)
|
||||
{
|
||||
source = "REDIS";
|
||||
var cachedResult = JsonConvert.DeserializeObject<T>(cached!)!;
|
||||
|
||||
activity?.SetTag("data.source", source);
|
||||
LogTrace($"{source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms", NLog.LogLevel.Trace, caller);
|
||||
|
||||
return cachedResult;
|
||||
}
|
||||
|
||||
// 2. Chiamo il factory (DB)
|
||||
T result = await factory();
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
// 3. Salva in Redis per la prossima volta
|
||||
var serialized = JsonConvert.SerializeObject(result, JSSettings);
|
||||
await _redisDb.StringSetAsync(key, serialized, expiration ?? LongCache);
|
||||
}
|
||||
|
||||
// sistemo activity tracking data
|
||||
activity?.SetTag("data.source", source);
|
||||
activity?.Stop();
|
||||
|
||||
// log in console
|
||||
LogTrace($"GetOrSetCacheAsync | {source} | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds:N3}ms", NLog.LogLevel.Trace, caller);
|
||||
|
||||
return result!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper trace messaggio log (SE abilitato)
|
||||
/// </summary>
|
||||
/// <param name="traceMsg"></param>
|
||||
/// <param name="reqLevel"></param>
|
||||
/// <param name="methodName"></param>
|
||||
protected void LogTrace(string traceMsg, NLog.LogLevel? reqLevel = null, [CallerMemberName] string? methodName = null)
|
||||
{
|
||||
if (!_traceEnabled)
|
||||
return;
|
||||
|
||||
reqLevel ??= NLog.LogLevel.Debug;
|
||||
|
||||
// Loggo!
|
||||
Log.Log(reqLevel, $"{methodName} | {traceMsg}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Helper generale per la telemetria e gestione eccezioni
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="name"></param>
|
||||
/// <param name="body"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
protected async Task<T> TraceAsync<T>(string name, Func<Activity?, Task<T>> body, object? parameters = null)
|
||||
{
|
||||
using var activity = ActivitySource.StartActivity(name);
|
||||
try
|
||||
{
|
||||
if (parameters != null)
|
||||
{
|
||||
activity?.SetTag("params", JsonConvert.SerializeObject(parameters));
|
||||
}
|
||||
var result = await body(activity);
|
||||
activity?.SetStatus(ActivityStatusCode.Ok);
|
||||
activity?.Stop();
|
||||
LogTrace($"TraceAsync | trace: {activity?.TraceId} | {activity?.Duration.TotalMilliseconds}ms", methodName: name);
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
activity?.SetStatus(ActivityStatusCode.Error, ex.Message);
|
||||
//Log.Error(ex, "Errore in {MethodName}", name);
|
||||
LogTrace($"Errore in {name}", NLog.LogLevel.Error, name);
|
||||
throw; // Riesponi l'eccezione per il tracking globale
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private static readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new();
|
||||
|
||||
/// <summary>
|
||||
/// Durata della cache lunga in secondi (predefinito: 5 minuti)
|
||||
/// Utilizzato nella proprietà LongCache per definire quanto a lungo i dati devono essere memorizzati in cache.
|
||||
/// </summary>
|
||||
private int cacheTtlLong = 60 * 5;
|
||||
|
||||
/// <summary>
|
||||
/// Durata della cache breve in secondi (predefinito: 1 minuto)
|
||||
/// Utilizzato nelle proprietà FastCache e UltraFastCache per definire la durata della cache breve.
|
||||
/// </summary>
|
||||
private int cacheTtlShort = 60 * 1;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Generatore di numeri casuali utilizzato per introdurre variabilità dinamica nelle durate della cache
|
||||
/// (simula variazioni reali nella freschezza dei dati e nei tempi di scadenza).
|
||||
/// </summary>
|
||||
private Random rnd = new Random();
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Methods
|
||||
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
using MP.Data.DbModels.Utils;
|
||||
using MP.Data.Services.Utils;
|
||||
using NLog;
|
||||
using System.Globalization;
|
||||
|
||||
namespace MP.IOC.Services
|
||||
{
|
||||
@@ -29,7 +28,7 @@ namespace MP.IOC.Services
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
var interval = _config.GetValue<int>("RouteMan:FlushIntervalSeconds", FlushIntervalSeconds);
|
||||
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
@@ -51,7 +50,7 @@ namespace MP.IOC.Services
|
||||
public async Task FlushMetricsAsync()
|
||||
{
|
||||
var snapshot = _stats.Snapshot();
|
||||
|
||||
|
||||
if (snapshot.Count == 0)
|
||||
{
|
||||
return;
|
||||
@@ -61,7 +60,7 @@ namespace MP.IOC.Services
|
||||
{
|
||||
var utcNow = DateTime.Now;
|
||||
var hourStart = new DateTime(utcNow.Year, utcNow.Month, utcNow.Day, utcNow.Hour, 0, 0);
|
||||
|
||||
|
||||
var aggrRecords = new List<StatsAggregatedModel>();
|
||||
var detailRecords = new List<StatsDetailModel>();
|
||||
|
||||
@@ -82,8 +81,7 @@ namespace MP.IOC.Services
|
||||
RequestCount = count,
|
||||
AvgDuration = avgDuration,
|
||||
MaxDuration = totalMs,
|
||||
Perc05Duration = 0,
|
||||
Perc95Duration = 0,
|
||||
MinDuration = 0,
|
||||
NoReply = 0
|
||||
};
|
||||
|
||||
@@ -99,8 +97,7 @@ namespace MP.IOC.Services
|
||||
RequestCount = dest.Value,
|
||||
AvgDuration = totalMs / dest.Value,
|
||||
MaxDuration = totalMs,
|
||||
Perc05Duration = 0,
|
||||
Perc95Duration = 0,
|
||||
MinDuration = 0,
|
||||
NoReply = 0
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user