Quando un utente prova a salvare un record con una chiave primaria (o un indice univoco) già esistente, il motore dati solleva un errore generico di tipo EDatabaseError. Mostrarlo così com'è all'utente — spesso un testo tecnico in inglese, dipendente dal driver e dalla lingua di sistema — non è quasi mai una buona esperienza. Conviene riconoscerlo specificamente e tradurlo in un messaggio comprensibile.
Il pattern classico: OnPostError
L'evento OnPostError del dataset (disponibile su TTable/TQuery di BDE, e sui dataset ADO) riceve l'eccezione originale e permette di decidere come proseguire tramite il parametro Action:
procedure TForm1.Table1PostError(DataSet: TDataSet; E: EDatabaseError;
var Action: TDataAction);
begin
if E.Errors[0].ErrorCode = DBIERR_KEYVIOL then
ShowMessage('Esiste già un record con questa chiave. Modifica il codice e riprova.')
else
ShowMessage(E.Message);
Action := daAbort; // annulla il posting: l'utente resta sul record da correggere
end;Il punto importante è il confronto su ErrorCode (una costante come DBIERR_KEYVIOL), non sul testo del messaggio: il testo cambia in base alla lingua del sistema operativo e alla versione del driver, mentre il codice numerico resta stabile.
Le tre opzioni di Action
Il parametro Action controlla cosa succede dopo l'evento, e vale la pena conoscere tutte e tre le opzioni:
daAbort— annulla il posting, il record resta in modifica: la scelta giusta per un errore che l'utente deve correggere manualmente (come una chiave duplicata).daRetry— ritenta l'operazione da capo: utile se l'errore può essere transitorio (es. un lock temporaneo rilasciato nel frattempo), non per errori di validazione dei dati.daAbortcon log — nella pratica, spesso conviene anche registrare l'errore originale (conE.Messagecompleto) in un log applicativo prima di mostrare il messaggio semplificato all'utente, per poter diagnosticare in seguito problemi meno ovvi.
L'equivalente moderno con FireDAC
Sui progetti più recenti basati su FireDAC, l'evento OnPostError esiste ancora ma è più comune intercettare l'eccezione direttamente in un blocco try..except attorno a Post, distinguendo il tipo di eccezione invece del codice numerico:
procedure TForm1.SalvaRecord;
begin
try
FDQuery1.Post;
except
on E: EFDDBEngineException do
begin
if E.Kind = ekUKViolated then // violazione di chiave univoca
ShowMessage('Esiste già un record con questi dati. Modifica e riprova.')
else
ShowMessage('Errore database: ' + E.Message);
FDQuery1.Cancel; // riporta il record allo stato precedente
end;
end;
end;EFDDBEngineException.Kind è una proprietà tipizzata (un'enumerazione) che FireDAC popola normalizzando l'errore specifico del driver sottostante (MySQL, SQL Server, SQLite...) in una categoria comune: è più portabile del vecchio confronto su codici numerici specifici di un singolo motore dati.
Il principio generale
Che si usi BDE, ADO o FireDAC, la regola resta la stessa: mai propagare all'utente finale un'eccezione tecnica così come arriva dal driver. Intercettarla, riconoscerne il tipo tramite un codice o una proprietà stabile (non il testo), e tradurla in un messaggio che spieghi cosa fare — nel caso della chiave duplicata, quasi sempre "cambia questo valore, esiste già".