refactor: Centralizzata logica Pre-Discovery in servizio dedicato
Creato AssociationService per eliminare duplicazione codice e migliorare manutenibilità
Nuovo servizio:
- Data_Coupler/Services/AssociationService.cs (276 righe)
* Interfaccia IAssociationService con metodi pubblici
* PreDiscoveryRequest DTO per parametri configurabili
* FindOrCreateAssociationAsync(): ricerca locale + Pre-Discovery REST
* IsPreDiscoveryAssociation(): verifica marker associazioni Pre-Discovery
Refactoring DataCoupler.razor.cs:
- Injected IAssociationService nel componente
- StartDataTransferOriginal(): ridotto da 98 a 20 righe (-78)
- StartDataTransferWithComposite(): ridotto da 93 a 20 righe (-73)
- Verifica Pre-Discovery: ridotto da 20 a 2 righe (-18)
- Sostituito logica inline con chiamate al servizio centralizzato
Refactoring ScheduledProfileExecutionService.cs:
- Injected IAssociationService nel costruttore
- ExecuteDataTransferWithCompositeAsync(): ridotto da 99 a 20 righe (-79)
- Verifica Pre-Discovery: ridotto da 20 a 2 righe (-18)
- Parametro IsScheduledTransfer=true per tracciabilità
Dependency Injection:
- Registrato IAssociationService in Program.cs come Scoped
- Disponibile per dependency injection in tutti i componenti
Vantaggi:
- Eliminata duplicazione: 3 implementazioni → 1 servizio centralizzato
- Codice ridotto di 266 righe (330 → 64 nelle chiamate)
- Manutenibilità: modifiche future in un solo file
- Testabilità: interfaccia facilmente mockabile per unit test
- Riusabilità: servizio disponibile per futuri componenti
- Separazione responsabilità: logica associazioni isolata
Comportamento invariato:
- Nessuna modifica alla logica Pre-Discovery esistente
- Compatibilità completa con database e API
- Stessi marker e metadata nelle associazioni create
Docs: PRE_DISCOVERY_REFACTORING.md
Build: ✅ Successo (0 errori, 25 warning pre-esistenti)
This commit is contained in:
@@ -27,6 +27,7 @@ public class ScheduledProfileExecutionService : IScheduledProfileExecutionServic
|
||||
private readonly ICredentialService _credentialService;
|
||||
private readonly IDataConnectionCredentialService _dataConnectionCredentialService;
|
||||
private readonly IKeyAssociationService _keyAssociationService;
|
||||
private readonly IAssociationService _associationService;
|
||||
private readonly ILogger<ScheduledProfileExecutionService> _logger;
|
||||
|
||||
public ScheduledProfileExecutionService(
|
||||
@@ -35,6 +36,7 @@ public class ScheduledProfileExecutionService : IScheduledProfileExecutionServic
|
||||
ICredentialService credentialService,
|
||||
IDataConnectionCredentialService dataConnectionCredentialService,
|
||||
IKeyAssociationService keyAssociationService,
|
||||
IAssociationService associationService,
|
||||
ILogger<ScheduledProfileExecutionService> logger)
|
||||
{
|
||||
_profileService = profileService;
|
||||
@@ -42,6 +44,7 @@ public class ScheduledProfileExecutionService : IScheduledProfileExecutionServic
|
||||
_credentialService = credentialService;
|
||||
_dataConnectionCredentialService = dataConnectionCredentialService;
|
||||
_keyAssociationService = keyAssociationService;
|
||||
_associationService = associationService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
@@ -534,121 +537,34 @@ public class ScheduledProfileExecutionService : IScheduledProfileExecutionServic
|
||||
}
|
||||
}
|
||||
|
||||
// 🔍 PRE-DISCOVERY: Se non esiste associazione, cerca nella destinazione
|
||||
// 🔍 PRE-DISCOVERY: Usa il servizio centralizzato
|
||||
if (existingAssociation == null && !string.IsNullOrEmpty(profile.SourceKeyField))
|
||||
{
|
||||
_logger.LogInformation("PRE-DISCOVERY SCHEDULED: Nessuna associazione trovata per '{KeyValue}'. Cerco nella destinazione...", sourceKey);
|
||||
|
||||
// Cerca il campo destinazione mappato al campo chiave sorgente
|
||||
if (fieldMappings.TryGetValue(profile.SourceKeyField, out var mappedDestinationFieldName))
|
||||
var preDiscoveryRequest = new PreDiscoveryRequest
|
||||
{
|
||||
try
|
||||
{
|
||||
// Prepara i campi di ricerca: usa il campo mappato + il valore della chiave
|
||||
var searchFields = new Dictionary<string, object>
|
||||
{
|
||||
{ mappedDestinationFieldName, sourceKey }
|
||||
};
|
||||
SourceKey = sourceKey,
|
||||
SourceKeyField = profile.SourceKeyField,
|
||||
DestinationEntity = currentEntityName,
|
||||
CredentialName = currentCredentialName,
|
||||
DestinationKeyField = "Id",
|
||||
FieldMappings = fieldMappings,
|
||||
RestClient = restClient,
|
||||
CurrentDataHash = currentDataHash,
|
||||
EnablePreDiscovery = true,
|
||||
UseParallelMethod = true, // Usa metodi paralleli thread-safe
|
||||
IsScheduledTransfer = true,
|
||||
SourceType = profile.SourceType
|
||||
};
|
||||
|
||||
_logger.LogInformation("PRE-DISCOVERY SCHEDULED: Cerco in '{Entity}' dove {Field} = '{Value}'",
|
||||
currentEntityName, mappedDestinationFieldName, sourceKey);
|
||||
|
||||
// Cerca nella destinazione REST
|
||||
var existingEntities = await restClient.FindEntitiesByKeysAsync(
|
||||
currentEntityName, searchFields);
|
||||
|
||||
if (existingEntities != null && existingEntities.Count > 0)
|
||||
{
|
||||
// Trovato! Prendi il primo risultato
|
||||
var foundEntity = existingEntities[0];
|
||||
|
||||
// Estrai l'ID del record trovato
|
||||
var destinationId = foundEntity.ContainsKey("Id")
|
||||
? foundEntity["Id"]?.ToString()
|
||||
: foundEntity.ContainsKey("id")
|
||||
? foundEntity["id"]?.ToString()
|
||||
: null;
|
||||
|
||||
if (!string.IsNullOrEmpty(destinationId))
|
||||
{
|
||||
_logger.LogInformation("PRE-DISCOVERY SCHEDULED: ✅ Trovato record esistente! KeyValue: '{KeyValue}' -> DestinationId: '{DestinationId}'",
|
||||
sourceKey, destinationId);
|
||||
|
||||
// Crea l'associazione prima di procedere
|
||||
var newAssociation = new KeyAssociation
|
||||
{
|
||||
KeyValue = sourceKey,
|
||||
SourceKeyField = profile.SourceKeyField,
|
||||
DestinationKeyField = "Id",
|
||||
MappedDestinationField = mappedDestinationFieldName,
|
||||
DestinationEntity = currentEntityName,
|
||||
DestinationId = destinationId,
|
||||
RestCredentialName = currentCredentialName,
|
||||
CreatedAt = DateTime.UtcNow,
|
||||
LastVerifiedAt = DateTime.UtcNow,
|
||||
IsActive = true,
|
||||
Data_Hash = currentDataHash,
|
||||
AdditionalInfo = JsonSerializer.Serialize(new
|
||||
{
|
||||
CreatedBy = "PreDiscovery",
|
||||
DiscoveredAt = DateTime.UtcNow,
|
||||
MappingCount = fieldMappings.Count,
|
||||
ScheduledTransfer = true
|
||||
})
|
||||
};
|
||||
|
||||
// Salva l'associazione (metodo parallelo thread-safe)
|
||||
var associationId = await _dataConnectionCredentialService.SaveKeyAssociationParallelAsync(newAssociation);
|
||||
_logger.LogInformation("PRE-DISCOVERY SCHEDULED: Associazione creata con ID: {AssociationId}", associationId);
|
||||
|
||||
// Usa l'associazione appena creata per il resto del flusso
|
||||
existingAssociation = newAssociation;
|
||||
existingAssociation.Id = associationId;
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("PRE-DISCOVERY SCHEDULED: Record trovato ma senza ID valido per KeyValue: '{KeyValue}'", sourceKey);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation("PRE-DISCOVERY SCHEDULED: Nessun record esistente trovato per KeyValue: '{KeyValue}'", sourceKey);
|
||||
}
|
||||
}
|
||||
catch (Exception discEx)
|
||||
{
|
||||
_logger.LogWarning(discEx, "PRE-DISCOVERY SCHEDULED: Errore durante la ricerca nella destinazione per KeyValue: '{KeyValue}'", sourceKey);
|
||||
// Continua comunque, il record verrà creato normalmente
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogWarning("PRE-DISCOVERY SCHEDULED: Campo chiave '{SourceKeyField}' non trovato nei mappings. Skip discovery.", profile.SourceKeyField);
|
||||
}
|
||||
existingAssociation = await _associationService.FindOrCreateAssociationAsync(preDiscoveryRequest);
|
||||
}
|
||||
|
||||
if (existingAssociation != null && existingAssociation.IsActive)
|
||||
{
|
||||
// Verifica se l'associazione è stata creata dal Pre-Discovery
|
||||
var isPreDiscoveryAssociation = false;
|
||||
if (!string.IsNullOrEmpty(existingAssociation.AdditionalInfo))
|
||||
{
|
||||
try
|
||||
{
|
||||
var additionalInfo = JsonSerializer.Deserialize<Dictionary<string, object>>(existingAssociation.AdditionalInfo);
|
||||
if (additionalInfo != null && additionalInfo.ContainsKey("CreatedBy"))
|
||||
{
|
||||
var createdBy = additionalInfo["CreatedBy"]?.ToString();
|
||||
isPreDiscoveryAssociation = createdBy == "PreDiscovery";
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignora errori di parsing
|
||||
}
|
||||
}
|
||||
// 🔍 PRE-DISCOVERY: Usa il servizio per verificare se è un'associazione Pre-Discovery
|
||||
var isPreDiscoveryAssociation = _associationService.IsPreDiscoveryAssociation(existingAssociation);
|
||||
|
||||
// 🔍 PRE-DISCOVERY: Se l'associazione è stata appena creata dal Pre-Discovery, FORZA l'aggiornamento
|
||||
// Se l'associazione è stata appena creata dal Pre-Discovery, FORZA l'aggiornamento
|
||||
if (isPreDiscoveryAssociation)
|
||||
{
|
||||
// Forza aggiornamento senza controllo hash
|
||||
|
||||
Reference in New Issue
Block a user