Skip to content

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:

Image

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
1
DropDownList
ComboBoxEmployes
Contiene l'elenco dei nomi dei dipendenti
2
TextBox
TextBoxHeures
Numero di ore lavorate – numero effettivo
3
TextBox
TextBoxJours
Numero di giorni lavorati – numero intero
4
Pulsante
ButtonSalaire
Richiede il calcolo dello stipendio
5
TextBox
TextBoxErreur
Messaggio informativo destinato all'utente
ReadOnly=true, TextMode=MultiLine
6
Etichetta
LabelNom
Nome del dipendente selezionato al punto (1)
7
Etichetta
LabelPrénom
Nome del dipendente selezionato in (1)
8
Etichetta
LabelAdresse
Indirizzo
9
Etichetta
LabelVille
Città
10
Etichetta
LabelCP
Codice postale
11
Etichetta
LabelIndice
Indice
12
Etichetta
LabelCSGRDS
Aliquota contributiva CSGRDS
13
Etichetta
LabelCSGD
Aliquota contributiva CSGD
14
Etichetta
LabelRetraite
Aliquota contributiva pensione
15
Etichetta
LabelSS
Aliquota contributiva per la previdenza sociale
16
Etichetta
LabelSH
Salario orario base per l'indice indicato al punto (11)
17
Etichetta
LabelEJ
Indennità giornaliera di mantenimento per l'indice indicato al punto (11)
18
Etichetta
LabelRJ
Indennità giornaliera per i pasti relativa all'indice indicato al punto (11)
19
Etichetta
LabelCongés
Aliquota delle indennità di ferie pagate da applicare alla retribuzione base
20
Etichetta
LabelSB
Importo dello stipendio base
21
Etichetta
LabelCS
Importo dei contributi previdenziali da versare
22
Etichetta
LabelIE
Importo delle indennità per il mantenimento del figlio affidato
23
Etichetta
LabelIR
Importo delle indennità per i pasti del bambino affidato
24
Etichetta
LabelSN
Stipendio netto da corrispondere al dipendente

Il modulo contiene inoltre due contenitori di tipo [Panel]:

PanelErreurs
contiene il componente (5) TextBoxErreur
PanelSalaire
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
25
RequiredFieldValidator
RequiredFieldValidatorHeures
verifica che il campo [2] [TextBoxHeures] non sia vuoto
26
RangeValidator
RangeValidatorHeures
verifica che il campo [2] [TextBoxHeures] sia un numero reale compreso nell'intervallo [0, 200]
27
RequiredFieldValidator
RequiredFieldValidatorJours
verifica che il campo [3] [TextBoxJours] non sia vuoto
28
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:

Image


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].