Premiere commit - Omika
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
// Probe LECTURE SEULE ciblé : chargement RendezVous du snapshot mobile (bulk).
|
||||
//
|
||||
// Reproduit EXACTEMENT le chemin du snapshot RendezVous corrigé :
|
||||
// MobileSnapshotService.LoadRendezVousAsync
|
||||
// -> IRendezVousService.SearchDetailsAsync
|
||||
// -> BatikRendezVousRepository.SearchDetailsAsync (bulk, pas de N+1)
|
||||
//
|
||||
// Le probe compte le nombre de requêtes HFSQL exécutées pour prouver que le
|
||||
// chargement est bulk (nombre borné, indépendant du nombre de rendez-vous) et
|
||||
// qu'aucun GetById par rendez-vous n'est émis.
|
||||
//
|
||||
// La quarantaine read-only des anomalies physiques (PL_DateFin < PL_DateDebut)
|
||||
// est appliquée par le repository AVANT le mapper : aucune DomainValidationException
|
||||
// ne doit être levée pour les 53 anomalies source.
|
||||
//
|
||||
// Aucune écriture. Aucun password affiché. Aucune donnée personnelle loggée.
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Omika.Core.Contracts.Common;
|
||||
using Omika.Core.Domain.Commands;
|
||||
using Omika.Core.Domain.Entities.Planning;
|
||||
using Omika.Core.Interfaces;
|
||||
|
||||
using Omika.Data.Configuration;
|
||||
|
||||
using Omika.Data.Connection;
|
||||
using Omika.Data.HfSql.Connection;
|
||||
using Omika.Data.HfSql.Metadata;
|
||||
using Omika.Data.Repositories.Batik;
|
||||
|
||||
// --- 1. Charger la configuration Hfsql (avec FilePasswords) ---
|
||||
var configPath = Path.GetFullPath(Path.Combine(
|
||||
Directory.GetCurrentDirectory(),
|
||||
"config", "hfsql-test.local.json"));
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddJsonFile(configPath, optional: false, reloadOnChange: false)
|
||||
.Build();
|
||||
|
||||
var hfsqlOptions = new HfsqlOptions();
|
||||
config.GetSection(HfsqlOptions.SectionName).Bind(hfsqlOptions);
|
||||
|
||||
Console.WriteLine($"=== Probe RendezVous Snapshot Bulk (base {hfsqlOptions.DatabaseName}) ===");
|
||||
Console.WriteLine();
|
||||
|
||||
// --- 2. Reproduire le mécanisme de l'API : BusinessOleDbConnection ---
|
||||
var factory = new HfSqlOleDbConnectionFactory();
|
||||
await using var business = new BusinessOleDbConnection(hfsqlOptions, factory);
|
||||
|
||||
// Wrapper compteur de requêtes HFSQL (lecture seule, délègue tout).
|
||||
var counting = new CountingHfsqlConnection(business);
|
||||
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
await counting.OpenAsync();
|
||||
Console.WriteLine("Connexion OLE DB ouverte (mécanisme API).");
|
||||
|
||||
// --- 3. Repository Batik + chemin bulk du snapshot RendezVous ---
|
||||
IRendezVousRepository repository = new BatikRendezVousRepository(counting);
|
||||
|
||||
var criteria = new RendezVousSearchCriteria
|
||||
{
|
||||
Page = 1,
|
||||
PageSize = 1000
|
||||
};
|
||||
|
||||
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
|
||||
PagedResult<RendezVous> result = await repository.SearchDetailsAsync(criteria, cts.Token);
|
||||
|
||||
sw.Stop();
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("--- Résultat ---");
|
||||
Console.WriteLine($"rendez-vous chargés (page) = {result.Items.Count}");
|
||||
Console.WriteLine($"total rendez-vous (SearchDetails) = {result.TotalCount}");
|
||||
Console.WriteLine($"requêtes HFSQL exécutées = {counting.QueryCount}");
|
||||
Console.WriteLine($"elapsed = {sw.ElapsedMilliseconds} ms");
|
||||
|
||||
// Vérification anti-N+1 : le nombre de requêtes doit être borné et très
|
||||
// inférieur au nombre de rendez-vous. Avec l'ancien pattern N+1, on aurait
|
||||
// observé ~1 requête par rendez-vous (timeout HTTP 30s).
|
||||
if (result.TotalCount > 0 && counting.QueryCount >= result.TotalCount)
|
||||
{
|
||||
Console.WriteLine("ATTENTION : le nombre de requêtes est >= au nombre de rendez-vous (pattern N+1 suspect).");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("OK : chargement bulk (requêtes << rendez-vous), aucun GetById par rendez-vous.");
|
||||
}
|
||||
|
||||
// Aucune DomainValidationException ne doit être levée : la quarantaine des
|
||||
// anomalies (PL_DateFin < PL_DateDebut) est appliquée AVANT le mapper.
|
||||
Console.WriteLine("OK : aucune DomainValidationException (quarantaine appliquée avant mapper).");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
sw.Stop();
|
||||
Console.WriteLine($"ERREUR: {ex.Message}");
|
||||
Console.WriteLine($"elapsed = {sw.ElapsedMilliseconds} ms");
|
||||
}
|
||||
|
||||
Console.WriteLine();
|
||||
Console.WriteLine("Probe RendezVous Snapshot Bulk terminé (lecture seule).");
|
||||
|
||||
/// <summary>
|
||||
/// Wrapper IHfsqlConnection qui délègue à la connexion réelle et compte les
|
||||
/// requêtes de lecture (QueryAsync). Lecture seule : aucune écriture.
|
||||
/// </summary>
|
||||
internal sealed class CountingHfsqlConnection : IHfsqlConnection
|
||||
{
|
||||
private readonly IHfsqlConnection _inner;
|
||||
public int QueryCount { get; private set; }
|
||||
|
||||
public CountingHfsqlConnection(IHfsqlConnection inner) => _inner = inner;
|
||||
|
||||
public HfsqlOptions Options => _inner.Options;
|
||||
public HfsqlConnectionState State => _inner.State;
|
||||
|
||||
public Task OpenAsync(CancellationToken ct = default) => _inner.OpenAsync(ct);
|
||||
|
||||
public Task CloseAsync() => _inner.CloseAsync();
|
||||
|
||||
|
||||
public Task<object?> ExecuteScalarAsync(string sql, IReadOnlyList<object?>? parameters = null, CancellationToken ct = default)
|
||||
=> _inner.ExecuteScalarAsync(sql, parameters, ct);
|
||||
|
||||
public Task<int> ExecuteNonQueryAsync(string sql, IReadOnlyList<object?>? parameters = null, CancellationToken ct = default)
|
||||
=> _inner.ExecuteNonQueryAsync(sql, parameters, ct);
|
||||
|
||||
public async Task<IReadOnlyList<IReadOnlyDictionary<string, object?>>> QueryAsync(
|
||||
string sql, IReadOnlyList<object?>? parameters = null, CancellationToken ct = default)
|
||||
{
|
||||
QueryCount++;
|
||||
return await _inner.QueryAsync(sql, parameters, ct);
|
||||
}
|
||||
|
||||
public Task<IReadOnlyList<HfsqlColumnMetadata>> GetColumnsAsync(string tableName, CancellationToken ct = default)
|
||||
=> _inner.GetColumnsAsync(tableName, ct);
|
||||
|
||||
public ValueTask DisposeAsync() => _inner.DisposeAsync();
|
||||
}
|
||||
Reference in New Issue
Block a user