Tutorial — aggiungere un campo a job.workers
Esempio job-specifico del pattern di estensione CRUD. Per i passi
comuni (sync DB, MCP generator, rigenerazione) vedi
Aggiungere un campo su inv.invoices.
Questa pagina mostra le specificità del dominio job.
🎯 Cosa fa
Esempio: aggiungere un campo emergencyContactPhone (numero di un
familiare o amico contattabile in emergenza) alla tabella
job.workers.
⚠️ Prerequisiti
Stessi del tutorial generale: ambiente sviluppo + DB locale + MCP generator + branch dedicato.
👣 Passi
1. Modifica job.workers
File: TrainingHub.Database/job/Tables/workers.sql
Aggiungi il campo accanto a phone:
CREATE TABLE [job].[workers] (
-- ... campi esistenti ...
[phone] NVARCHAR (1024) NULL,
[emergencyContactPhone] NVARCHAR (128) NULL,
-- ... altri campi ...
);
GO
EXECUTE sp_addextendedproperty @name = N'MS_Description',
@value = N'Numero telefonico da contattare in caso di emergenza',
@level0type = N'SCHEMA', @level0name = N'job',
@level1type = N'TABLE', @level1name = N'workers',
@level2type = N'COLUMN', @level2name = N'emergencyContactPhone';
GO
Considerazioni job-specifiche:
- Non toccare i campi chiave del cascade:
companyId,companyLocationId,roleId,riskLevelId,departmentId,academicQualificationId. Modifiche a questi triggerano cascade QueryModifier. - Non interferire con la computed column
label(derivata da firstName + lastName). - Indici: il nuovo campo non deve pesare sugli indici filtered
esistenti (
IX_workers_fiscalCode,IX_workers_active). Se il campo diventa frequentemente filtrato, valutare aggiunta indice dedicato.
2. Sincronizza il DB
Come altri tutorial.
3. conf.json — considerazioni job
File: TrainingHub.BackOffice/Components/CRUD/job/_conf/workers.dxgrid.conf.json
Per un campo contatto opzionale:
{
"Columns": {
"emergencyContactPhone": {
"Visible": false,
"FormInsertVisibility": "editable",
"FormUpdateVisibility": "editable"
}
},
"FormOrder": [
// ... altri campi ...
"phone",
"emergencyContactPhone",
// ...
]
}
Nascondi in grid di default (campi contatto spesso ingombranti) ma lascia editabile nel form.
4. Rigenera
Invoca mcp__3sd-generator__generator_run per workers. File
rigenerati:
TrainingHub.BackOffice/Components/CRUD/job/Worker.razor
TrainingHub.BackOffice/Components/CRUD/job/Worker.razor.tt.cs
TrainingHub.BackOffice/Components/CRUD/job/Forms/WorkerForm.razor
TrainingHub.BackOffice/Components/CRUD/job/Forms/WorkerForm.razor.tt.cs
Non devono cambiare, perché sono gli escape hatch scritti a mano:
Worker.razor.cs— code-behind della pagina: iniettaIRiskInheritanceServiceeITrainingRequirementsServiceper i pannelli rischi e formazione. (Il cascade FK azienda → sede/reparto non è qui: sta inForms/WorkerForm.razor.cs:14-31.)Forms/WorkerForm.razor.cs— cascade FK e derivazione del sesso dal CF.
Attenzione, invece, a queste tre: sono CRUD generati su vista,
con il proprio .razor.tt.cs e il proprio conf. Un generator_run le
riscrive, quindi non vanno modificate a mano ma rigenerate:
| Componente | Vista sorgente | Conf |
|---|---|---|
WorkerEffectiveRisk.razor | job.vw_workerEffectiveRisks | _conf/vw_workerEffectiveRisks.dxgrid.conf.json |
WorkersData.razor | job.vw_workersData | _conf/vw_workersData.dxgrid.conf.json |
WorkerJobHistory.razor | job.workerJobHistory | _conf/workerJobHistory.dxgrid.conf.json |
5. Aggiorna le viste analitiche (se rilevante)
WorkersData.razor è CRUD generato sulla vista job.vw_workersData:
il campo non si aggiunge al componente, si aggiunge alla vista SQL e
poi si rigenera (generator_run). Modificare il .razor a mano lo
espone a essere clobberato al primo rigenerato.
Per emergencyContactPhone non è rilevante (dato individuale), ma
lo sarebbe per campi aggregabili (es. anniDiServizio).
6. Considera impatto su cascade rischi
Per un campo puramente informativo come emergencyContactPhone:
- Non triggera cascade.
- Il
CompaniesQueryModifier(reg) continuerà a chiamareworker.UpdateRiskLevelal cambio azienda anche se il nuovo campo non è coinvolto, ma non è un problema: l'UPDATEporta il predicatoWHERE ISNULL(W.riskLevelId,-1) <> ISNULL(...)(Shared/DataLayer/job/workers.cs:28), quindi se nulla cambia non scrive righe. Costa una scansione, non una scrittura.
Per un campo che invece influenza il cascade (es. un nuovo
riskFactorId):
- Aggiornare la vista
job.vw_workerEffectiveRisks(è lì che vive il cascade) e, se serve,worker.UpdateRiskLevelper la colonna materializzata. NonIRiskInheritanceService, che è di sola lettura. - Aggiungere QueryModifier dedicato se serve hook su cambi.
- Ricalcolare
workers.riskLevelIdmaterializzato.
7. Considera impatto su workerJobHistory
Se il campo deve essere storicizzato (es. un dato che cambia nel tempo e serve audit):
- Aggiungi il campo anche a
workerJobHistorycon lo stesso tipo. - Aggiorna la logica di snapshot che popola
workerJobHistoryal cambio mansione per copiare anche il nuovo campo.
Per emergencyContactPhone non serve.
8. Considera impatto su import Excel
TrainingHub.Import importa anche workers. Il nuovo campo:
- Non viene popolato automaticamente — l'Excel deve avere la colonna e il mapping va aggiornato.
- Campi ottimali nullable (come questo) non rompono l'import esistente.
9. Build, test, commit
dotnet build TrainingHub.slnx
dotnet test TrainingHub.UnitTests
git add TrainingHub.Database/job/Tables/workers.sql
git commit -m "feat(db): add emergencyContactPhone to job.workers"
git add TrainingHub.BackOffice/Components/CRUD/job/_conf/workers.dxgrid.conf.json
git commit -m "feat(job): show emergencyContactPhone in worker form"
git add TrainingHub.BackOffice/Components/CRUD/job/Worker.razor \
TrainingHub.BackOffice/Components/CRUD/job/Worker.razor.tt.cs \
TrainingHub.BackOffice/Components/CRUD/job/Forms/WorkerForm.razor \
TrainingHub.BackOffice/Components/CRUD/job/Forms/WorkerForm.razor.tt.cs
git commit -m "regen job.workers"
⚡ Casi particolari job
- Nuovo campo cascade-sensitive. Se il campo ha impatto su
riskLevelIddel lavoratore (tipicamente improbabile — il cascade gira su rischi e ATECO, non su campi worker), aggiorna la vistavw_workerEffectiveRiskseworker.UpdateRiskLevel, poi valida il ricalcolo. - Nuovo campo su
jobs(mansione). Se toccariskLevelIdoinheritsCompanyRisks, servirà un refresh massivo diworkers.riskLevelId: costoso su dataset grandi. - Nuovo rischio. Aggiungere una voce a
risksè banale (label + id). Impatta i flussi di assegnazione e può richiedere aggiornamento di eventuali viste pre-calcolate su compliance. - Nuovo codice ATECO. ATECO ha
parentCode(gerarchia) + flagselectable: inserire come figlio di un codice esistente e impostareselectablecoerentemente. - Nuovo livello di rischio.
riskLevel.idè INT. Aggiungere un 4° livello (es. "Critico"): potrebbe richiedere aggiornamenti a tabelle conriskLevelIdnullable (oggi interpretato come "eredita parent") e a UI che mostrano livelli.
⚠️ Domande aperte
- Impatto su import Excel. Il mapping
TrainingHub.Importva aggiornato manualmente. Checklist dei mapping da toccare per ogni tabella modificata. - Discriminazione cascade per campo. Problema ricorrente
(già in
reg): modificare un campo non cascade-sensitive (es. note) triggera comunque refresh globale diworkers.riskLevelId. Ottimizzazione futura. -
workerJobHistorysincronizzato automaticamente. Se aggiungi un campo storicizzabile aworkers, la logica di snapshot va aggiornata in più punti (form custom, hook DB, trigger). Manca pattern centralizzato.
🔗 Vedi anche
- Aggiungere un campo
inv— tutorial generale - Aggiungere un campo
reg - Aggiungere un campo
edu - Panoramica dominio
- Logica applicativa — cascade rischi