// Probe LECTURE SEULE ciblé RendezVous. // Reproduit EXACTEMENT le mécanisme de connexion de l'API // (BusinessOleDbConnection -> HfSqlOleDbConnectionFactory -> BuildExtendedPropertiesFromFilePasswords) // pour lire Planning_RDV sur BATIK_TEST et identifier les lignes où PL_DateFin < PL_DateDebut. // // Aucune écriture. Aucun password affiché. Aucune donnée personnelle loggée // (nom client, personne, adresse, notes, téléphone, email). // Seuls PL_PKID (identifiant technique) et les valeurs brutes DATE/HEURE sont affichés. using System.Globalization; using Microsoft.Extensions.Configuration; using Omika.Data.Configuration; using Omika.Data.Connection; using Omika.Data.HfSql.Connection; // --- 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 (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); var sw = System.Diagnostics.Stopwatch.StartNew(); int totalRows = 0; int countInvalid = 0; int countCrossMidnight = 0; int countAnomalyPure = 0; long? firstInvalidPk = null; DateTime? firstInvalidStart = null; DateTime? firstInvalidEnd = null; try { await business.OpenAsync(); Console.WriteLine("Connexion OLE DB ouverte (mécanisme API)."); // --- 3. Lire les colonnes DATE/HEURE de Planning_RDV --- // Même périmètre que le repository (SearchAsync) : toutes les lignes. string sql = "SELECT PL_PKID, PL_DateDebut, PL_DateFin FROM Planning_RDV"; using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); var rows = await business.QueryAsync(sql, cancellationToken: cts.Token); totalRows = rows.Count; Console.WriteLine($"rows lues = {totalRows}"); foreach (var row in rows) { long pk = ToLong(row, "PL_PKID"); DateTime start = ToDateTime(row, "PL_DateDebut"); DateTime end = ToDateTime(row, "PL_DateFin"); if (end < start) { countInvalid++; if (firstInvalidPk is null) { firstInvalidPk = pk; firstInvalidStart = start; firstInvalidEnd = end; } // Distinguer un éventuel rendez-vous à cheval sur minuit // (fin le lendemain du début) d'une anomalie pure (fin avant début). // Cross-midnight : la fin est le jour suivant le début. bool isCrossMidnight = end.Date == start.Date.AddDays(1); if (isCrossMidnight) { countCrossMidnight++; } else { countAnomalyPure++; } } } sw.Stop(); Console.WriteLine(); Console.WriteLine("--- Résultat ---"); Console.WriteLine($"rows = {totalRows}"); Console.WriteLine($"count_invalid (PL_DateFin < PL_DateDebut) = {countInvalid}"); Console.WriteLine($" dont cross-midnight (fin = lendemain du début) = {countCrossMidnight}"); Console.WriteLine($" dont anomalie pure (fin avant début, hors lendemain) = {countAnomalyPure}"); Console.WriteLine($"elapsed = {sw.ElapsedMilliseconds} ms"); if (firstInvalidPk is not null) { Console.WriteLine(); Console.WriteLine("--- Première ligne invalide (technique, non sensible) ---"); Console.WriteLine($"PL_PKID = {firstInvalidPk}"); Console.WriteLine($"PL_DateDebut (brut) = {firstInvalidStart:O}"); Console.WriteLine($"PL_DateFin (brut) = {firstInvalidEnd:O}"); Console.WriteLine($"start calculé = {firstInvalidStart:O}"); Console.WriteLine($"end calculé = {firstInvalidEnd:O}"); } else { Console.WriteLine("Aucune ligne invalide détectée."); } } catch (Exception ex) { sw.Stop(); Console.WriteLine($"ERREUR: {ex.Message}"); Console.WriteLine($"elapsed = {sw.ElapsedMilliseconds} ms"); } Console.WriteLine(); Console.WriteLine("Probe RendezVous terminé (lecture seule)."); // --- Helpers --- static long ToLong(IReadOnlyDictionary row, string column) { if (row.TryGetValue(column, out var value) && value is not null) { if (value is long l) return l; if (value is int i) return i; if (value is decimal d) return (long)d; if (long.TryParse(value.ToString(), NumberStyles.Integer, CultureInfo.InvariantCulture, out long parsed)) return parsed; } return 0; } static DateTime ToDateTime(IReadOnlyDictionary row, string column) { if (row.TryGetValue(column, out var value) && value is not null) { if (value is DateTime dt) return dt; if (DateTime.TryParse(value.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime parsed)) return parsed; } return default; }