4. L’applicazione [SimuPaie] – versione 1 – ASP.NET
4.1. Introduction
Desideriamo sviluppare un'applicazione .NET che consenta a un utente di effettuare simulazioni di calcolo delle retribuzioni delle assistenti materne dell'associazione «Maison de la petite enfance» di un comune.
Il modulo ASP.NET per il calcolo dello stipendio avrà il seguente aspetto:

L'applicazione ASP.NET avrà la seguente struttura:
![]() |
- Al momento della prima richiesta effettuata all'applicazione, viene istanziato un oggetto di tipo [Global] derivato dal tipo [System.Web.HttpApplication]. Sarà proprio questo oggetto a gestire il file di configurazione [web.config] dell’applicazione web e a memorizzare nella cache alcuni dati del database (operazione 0 sopra indicata).
- Al momento della prima richiesta (operazione 1) effettuata alla pagina [Default.aspx], che è l’unica pagina dell’applicazione, viene elaborato l’evento [Load]. In questa fase viene gestito il popolamento del menu a tendina dei dipendenti. I dati necessari per il menu a tendina vengono richiesti all’oggetto [Global], che li ha memorizzati nella cache. Si tratta dell’operazione 2 sopra indicata. L’operazione 4 invia all’utente la pagina così inizializzata.
- Quando viene richiesta la calcolo dello stipendio (operazione 1) alla pagina [Default.aspx], viene nuovamente elaborato l'evento [Load]. Non viene eseguita alcuna operazione poiché si tratta di una richiesta di tipo POST e il gestore [Pam_Load] è stato programmato per non eseguire alcuna operazione in questo caso (utilizzo del valore booleano IsPostBack). Una volta elaborato l'evento [Load], viene elaborato l'evento [Click] relativo al pulsante [Salaire]. Quest'ultimo necessita di dati che non sono stati memorizzati nella cache in [Global]. Pertanto, il gestore dell'evento li richiede al database. Si tratta dell'operazione 3 sopra indicata. Viene quindi effettuato il calcolo dello stipendio e l'operazione 4 invia i risultati all'utente.
4.2. Il progetto Visual Web Developer 2008
![]() |
- in [1], si crea un nuovo progetto
- in [2], di tipo [Web / Application Web ASP.NET]
- in [3], si assegna un nome al progetto
- in [4], si specifica il nome e in [5] la posizione. Verrà creata una cartella [c:\temp\pam-aspnet\pam-v1-adonet] per il progetto.
![]() |
- in [5], il progetto Visual Web Developer
- in [6], le proprietà del progetto (clic destro sul progetto / Proprietà / Applicazione).
- in [7], il nome dell’assembly che verrà generato dal progetto
- in [8], lo spazio dei nomi predefinito che desideriamo utilizzare per le classi del progetto. La classe [_Default] definita nei file [Default.aspx.cs] e [Default.aspx.designer.cs] è stata creata nello spazio dei nomi [pam_v1_adonet] derivato dal nome del progetto. È possibile modificare questo spazio dei nomi direttamente nel codice di questi due file:
[Default.aspx.cs]
using System;
....
namespace pam_v1
{
public partial class _Default : System.Web.UI.Page
{
[Default.aspx.designer.cs]
//------------------------------------------------------------------------------
// <generato automaticamente>
// Questo codice è stato generato da uno strumento.
// Versione del runtime: 2.0.50727.3603
//
// Le modifiche apportate a questo file potrebbero causare un comportamento errato e andranno perse se
// il codice viene rigenerato.
// </auto-generated>
//------------------------------------------------------------------------------
namespace pam_v1 {
public partial class _Default {
.........
Anche il codice HTML del file [Default.aspx] deve essere modificato:
![]() |
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="pam_v1._Default" %>
...
L'attributo Inherits sopra indicato fa riferimento alla classe definita nei file [Default.aspx.cs] e [Default.aspx.designer.cs]. In essi viene utilizzato lo spazio dei nomi pam_v1.
4.2.1. Il modulo [Default.aspx]
L'aspetto visivo del modulo [Default.aspx] è il seguente:
![]() |
I componenti sono i seguenti:
N. | Tipo | Nome | Ruolo |
DropDownList | ComboBoxEmployes | Contiene l'elenco dei nomi dei dipendenti | |
TextBox | TextBoxHeures | Numero di ore lavorate – numero effettivo | |
TextBox | TextBoxJours | Numero di giorni lavorati – numero intero | |
Pulsante | ButtonSalaire | Richiede il calcolo dello stipendio | |
TextBox | TextBoxErreur | Messaggio informativo destinato all'utente ReadOnly=true, TextMode=MultiLine | |
Etichetta | LabelNom | Nome del dipendente selezionato al punto (1) | |
Etichetta | LabelPrénom | Nome del dipendente selezionato in (1) | |
Etichetta | LabelAdresse | Indirizzo | |
Etichetta | LabelVille | Città | |
Etichetta | LabelCP | Codice postale | |
Etichetta | LabelIndice | Indice | |
Etichetta | LabelCSGRDS | Aliquota contributiva CSGRDS | |
Etichetta | LabelCSGD | Aliquota contributiva CSGD | |
Etichetta | LabelRetraite | Aliquota contributiva pensione | |
Etichetta | LabelSS | Aliquota contributiva per la previdenza sociale | |
Etichetta | LabelSH | Salario orario base per l'indice indicato al punto (11) | |
Etichetta | LabelEJ | Indennità giornaliera di mantenimento per l'indice indicato al punto (11) | |
Etichetta | LabelRJ | Indennità giornaliera per i pasti relativa all'indice indicato al punto (11) | |
Etichetta | LabelCongés | Aliquota delle indennità di ferie pagate da applicare alla retribuzione base | |
Etichetta | LabelSB | Importo dello stipendio base | |
Etichetta | LabelCS | Importo dei contributi previdenziali da versare | |
Etichetta | LabelIE | Importo delle indennità per il mantenimento del figlio affidato | |
Etichetta | LabelIR | Importo delle indennità per i pasti del bambino affidato | |
Etichetta | LabelSN | Stipendio netto da corrispondere al dipendente |
Il modulo contiene inoltre due contenitori di tipo [Panel]:
contiene il componente (5) TextBoxErreur | |
contiene i componenti da (6) a (24) |
Un componente di tipo [Panel] può essere reso visibile o meno tramite programmazione grazie alla sua proprietà booleana [Panel].Visible.
4.2.2. Verifica dei dati inseriti
Per calcolare uno stipendio, l’utente:
- seleziona un dipendente in [1]
- inserisce il numero di ore lavorate in [2]. Questo numero può essere decimale, ad esempio 2,5 per 2 ore e 30 minuti.
- inserisce il numero di giorni lavorati in [3]. Questo numero è intero.
- richiede lo stipendio tramite il pulsante [4]
![]() |
Quando l’utente inserisce dati errati in [2] e [3], questi vengono verificati non appena l’utente passa a un altro campo di immissione. Pertanto, la schermata riportata di seguito è stata acquisita prima ancora che l’utente facesse clic sul pulsante [Salaire]:
![]() |
Affinché si verifichi il comportamento descritto in precedenza, devono essere soddisfatte due condizioni:
- i componenti di convalida devono avere la proprietà EnableClientScript impostata su vero:
![]() |
- il browser che visualizza la pagina deve essere in grado di eseguire il codice JavaScript incorporato in una pagina HTML.
Se il browser client non verifica autonomamente la validità dei dati, questi saranno controllati solo quando il browser invierà i dati inseriti nel modulo al server. A quel punto, sarà il codice presente sul server, che elabora la richiesta del browser, a verificare la validità dei dati. Si ricorda che questa verifica deve essere sempre effettuata anche se la pagina visualizzata nel browser del cliente contiene codice JavaScript che esegue la stessa verifica. Infatti, il server non può essere certo che la richiesta POST che gli viene inviata provenga effettivamente da quella pagina e che quindi la verifica dei dati sia stata effettuata.
L’elenco dei validatori è il seguente:
N. | Tipo | Nome | Ruolo |
RequiredFieldValidator | RequiredFieldValidatorHeures | verifica che il campo [2] [TextBoxHeures] non sia vuoto | |
RangeValidator | RangeValidatorHeures | verifica che il campo [2] [TextBoxHeures] sia un numero reale compreso nell'intervallo [0, 200] | |
RequiredFieldValidator | RequiredFieldValidatorJours | verifica che il campo [3] [TextBoxJours] non sia vuoto | |
RangeValidator | RangeValidatorJours | verifica che il campo [3] [TextBoxJours] sia un numero intero compreso nell'intervallo [0,31] |
Compito da svolgere: costruire la pagina [Default.aspx]. Si posizioneranno innanzitutto i due contenitori [PanelErreurs] e [PanelSalaire] per poter poi inserirvi i componenti che devono contenere.
4.2.3. Le entità dell’applicazione
![]() |
Una volta lette, le righe delle tabelle [cotisations], [employes], [indemnites] verranno inserite in oggetti di tipo [Cotisations], [Employe] e [Indemnites] definiti come segue:
namespace Pam.Entites
{
public class Cotisations
{
// proprietà automatiche
public double CsgRds { get; set; }
public double Csgd { get; set; }
public double Secu { get; set; }
public double Retraite { get; set; }
// costruttori
public Cotisations()
{
}
public Cotisations(double csgRds, double csgd, double secu, double retraite)
{
CsgRds = csgRds;
Csgd = csgd;
Secu = secu;
Retraite = retraite;
}
// ToString
public override string ToString()
{
return string.Format("[{0},{1},{2},{3}]", CsgRds, Csgd, Secu, Retraite);
}
}
}
namespace Pam.Entites
{
public class Employe
{
// proprietà automatiche
public string SS { get; set; }
public string Nom { get; set; }
public string Prenom { get; set; }
public string Adresse { get; set; }
private string Ville { get; set; }
private string CodePostal { get; set; }
private int Indice { get; set; }
// costruttori
public Employe()
{
}
public Employe(string ss, string nom, string prenom, string adresse, string codePostal, string ville, int indice)
{
SS = ss;
Nom = nom;
Prenom = prenom;
Adresse = adresse;
CodePostal = codePostal;
Ville = ville;
Indice = indice;
}
// ToString
public override string ToString()
{
return string.Format("[{0},{1},{2},{3},{4},{5},{6}]", SS, Nom, Prenom, Adresse, Ville, CodePostal, Indice);
}
}
}
namespace Pam.Entites
{
public class Indemnites
{
// proprietà automatiche
public int Indice { get; set; }
public double BaseHeure { get; set; }
public double EntretienJour { get; set; }
public double RepasJour { get; set; }
public double IndemnitesCP { get; set; }
// costruttori
public Indemnites()
{
}
public Indemnites(int indice, double baseHeure, double entretienJour, double repasJour, double indemnitesCP)
{
Indice = indice;
BaseHeure = baseHeure;
EntretienJour = entretienJour;
RepasJour = repasJour;
IndemnitesCP = indemnitesCP;
}
// identità
public override string ToString()
{
return string.Format("[{0}, {1}, {2}, {3}, {4}]", Indice, BaseHeure, EntretienJour, RepasJour, IndemnitesCP);
}
}
}
4.2.4. Configurazione dell'applicazione
Il file [Web.config] che configura l'applicazione sarà il seguente:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
...
</configSections>
<connectionStrings>
<add name="dbpamSqlServer2005" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=C:\data\...\dbpam.mdf;User Id=sa;Password=msde;Connect Timeout=30;" providerName="System.Data.SqlClient"/>
</connectionStrings>
<appSettings>
<add key="selectEmploye" value="select NOM,PRENOM,ADRESSE,VILLE,CODEPOSTAL,INDICE from EMPLOYES where SS=@SS"/>
<add key="selectEmployes" value="select PRENOM, NOM, SS from EMPLOYES"/>
<add key="selectCotisations" value="select CSGRDS,CSGD,SECU,RETRAITE from COTISATIONS"/>
<add key="SelectIndemnites" value="select INDICE,BASEHEURE,ENTRETIENJOUR,REPASJOUR,INDEMNITESCP from INDEMNITES"/>
</appSettings>
<system.web>
<!--
Définissez compilation debug="true" pour insérer des symboles
de débogage dans la page compilée. Comme ceci
affecte les performances, définissez cette valeur à true uniquement
lors du développement.
-->
<compilation debug="false">
...
</configuration>
- riga 9: definisce la stringa di connessione al server SQL precedente
- righe 13-16: definiscono le query SQL utilizzate dall'applicazione per evitare di inserirle in modo statico nel codice.
- riga 13: la query SQL è configurata. La notazione del parametro è specifica del server SQL.
Compito da svolgere: inserire questi parametri nel file [Web.config]. La riga 9 dovrà essere adattata al percorso effettivo del database [dbpam.mdf].
4.2.5. Inizializzazione dell’applicazione
L'inizializzazione di un'applicazione ASP.NET avviene tramite il file [Global.asax.cs]. Quest'ultimo è strutturato come segue:
![]() |
- in [1], si aggiunge un nuovo elemento al progetto
- in [2], si aggiunge la classe dell'applicazione globale che per impostazione predefinita si chiama [Global.asax] [3]
![]() |
- in [4], il file [Global.asax] e la classe associata [Global.asax.cs]
- in [5], viene visualizzato il markup di [Global.asax]
<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v1.Global" Language="C#" %>
La classe [pam_v1.Global] è definita nel file [Global.asax.cs]. Ai fini del nostro problema, verrà definita come segue:
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using Pam.Entites;
namespace pam_v1
{
public class Global : System.Web.HttpApplication
{
// --- dati statici dell'applicazione ---
public static Employe[] Employes;
public static Cotisations Cotisations;
public static Dictionary<int, Indemnites> Indemnites = new Dictionary<int, Indemnites>();
public static string Msg = string.Empty;
public static bool Erreur = false;
public static string ConnectionString = null;
// avvio dell'applicazione
public void Application_Start(object sender, EventArgs e)
{
...
try
{
// connessione
ConnectionString = ...
using (SqlConnection connexion = new SqlConnection(ConnectionString))
{
connexion.Open();
// si recupera l'elenco dei dipendenti che viene inserito nell'array statico [Employes]
...
// si recuperano le aliquote contributive nella variabile statica [Cotisations]
...
// si recuperano le indennità nel dizionario statico [Indemnites]
...
// operazione riuscita
Msg = "Base chargée...";
}
}
catch (Exception ex)
{
// si registra l’errore
Msg = string.Format("L'erreur suivante s'est produite lors de l'accès à la base de données : {0}", ex);
Erreur = true;
}
}
}
}
- riga 20: il metodo [Application_Start] viene eseguito all'avvio dell'applicazione web. Viene eseguito una sola volta.
- righe 12-17: campi pubblici e statici della classe. Un campo statico è condiviso da tutte le istanze della classe. Pertanto, se vengono create più istanze della classe [Global], tutte condividono lo stesso campo statico [Employes], accessibile tramite il riferimento [Global.Employes], c.a.d. [NomDeClasse].ChampStatique. [Global] è il nome di una classe. Si tratta quindi di un tipo di dati. Questo nome è arbitrario. La classe deriva sempre da [System.Web.HttpApplication].
Torniamo alla nostra classe [Global]. Ci si potrebbe chiedere se sia necessario dichiararne statici i campi. In effetti, sembra che in alcuni casi si possano avere più istanze della classe [Global], il che giustifica la scelta di rendere statici i campi che devono essere condivisi da tutte queste istanze.
Esiste un altro modo per condividere dati con ambito “applicazione” tra le diverse pagine di un’applicazione web. Pertanto, l’array Employes dei dipendenti potrebbe essere memorizzato nella procedura Application_Start nel modo seguente:
Application.Add("employes",Employes);
[Application] è, per impostazione predefinita in qualsiasi applicazione, un riferimento a un'istanza della classe definita da ASP.NET. [Global.asax.cs] è un contenitore in grado di memorizzare oggetti di qualsiasi tipo. L'array Employes potrebbe quindi essere recuperato nel codice di una qualsiasi pagina dell'applicazione web nel modo seguente:
Employe[] employes=Application.Get["employes"] as Employe[];
Poiché il contenitore Application memorizza oggetti di qualsiasi tipo, si recupera un tipo Object che deve poi essere convertito. Questo metodo è sempre utilizzabile, ma la condivisione dei dati tramite campi statici tipizzati dell'oggetto [Global] evita le conversioni di tipo e consente al compilatore di effettuare verifiche di tipo che aiutano lo sviluppatore. È questo il metodo che verrà utilizzato in questo caso.
I dati condivisi da tutti gli utenti sono i seguenti:
- riga 12: l’array di oggetti di tipo [Employe] che memorizzerà l’elenco semplificato (SS, NOM, PRENOM) di tutti i dipendenti
- riga 13: l’oggetto di tipo [Cotisations] che memorizzerà le aliquote contributive
- riga 14: il dizionario che memorizzerà le indennità legate ai diversi indici dei dipendenti. Sarà indicizzato in base all’indice del dipendente e i suoi valori saranno di tipo [Indemnites]
- riga 15: un messaggio che indica l’esito dell’inizializzazione (riuscita o con errore)
- riga 16: un valore booleano che indica se l'inizializzazione si è conclusa con un errore o meno.
- riga 17: la stringa di connessione al database.
Domanda: completare il codice della classe [Application_Start].
4.3. Gli eventi del modulo [Default.aspx]
4.3.1. La procedura [Page_Load]
Quando il modulo [Default.aspx] viene caricato, cerca nell’array [Global.Employes] (riga 12) i nomi dei dipendenti per inserirli nell’elenco a discesa [1]:
![]() |
- l'elenco a discesa [1] è stato compilato
- il TextBox [5] indica che il database è stato letto correttamente
Se si sono verificati errori di inizializzazione all'avvio dell'applicazione, i codici TextBox e [5] lo segnalano:

Domanda: scrivere la procedura [Page_Load] della pagina web [Default.aspx] che, eseguita all’avvio dell’applicazione, garantisce il funzionamento descritto in precedenza.
4.3.2. Il calcolo dello stipendio
Facendo clic sul pulsante [4] si avvia l’esecuzione del gestore:
protected void ButtonSalaire_Click(object sender, System.EventArgs e)
Questo gestore inizia verificando la validità dei dati inseriti in [2] e [3]. Se uno dei due è errato, l’errore viene segnalato come illustrato in precedenza. Una volta verificati e ritenuti validi i dati inseriti in [2] e [3], l’applicazione deve visualizzare dati aggiuntivi sull’utente selezionato in [1], nonché il suo stipendio (cfr. schermata al paragrafo 4.1).
Domanda: scrivere il codice della procedura [ButtonSalaire_Click].











