6. Esempi
In questo capitolo ci proponiamo di illustrare quanto visto in precedenza attraverso una serie di esempi.
6.1. Esempio 1
6.1.1. Il problema
Questa applicazione deve consentire a un utente di calcolare la propria imposta. Consideriamo il caso semplificato di un contribuente che abbia da dichiarare solo il proprio stipendio (dati del 2004 relativi ai redditi del 2003):
- si calcola il numero di quote del dipendente nbParts=nbEnfants/2 +1 se non è sposato, nbEnfants/2+2 se è sposato, dove nbEnfants è il numero dei suoi figli.
- se ha almeno tre figli, ha diritto a una mezza quota in più
- si calcola il suo reddito imponibile R=0,72*S, dove S è il suo stipendio annuale
- si calcola il suo coefficiente familiare QF = R / nbParts
- si calcola la sua imposta I. Consideriamo la seguente tabella:
4262 | 0 | 0 |
8382 | 0,0683 | 291,09 |
14753 | 0,1914 | 1322,92 |
23.888 | 0,2826 | 2668,39 |
38.868 | 0,3738 | 4846,98 |
47932 | 0,4262 | 6883,66 |
0 | 0,4809 | 9505,54 |
Ogni riga contiene 3 campi. Per calcolare l'imposta I, si cerca la prima riga in cui QF <= campo1. Ad esempio, se QF = 5000, si troverà la riga
L'imposta I è quindi pari a 0,0683*R - 291,09*nbParts. Se QF è tale che la relazione QF<=campo1 non è mai soddisfatta, allora vengono utilizzati i coefficienti dell’ultima riga. In questo caso:
il che dà l’imposta I = 0,4809*R - 9505,54*nbParts.
6.1.2. La struttura MVC dell'applicazione
La struttura MVC dell’applicazione sarà la seguente:

Il ruolo del controller sarà svolto dalla pagina [main.aspx]. Saranno disponibili tre azioni:
- init: corrisponde alla prima richiesta del client. Il controller visualizzerà la vista [formulaire.aspx]
- calcul: corrisponde alla richiesta di calcolo dell’imposta. Se i dati del modulo di inserimento sono corretti, l’imposta viene calcolata tramite la classe di business [impots]. Il controller restituisce al client la vista [formulaire.aspx] così come era stata convalidata, con l’aggiunta dell’imposta calcolata. Se i dati del modulo di inserimento non sono corretti, il controller restituirà la vista [erreurs.aspx] con l’elenco degli errori e un link per tornare al modulo.
- ritorno: corrisponde al ritorno al modulo dopo un errore. Il controller visualizza la vista [formulaire.aspx] così come era stata convalidata prima dell’errore.
Il controller [main.aspx] non si occupa del calcolo delle imposte. Si limita a gestire il dialogo client-server e a far eseguire le azioni richieste dal client. Per l’azione [calcul], si avvarrà della classe di business [impot].
6.1.3. La classe di business
La classe «impostazioni fiscali» sarà definita come segue:
' spazi dei nomi importati
Imports System
' classe
Namespace st.istia.univangers.fr
Public Class impot
Private limites(), coeffR(), coeffN() As Decimal
' costruttore
Public Sub New(ByRef source As impotsData)
' i dati necessari per il calcolo dell'imposta
' provengono da una fonte esterna [source]
' vengono recuperati – potrebbe esserci un'eccezione
Dim data() As Object = source.getData
limites = CType(data(0), Decimal())
coeffR = CType(data(1), Decimal())
coeffN = CType(data(2), Decimal())
End Sub
' calcolo dell'imposta
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
' calcolo del numero di quote
Dim nbParts As Decimal
If marié Then
nbParts = CDec(nbEnfants) / 2 + 2
Else
nbParts = CDec(nbEnfants) / 2 + 1
End If
If nbEnfants >= 3 Then
nbParts += 0.5D
End If
' calcolo del reddito imponibile e del quoziente familiare
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' calcolo dell'imposta
limites((limites.Length - 1)) = QF + 1
Dim i As Integer = 0
While QF > limites(i)
i += 1
End While
Return CLng(revenu * coeffR(i) - nbParts * coeffN(i))
End Function
End Class
End Namespace
Un oggetto imposta viene creato fornendo al suo costruttore una fonte di dati di tipo [impotsData]. Questa classe dispone di un metodo pubblico [getData] che consente di ottenere i tre array di dati necessari al calcolo dell'imposta e che sono stati presentati in precedenza. Questo metodo è in grado di gestire un'eccezione qualora i dati non siano stati acquisiti o risultino errati. Una volta creato l'oggetto [impot], è possibile richiamare ripetutamente il suo metodo «calcolare», che calcola l'imposta del contribuente in base al suo stato civile (coniugato o meno), al numero di figli e al suo stipendio annuale.
6.1.4. La classe di accesso ai dati
La classe [impotsData] è la classe che consente di accedere ai dati. Si tratta di una classe astratta. È necessario creare una classe derivata per ogni nuova possibile fonte di dati (tabelle, file di testo, database, console, ...). La sua definizione è la seguente:
Imports System.Collections
Namespace st.istia.univangers.fr
Public MustInherit Class impotsData
Protected limites() As Decimal
Protected coeffr() As Decimal
Protected coeffn() As Decimal
Protected checked As Boolean
Protected valide As Boolean
' metodo di accesso ai dati
Public MustOverride Function getData() As Object()
' metodo di verifica dei dati
Protected Function checkData() As Integer
' verifica i dati acquisiti
' devono essere presenti dei dati
valide = Not limites Is Nothing AndAlso Not coeffr Is Nothing AndAlso Not coeffn Is Nothing
If Not valide Then Return 1
' devono esserci 3 tabelle della stessa dimensione
If valide Then valide = limites.Length = coeffr.Length AndAlso limites.Length = coeffn.Length
If Not valide Then Return 2
' gli array devono essere non vuoti
valide = limites.Length <> 0
If Not valide Then Return 3
' ogni tabella deve contenere elementi >=0 e in ordine crescente
valide = check(limites, limites.Length - 1) AndAlso check(coeffr, coeffr.Length) AndAlso check(coeffn, coeffn.Length)
If Not valide Then Return 4
' tutto è corretto
Return 0
End Function
' verifica la validità del contenuto di un array
Protected Function check(ByRef tableau() As Decimal, ByVal n As Integer) As Boolean
' l'array deve avere i primi n elementi >=0 e in ordine strettamente crescente
If tableau(0) < 0 Then Return False
For i As Integer = 1 To n - 1
If tableau(i) <= tableau(i - 1) Then Return False
Next
' va bene
Return True
End Function
End Class
End Namespace
La classe ha i seguenti attributi protetti:
tabella dei limiti delle fasce di imposta | |
tabella dei coefficienti applicati al reddito imponibile | |
tabella dei coefficienti applicati al numero di quote | |
valore booleano che indica se i dati (limiti, coeffr, coeffn) sono stati verificati | |
valore booleano che indica se i dati (limiti, coeffr, coeffn) sono validi |
La classe non ha un costruttore. Dispone di un metodo astratto [getData] che le classi derivate dovranno implementare. Questo metodo ha il compito di:
- assegnare valori ai tre array limiti, coeffr, coeffn
- generare un'eccezione se i dati non sono stati acquisiti o se risultano non validi.
La classe fornisce i metodi protetti [checkData] e [check] che verificano la validità degli attributi (limites, coeffr, coeffn). Ciò esonera le classi derivate dall’implementarli; dovranno semplicemente utilizzarli.
La prima classe derivata che utilizzeremo sarà la seguente:
Imports System.Collections
Imports System
Namespace st.istia.univangers.fr
Public Class impotsArray
Inherits impotsData
' costruttore senza argomenti
Public Sub New()
' inizializzazione degli array con costanti
limites = New Decimal() {4262D, 8382D, 14753D, 23888D, 38868D, 47932D, 0D}
coeffr = New Decimal() {0D, 0.0683D, 0.1914D, 0.2826D, 0.3738D, 0.4262D, 0.4809D}
coeffn = New Decimal() {0D, 291.09D, 1322.92D, 2668.39D, 4846.98D, 6883.66D, 9505.54D}
checked = True
valide = True
End Sub
' costruttore con tre array in ingresso
Public Sub New(ByRef limites() As Decimal, ByRef coeffr() As Decimal, ByRef coeffn() As Decimal)
' si memorizzano i dati
Me.limites = limites
Me.coeffr = coeffr
Me.coeffn = coeffn
checked = False
End Sub
Public Overrides Function getData() As Object()
' si verificano eventualmente i dati
Dim erreur As Integer
If Not checked Then erreur = checkData() : checked = True
' se non validi, si genera un'eccezione
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' altrimenti si restituiscono i tre array
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Questa classe, denominata [impotsArray], dispone di due costruttori:
- un costruttore senza argomenti che inizializza gli attributi (limiti, coeffr, coeffn) della classe base con array hardcoded
- un costruttore che inizializza gli attributi (limiti, coeffr, coeffn) della classe base con array passati come parametri
Il metodo [getData], che consentirà alle classi esterne di ottenere gli array (limiti, coeffr, coeffn), si limita a verificare la validità dei tre array utilizzando il metodo [checkData] della classe base. Genera un'eccezione se i dati non sono validi.
6.1.5. Test delle classi di business e delle classi di accesso ai dati
È importante includere in un’applicazione web solo classi di business e di accesso ai dati la cui correttezza sia stata certificata. In questo modo, la fase di debug dell’applicazione web potrà concentrarsi sulla parte relativa al controller e alle viste. Un programma di test potrebbe essere il seguente:
' opzioni
Option Strict On
Option Explicit On
' spazi dei nomi
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
Module test
Sub Main()
' programma interattivo per il calcolo delle imposte
' l'utente digita tre dati dalla tastiera: coniugato nbEnfants stipendio
' il programma visualizza quindi l'imposta da pagare
Const syntaxe As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"
' creazione di un oggetto «imposta»
Dim objImpôt As impot = Nothing
Try
objImpôt = New impot(New impotsArray)
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(1)
End Try
' ciclo infinito
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Long
While True
' vengono richiesti i parametri per il calcolo dell’imposta
Console.Out.Write("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :")
Dim paramètres As String = Console.In.ReadLine().Trim()
' C'è qualcosa da fare?
If paramètres Is Nothing OrElse paramètres = "" Then
Exit While
End If
' verifica del numero di argomenti nella riga inserita
Dim erreur As Boolean = False
Dim args As String() = paramètres.Split(Nothing)
Dim nbParamètres As Integer = args.Length
If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe)
erreur = True
End If
' verifica della validità dei parametri
If Not erreur Then
' sposato
marié = args(0).ToLower()
If marié <> "o" And marié <> "n" Then
erreur = True
End If
' nbEnfants
Try
nbEnfants = Integer.Parse(args(1))
If nbEnfants < 0 Then
Throw New Exception
End If
Catch
erreur = True
End Try
' stipendio
Try
salaire = Integer.Parse(args(2))
If salaire < 0 Then
Throw New Exception
End If
Catch
erreur = True
End Try
End If
' se i parametri sono corretti, si calcola l'imposta
If Not erreur Then
Console.Out.WriteLine(("impôt=" & objImpôt.calculer(marié = "o", nbEnfants, salaire) & " euro(s)"))
Else
Console.Error.WriteLine(syntaxe)
End If
End While
End Sub
End Module
End Namespace
L'applicazione chiede all'utente di digitare le tre informazioni necessarie per calcolare la propria imposta:
- il proprio stato civile: o per sposato, n per non sposato
- il numero di figli
- il suo stipendio annuale
Il calcolo dell’imposta viene effettuato tramite un oggetto di tipo [impot] creato all’avvio dell’applicazione:
' creazione di un oggetto imposta
Dim objImpôt As impot = Nothing
Try
objImpôt = New impot(New impotsArray)
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(1)
End Try
Come fonte di dati si utilizza un oggetto di tipo [impotsArray]. Viene utilizzato il costruttore senza argomenti di questa classe, che fornisce i tre array (limiti, coeffr, coeffn) con valori "fissi". La creazione di un oggetto [impot] può, in teoria, generare un'eccezione poiché, per crearsi, l'oggetto richiederà i dati (limiti, coeffr, coeffn) alla propria fonte di dati che gli è stata passata come parametro, e tale acquisizione di dati può far scattare un'eccezione. In questo caso, il metodo di acquisizione dei dati (codifica hardcoded) non può causare alcuna eccezione. Abbiamo tuttavia lasciato la gestione di quest’ultima per richiamare l’attenzione del lettore sulla possibilità che l’oggetto [impot] venga costruito in modo errato.
Ecco un esempio di esecuzione del programma precedente:
dos>dir
05/04/2004 13:28 1 337 impots.vb
21/04/2004 08:23 1 311 impotsArray.vb
21/04/2004 08:26 1 634 impotsData.vb
21/04/2004 08:42 2 490 testimpots1.vb
Compiliamo l'insieme delle classi [impot, impotsData, impotsArray] in un assembly [impot.dll]:
dos>vbc /t:library /out:impot.dll impotsData.vb impotsArray.vb impots.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4
dos>dir
05/04/2004 13:28 1 337 impots.vb
21/04/2004 08:23 1 311 impotsArray.vb
21/04/2004 08:26 1 634 impotsData.vb
21/04/2004 08:42 2 490 testimpots1.vb
21/04/2004 09:21 5 632 impot.dll
Compiliamo il programma di test:
dos>dir
05/04/2004 13:28 1 337 impots.vb
21/04/2004 08:23 1 311 impotsArray.vb
21/04/2004 08:26 1 634 impotsData.vb
21/04/2004 08:42 2 490 testimpots1.vb
21/04/2004 09:21 5 632 impot.dll
21/04/2004 09:23 4 608 testimpots1.exe
Possiamo eseguire i test:
dos>testimpots1
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 60000
impôt=4300 euro(s)
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :n 2 60000
impôt=6872 euro(s)
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :
6.1.6. Le viste dell'applicazione web
L'applicazione avrà due viste: [formulaire.aspx] e [erreurs.aspx]. Illustriamo il funzionamento dell'applicazione tramite alcune schermate. La vista [formulaire.aspx] viene visualizzata quando l'URL [main.aspx] viene richiesto per la prima volta:

L'utente compila il modulo:

e utilizza il pulsante [Calculer] per ottenere la seguente risposta:

Potrebbe commettere un errore nell'inserimento dei dati:

L'utilizzo del pulsante [Calculer] porta quindi a un'altra risposta [erreurs.aspx]:

Può utilizzare il link [Retour au formulaire] sopra riportato per tornare alla vista [formulaire.aspx] così come l'aveva confermata prima dell'errore:

6.1.7. La vista [formulaire.aspx]
La pagina [formulaire.aspx] sarà la seguente:
<%@ page src="formulaire.aspx.vb" inherits="formulaire" AutoEventWireup="false"%>
<html>
<head>
<title>Impôt</title>
</head>
<body>
<P>Calcul de votre impôt</P>
<HR>
<form method="post" action="main.aspx?action=calcul">
<TABLE border="0">
<TR>
<TD>Etes-vous marié(e)</TD>
<TD>
<INPUT type="radio" value="oui" name="rdMarie" <%=rdouichecked%>>Oui
<INPUT type="radio" value="non" name="rdMarie" <%=rdnonchecked%>>Non
</TD>
</TR>
<TR>
<TD>Nombre d'enfants</TD>
<TD><INPUT type="text" size="3" maxLength="3" name="txtEnfants" value="<%=txtEnfants%>"></TD>
</TR>
<TR>
<TD>Salaire annuel (euro)</TD>
<TD><INPUT type="text" maxLength="12" size="12" name="txtSalaire" value="<%=txtSalaire%>"></TD>
</TR>
<TR>
<TD>Impôt à payer :
</TD>
<TD><%=txtImpot%></TD>
</TR>
</TABLE>
<hr>
<P>
<INPUT type="submit" value="Calculer">
</P>
</form>
<form method="post" action="main.aspx?action=effacer">
<INPUT type="submit" value="Effacer">
</form>
</body>
</html>
I campi dinamici di questa pagina sono i seguenti:
"checked" se la casella [oui] deve essere selezionata, "" in caso contrario | |
idem per la casella [non] | |
valore da inserire nel campo di immissione [txtEnfants] | |
valore da inserire nel campo di immissione [txtSalaire] | |
valore da inserire nel campo di immissione [txtImpot] |
La pagina presenta due moduli, ciascuno dei quali contiene un pulsante [submit]. Il pulsante [Calculer] è il pulsante [submit] del modulo seguente:
<form method="post" action="main.aspx?action=calcul">
...
<P>
<INPUT type="submit" value="Calculer">
</P>
</form>
Si può notare che i parametri del modulo verranno inviati al controller con [action=calcul]. Il pulsante [Effacer] corrisponde al pulsante [submit] del modulo seguente:
<form method="post" action="main.aspx?action=effacer">
<INPUT type="submit" value="Effacer">
</form>
Si nota che i parametri del modulo verranno inviati al controller con [action=effacer]. In questo caso, il modulo non ha alcun parametro. È solo l'azione che conta.
I campi di [formulaire.aspx] vengono calcolati da [formulaire.aspx.vb]:
Imports System.Collections.Specialized
Public Class formulaire
Inherits System.Web.UI.Page
' campi della pagina
Protected rdouichecked As String
Protected rdnonchecked As String
Protected txtEnfants As String
Protected txtSalaire As String
Protected txtImpot As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' si recupera la richiesta precedente dal contesto
Dim form As NameValueCollection = Context.Items("formulaire")
' si prepara la pagina da visualizzare
' pulsanti di opzione
rdouichecked = ""
rdnonchecked = "checked"
If form("rdMarie").ToString = "oui" Then
rdouichecked = "checked"
rdnonchecked = ""
End If
' il resto
txtEnfants = CType(form("txtEnfants"), String)
txtSalaire = CType(form("txtSalaire"), String)
txtImpot = CType(Context.Items("txtImpot"), String)
End Sub
End Class
Il calcolo dei campi di [main.aspx] viene effettuato sulla base di due informazioni inserite dal controllore nel contesto della pagina:
- Context.Items("modulo"): dizionario di tipo NameValueCollection contenente i valori dei campi HTML [rdmarie,txtEnfants,txtSalaire]
- Context.Items("txtImpot"): valore dell'imposta
6.1.8. La vista [erreurs.aspx]
La vista [erreurs.aspx] è quella che visualizza gli eventuali errori che possono verificarsi durante il ciclo di vita dell’applicazione. Il suo codice di presentazione è il seguente:
<%@ page src="erreurs.aspx.vb" inherits="erreurs" AutoEventWireup="false"%>
<HTML>
<HEAD>
<title>Impôt</title>
</HEAD>
<body>
<P>Les erreurs suivantes se sont produites :</P>
<HR>
<ul>
<%=erreursHTML%>
</ul>
<a href="<%=href%>">
<%=lien%>
</a>
</body>
</HTML>
La pagina presenta tre campi dinamici:
codice HTML di un elenco di errori | |
URL di un link | |
testo del link |
Questi campi vengono calcolati dalla parte di controllo della pagina in [erreurs.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Public Class erreurs
Inherits System.Web.UI.Page
' parametro della pagina
Protected erreursHTML As String = ""
Protected href As String
Protected lien As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' si recuperano gli elementi del contesto
Dim erreurs As ArrayList = CType(context.Items("erreurs"), ArrayList)
href = context.Items("href").ToString
lien = context.Items("lien").ToString
' si genera il codice HTML dell'elenco
Dim i As Integer
For i = 0 To erreurs.Count - 1
erreursHTML += "<li> " + erreurs(i).ToString + "</li>" + ControlChars.CrLf
Next
End Sub
End Class
Il controller della pagina recupera le informazioni inserite dal controller dell'applicazione nel contesto della pagina:
oggetto ArrayList contenente l'elenco dei messaggi di errore da visualizzare | |
URL di un link | |
testo del link |
Ora che sappiamo cosa vede l'utente dell'applicazione, possiamo passare alla scrittura del controller dell'applicazione.
6.1.9. I controller [global.asax, main.aspx]
Ricordiamo lo schema MVC della nostra applicazione:

ClienteLogica applicativa
Il controller [main.aspx] deve gestire tre azioni:
- init: corrisponde alla prima richiesta del client. Il controller visualizza la vista [formulaire.aspx]
- calcolo: corrisponde alla richiesta di calcolo dell'imposta. Se i dati del modulo di inserimento sono corretti, l'imposta viene calcolata tramite la classe di business [impots]. Il controller restituisce al client la vista [formulaire.aspx] così come era stata convalidata, con l’aggiunta dell’imposta calcolata. Se i dati del modulo di inserimento non sono corretti, il controller restituisce la vista [erreurs.aspx] con l’elenco degli errori e un link per tornare al modulo.
- ritorno: corrisponde al ritorno al modulo dopo un errore. Il controller visualizza la vista [formulaire.aspx] così come era stata convalidata prima dell’errore.
Si sa inoltre che ogni richiesta all’applicazione transita attraverso il controller [global.asax], se presente. All’ingresso dell’applicazione abbiamo quindi una catena di due controller:
- [global.asax] che, in base all’architettura di ASP.NET, riceve tutte le richieste dirette all’applicazione
- [main.aspx] che, per scelta dello sviluppatore, riceve anch’esso tutte le richieste dirette all’applicazione
La necessità di [main.aspx] deriva dal fatto che dovremo gestire una sessione. Abbiamo visto che [global.asax] non era adatto come controller in questo caso. Qui potremmo fare completamente a meno di [global.asax]. Lo useremo comunque per eseguire del codice all’avvio dell’applicazione. Lo schema MVC sopra riportato mostra che dovremo creare un oggetto [impot] per calcolare l’imposta. È inutile crearlo più volte, ne basta uno solo. Lo creeremo quindi all’avvio dell’applicazione durante l’evento [Application_Start] gestito dal controller [global.asax]. Il codice di quest’ultimo è il seguente:
[global.asax]
[global.asax.vb]
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' si crea un oggetto import
Dim objImpot As impot
Try
objImpot = New impot(New impotsArray)
' si inserisce l'oggetto nell'applicazione
Application("objImpot") = objImpot
' nessun errore
Application("erreur") = False
Catch ex As Exception
'si è verificato un errore, lo si registra nell'applicazione
Application("erreur") = True
End Try
End Sub
End Class
Una volta creato, l'oggetto di tipo [impot] viene inserito nell'applicazione. È da lì che le diverse richieste dei vari clienti andranno a prelevarlo. Poiché la creazione dell’oggetto [impot] potrebbe non andare a buon fine, gestiamo l’eventuale eccezione e inseriamo una chiave [erreur] nell’applicazione per segnalare se si è verificato o meno un errore durante la creazione dell’oggetto [impot].
Il codice del controller [main.aspx, main.aspx.vb] sarà il seguente:
[main.aspx]
[main.aspx.vb]
Imports System
Imports System.Collections.Specialized
Imports System.Collections
Imports st.istia.univangers.fr
Public Class main
Inherits System.Web.UI.Page
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' prima di tutto, si verifica se l'applicazione è riuscita a inizializzarsi correttamente
If CType(Application("erreur"), Boolean) Then
' si reindirizza alla pagina degli errori
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible...")
context.Items("erreurs") = erreurs
context.Items("lien") = ""
context.Items("href") = ""
Server.Transfer("erreurs.aspx")
End If
' si recupera l'azione da eseguire
Dim action As String
If Request.QueryString("action") Is Nothing Then
action = "init"
Else
action = Request.QueryString("action").ToString.ToLower
End If
' si esegue l'azione
Select Case action
Case "init"
' inizializzazione dell'applicazione
initAppli()
Case "calcul"
' calcolo delle imposte
calculImpot()
Case "retour"
' ritorno al modulo
retourFormulaire()
Case "effacer"
' inizializzazione dell'applicazione
initAppli()
Case Else
' azione sconosciuta = inizializzazione
initAppli()
End Select
End Sub
Private Sub initAppli()
' visualizzazione del modulo precompilato
Context.Items("formulaire") = initForm()
Context.Items("txtImpot") = ""
Server.Transfer("formulaire.aspx", True)
End Sub
Private Function initForm() As NameValueCollection
' si inizializza il modulo
Dim form As New NameValueCollection
form.Set("rdMarie", "non")
form.Set("txtEnfants", "")
form.Set("txtSalaire", "")
Return form
End Function
Private Sub calculImpot()
' si verifica la validità dei dati inseriti
Dim erreurs As ArrayList = checkData()
' se ci sono errori, li si segnala
If erreurs.Count <> 0 Then
' si salvano i dati inseriti
Session.Item("formulaire") = Request.Form
' si prepara la pagina degli errori
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
context.Items("erreurs") = erreurs
Server.Transfer("erreurs.aspx")
End If
' in questo caso non ci sono errori: si calcola l'imposta
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
Request.Form("rdMarie") = "oui", _
CType(Request.Form("txtEnfants"), Integer), _
CType(Request.Form("txtSalaire"), Long))
' si visualizza la pagina dei risultati
context.Items("txtImpot") = impot.ToString + " euro(s)"
context.Items("formulaire") = Request.Form
Server.Transfer("formulaire.aspx", True)
End Sub
Private Sub retourFormulaire()
' si visualizza il modulo con i valori prelevati dalla sessione
Context.Items("formulaire") = Session.Item("formulaire")
Context.Items("txtImpot") = ""
Server.Transfer("formulaire.aspx", True)
End Sub
Private Function checkData() As ArrayList
' inizialmente nessun errore
Dim erreurs As New ArrayList
Dim erreur As Boolean = False
' pulsante di opzione «sposato»
Try
Dim rdMarie As String = Request.Form("rdMarie").ToString
If rdMarie <> "oui" And rdMarie <> "non" Then
Throw New Exception
End If
Catch
erreurs.Add("Vous n'avez pas indiqué votre statut marital")
End Try
' numero di figli
Try
Dim txtEnfants As String = Request.Form("txtEnfants").ToString
Dim nbEnfants As Integer = CType(txtEnfants, Integer)
If nbEnfants < 0 Then Throw New Exception
Catch
erreurs.Add("Le nombre d'enfants est incorrect")
End Try
' stipendio
Try
Dim txtSalaire As String = Request.Form("txtSalaire").ToString
Dim salaire As Integer = CType(txtSalaire, Long)
If salaire < 0 Then Throw New Exception
Catch
erreurs.Add("Le salaire annuel est incorrect")
End Try
' viene visualizzato l'elenco degli errori
Return erreurs
End Function
End Class
Il controller inizia verificando che l'applicazione sia stata inizializzata correttamente:
' prima di tutto, si verifica se l'applicazione è riuscita a inizializzarsi correttamente
If CType(Application("erreur"), Boolean) Then
' si reindirizza alla pagina degli errori
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible...")
context.Items("erreurs") = erreurs
context.Items("lien") = ""
context.Items("href") = ""
Server.Transfer("erreurs.aspx")
End If
Se il controller rileva che l'applicazione non è riuscita a inizializzarsi correttamente (non è stato possibile creare l'oggetto [impot] necessario per il calcolo), visualizza la pagina degli errori con i parametri adeguati. In questo caso, non è necessario inserire il link di ritorno al modulo poiché l'intera applicazione risulta indisponibile. Un messaggio di errore generico viene inserito in [Context.Items("erreurs")] di tipo [ArrayList].
Se il controller rileva che l'applicazione è operativa, analizza quindi l'azione che gli viene richiesto di eseguire tramite il parametro [action]. Abbiamo già incontrato spesso questa modalità di funzionamento. L'elaborazione di ciascun tipo di azione è delegata a una funzione.
6.1.9.1. Le azioni init, cancellare
Queste due azioni devono visualizzare il modulo di immissione vuoto. Ricordiamo che quest’ultimo (cfr. viste) ha due parametri:
- Context.Items("modulo"): dizionario di tipo [NameValueCollection] contenente i valori dei campi HTML [rdmarie,txtEnfants,txtSalaire]
- Context.Items("txtImpot"): valore dell’imposta
La funzione [initAppli] inizializza questi due parametri in modo da visualizzare un modulo vuoto.
6.1.9.2. L'azione di calcolo
Questa azione deve calcolare l’imposta da pagare in base ai dati inseriti nel modulo e restituire quest’ultimo precompilato con i valori inseriti e, inoltre, con l’importo dell’imposta calcolata. La funzione [calculImpot], incaricata di questo compito, inizia verificando che i dati del modulo siano corretti:
- il campo [rdMarie] deve essere presente e avere il valore [oui] o [non]
- il campo [txtEnfants] deve essere presente ed essere un numero intero >=0
- il campo [txtSalaire] deve essere presente ed essere un numero intero >=0
Se i dati inseriti risultano non validi, il controller visualizza la vista [erreurs.aspx] dopo aver inserito nel contesto i valori da essa previsti:
- i messaggi di errore vengono inseriti in un oggetto [ArrayList], oggetto che viene poi inserito nel contesto [Context.Items("erreurs")]
- anche l’URL del link di ritorno e il testo di tale link vengono inseriti nel contesto.
Prima di passare il controllo alla pagina [erreurs.aspx] che invierà la risposta al cliente, i valori inseriti nel modulo (Request.Form) vengono inseriti nella sessione, associati alla chiave "modulo". Ciò consentirà a una richiesta successiva di recuperarli.
A questo punto ci si può chiedere se sia utile verificare che i campi [rdMarie, txtEnfants, txtSalaire] siano presenti nella richiesta inviata dal client. È inutile se si è certi che il nostro client sia un browser che ha ricevuto la vista [formulaire.aspx] contenente tali campi. Non si può mai esserne certi. Mostreremo più avanti un esempio in cui il client è l’applicazione [curl] già incontrata. Interrogheremo l’applicazione senza inviare i campi che essa si aspetta e vedremo come reagirà. Si tratta di una regola già enunciata più volte e che ricordiamo qui: un’applicazione non deve mai formulare ipotesi sul tipo di client che la interroga. Per motivi di sicurezza, deve considerare che possa essere interrogata da un’applicazione programmata in grado di inviarle stringhe di parametri inaspettate. Deve comportarsi correttamente in ogni caso.
Nel nostro caso, abbiamo verificato che i campi [rdMarie, txtEnfants, txtSalaire] fossero presenti nella richiesta, ma non che questa potesse contenerne altri. In questa applicazione, tali campi verrebbero ignorati. Tuttavia, sempre per motivi di sicurezza, sarebbe opportuno registrare questo tipo di richiesta in un file di log e inviare un avviso all’amministratore dell’applicazione, affinché quest’ultimo sia a conoscenza del fatto che l’applicazione riceve richieste “insolite”. Analizzando tali richieste nel file di log, potrebbe individuare un eventuale attacco all’applicazione e adottare quindi le misure necessarie per proteggerla.
Se i dati attesi sono corretti, il controller avvia il calcolo dell’imposta con l’oggetto [impot] memorizzato nell’applicazione. Quindi memorizza nel contesto le due informazioni attese dalla vista [formulaire.aspx]:
- Context.Items("modulo"): dizionario di tipo [NameValueCollection] contenente i valori dei campi HTML, [rdmarie,txtEnfants,txtSalaire], in questo caso [Request.Form)] e c.a.d. i valori inseriti in precedenza nel modulo
- Context.Items("txtImpot"): valore dell’imposta appena calcolato
Il lettore attento potrebbe essersi posto una domanda leggendo quanto sopra: poiché l’oggetto [impot] creato all’avvio dell’applicazione è condiviso tra tutte le richieste, non potrebbero verificarsi conflitti di accesso che causino il danneggiamento dei dati dell’oggetto [impot]? Per rispondere a questa domanda, dobbiamo tornare al codice della classe [impot]. Le richieste utilizzano il metodo [impot].calculerImpot per ottenere l’imposta da pagare. È quindi questo il codice che dobbiamo esaminare:
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
' calcolo del numero di quote
Dim nbParts As Decimal
If marié Then
nbParts = CDec(nbEnfants) / 2 + 2
Else
nbParts = CDec(nbEnfants) / 2 + 1
End If
If nbEnfants >= 3 Then
nbParts += 0.5D
End If
' calcolo del reddito imponibile e del quoziente familiare
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' calcolo dell'imposta
limites((limites.Length - 1)) = QF + 1
Dim i As Integer = 0
While QF > limites(i)
i += 1
End While
Dim impot As Long = CLng(revenu * coeffR(i) - nbParts * coeffN(i))
Return impot
End Function
Supponiamo che un thread stia eseguendo il metodo precedente e venga interrotto. A quel punto, un altro thread esegue il metodo. Quali sono i rischi? Per scoprirlo, abbiamo aggiunto il seguente codice:
Dim impot As Long = CLng(revenu * coeffR(i) - nbParts * coeffN(i))
' si attende 10 secondi
Thread.Sleep(10000)
Return impot
Il thread 1, dopo aver calcolato il valore [impot1] della variabile locale [impot], viene interrotto. Il thread 2 viene quindi eseguito e calcola un nuovo valore [impot2] per la stessa variabile [impot] prima di essere interrotto. Il thread 1 riprende il controllo. Cosa trova nella variabile locale [impot]? Essendo questa variabile locale a un metodo, è memorizzata in una struttura di memoria chiamata stack. Questo stack fa parte del contesto del thread, che viene salvato quando quest’ultimo viene interrotto. Quando il thread 2 entra in esecuzione, il suo contesto viene inizializzato con un nuovo stack e quindi con una nuova variabile locale [impot]. Quando a sua volta il thread 2 verrà interrotto, anche il suo contesto verrà salvato. Quando il thread 1 viene riavviato, il suo contesto viene ripristinato, compreso il suo stack. Ritrova quindi la sua variabile locale [impot] e non quella del thread 2. Ci troviamo quindi in una situazione in cui non vi sono conflitti di accesso tra le richieste. I test effettuati con la pausa di 10 secondi sopra descritta hanno confermato che le richieste simultanee ottenevano effettivamente il risultato atteso.
6.1.9.3. L'azione di ritorno
Questa azione corrisponde all’attivazione del link [Retour vers le formulaire] dalla vista [erreurs.aspx] per tornare alla vista [formulaire.aspx] precompilata con i valori inseriti in precedenza e salvati nella sessione. La funzione [retourFormulaire] recupera queste informazioni. I due parametri richiesti dalla vista [formulaire.aspx] vengono inizializzati:
- Context.Items("modulo") con i valori inseriti in precedenza e salvati nella sessione
- Context.Items("txtImpot") con la stringa vuota
6.1.10. Test dell'applicazione web
Tutti i file precedenti sono inseriti in una cartella <application-path>.

All'interno di questa cartella viene creata una sottocartella denominata [bin], nella quale viene inserito l'assembly [impot.dll] ottenuto dalla compilazione dei file delle classi di business: [impots.vb, impotsData.vb, impotsArray.vb]. Di seguito viene riportato il comando di compilazione necessario:
dos>vbc /t:library /out:impot.dll impotsData.vb impotsArray.vb impots.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4
dos>dir
05/04/2004 13:28 1 337 impots.vb
21/04/2004 08:23 1 311 impotsArray.vb
21/04/2004 08:26 1 634 impotsData.vb
21/04/2004 09:21 5 632 impot.dll
Il file [impot.dll] sopra indicato deve essere inserito in <application-path>\bin affinché l'applicazione web possa accedervi. Il server Cassini viene avviato con i parametri (<application-path>,/impots1). Tramite un browser, richiediamo l'URL [http://localhost/impots1/main.aspx]:

Compiliamo il modulo:

Quindi avviamo il calcolo dell’imposta con il pulsante [Calculer]. Otteniamo la seguente risposta:

Quindi inseriamo dati errati:

Premendo il pulsante [Calculer] si ottiene la seguente risposta:

L'utilizzo del link [Retour au formulaire] ci riporta al modulo nello stato in cui si trovava al momento della convalida:

Infine, cliccando sul pulsante [Effacer] si reimposta la pagina:

6.1.11. Utilizzo del client [curl]
È importante testare le applicazioni web con client diversi dai browser. Se si invia a un browser un modulo con parametri da inviare al momento della convalida, il browser rinvierà i valori di tali parametri al server. Un altro client potrebbe non farlo e quindi il server riceverebbe una richiesta in cui mancherebbero alcuni parametri. Deve sapere come comportarsi in questo caso. Un altro esempio è quello delle verifiche dei dati inseriti effettuate sul lato client. Se il modulo contiene dati da verificare, tale verifica può essere effettuata sul lato client grazie a script inclusi nel documento contenente il modulo. Il browser invierà il modulo solo se tutti i dati verificati sul lato client sono validi. Si potrebbe quindi essere tentati, lato server, di dare per scontato che si riceveranno dati già verificati e di non voler eseguire questa verifica una seconda volta. Sarebbe un errore. Infatti, un client diverso da un browser potrebbe inviare al server dati non validi e l’applicazione web rischierebbe quindi di avere un comportamento imprevisto. Illustreremo questi punti utilizzando il client [curl].
Innanzitutto, richiediamo l’URL [http://localhost/impots1/main.aspx]:
dos>curl --include --url http://localhost/impots1/main.aspx
HTTP/1.1 200 OK
Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0
Date: Thu, 01 Apr 2004 15:18:10 GMT
Set-Cookie: ASP.NET_SessionId=ivthkl45tjdjrzznevqsf255; path=/
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 982
Connection: Close
<html>
<head>
<title>Impôt</title>
</head>
<body>
<P>Calcul de votre impôt</P>
<HR width="100%" SIZE="1">
<form method="post" action="main.aspx?action=calcul">
<TABLE border="0">
<TR>
<TD>Etes-vous marié(e)</TD>
<TD>
<INPUT type="radio" value="oui" name="rdMarie" >Oui <INPUT type="radio" value="non" name="rdMarie" checked>Non</TD>
</TR>
<TR>
<TD>Nombre d'enfants</TD>
<TD><INPUT type="text" size="3" maxLength="3" name="txtEnfants" value=""></TD>
</TR>
<TR>
<TD>Salaire annuel (euro)</TD>
<TD><INPUT type="text" maxLength="12" size="12" name="txtSalaire" value=""></TD>
</TR>
<TR>
<TD>Impôt à payer :
</TD>
<TD></TD>
</TR>
</TABLE>
<hr>
<P>
<INPUT type="submit" value="Calculer">
</P>
</form>
<form method="post" action="main.aspx?action=effacer">
<INPUT type="submit" value="Effacer">
</form>
</body>
</html>
Il server ci ha inviato il codice HTML del modulo. Nelle intestazioni HTTP è presente il cookie di sessione. Lo useremo nelle richieste successive per mantenere la sessione. Richiediamo l'azione [calcul] senza fornire parametri:
dos>curl --cookie ASP.NET_SessionId=ivthkl45tjdjrzznevqsf255 --include --url http://localhost/impots1/main.aspx?action=calcolo
HTTP/1.1 200 OK
Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0
Date: Thu, 01 Apr 2004 15:22:42 GMT
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 380
Connection: Close
<HTML>
<HEAD>
<title>Impôt</title>
</HEAD>
<body>
<P>Les erreurs suivantes se sont produites :</P>
<HR>
<ul>
<li> Vous n'avez pas indiqué votre statut marital</li>
<li> Le nombre d'enfants est incorrect</li>
<li> Le salaire annuel est incorrect</li>
</ul>
<a href="main.aspx?action=retour">
Retour au formulaire
</a>
</body>
</HTML>
Possiamo notare che l'applicazione web ha restituito la vista [erreurs] con tre messaggi di errore relativi ai tre parametri mancanti. Inviamo ora dei parametri errati:
dos>curl --cookie ASP.NET_SessionId=ivthkl45tjdjrzznevqsf255 --include --data rdMarie=xx --data txtEnfants=xx --data txtSalaire=xx --url http://localhost/impots1/main.aspx?action=calcul
HTTP/1.1 200 OK
Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0
Date: Thu, 01 Apr 2004 15:25:50 GMT
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 380
Connection: Close
<HTML>
<HEAD>
<title>Impôt</title>
</HEAD>
<body>
<P>Les erreurs suivantes se sont produites :</P>
<HR>
<ul>
<li> Vous n'avez pas indiqué votre statut marital</li>
<li> Le nombre d'enfants est incorrect</li>
<li> Le salaire annuel est incorrect</li>
</ul>
<a href="main.aspx?action=retour">
Retour au formulaire
</a>
</body>
</HTML>
I tre errori sono stati correttamente rilevati. Ora inviamo dei parametri validi:
dos>curl --cookie ASP.NET_SessionId=ivthkl45tjdjrzznevqsf255 --include --data rdMarie=oui --data txtEnfants=2 --data txtSalaire=60000 --url http://localhost/impots1/main.aspx?action=calcul
HTTP/1.1 200 OK
Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0
Date: Thu, 01 Apr 2004 15:28:24 GMT
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 1000
Connection: Close
<html>
<head>
<title>Impôt</title>
</head>
<body>
<P>Calcul de votre impôt</P>
<HR width="100%" SIZE="1">
<form method="post" action="main.aspx?action=calcul">
<TABLE border="0">
<TR>
<TD>Etes-vous marié(e)</TD>
<TD>
<INPUT type="radio" value="oui" name="rdMarie" checked>Oui <INPUT type="radio" value="non" name="rdMarie" >Non</TD>
</TR>
<TR>
<TD>Nombre d'enfants</TD>
<TD><INPUT type="text" size="3" maxLength="3" name="txtEnfants" value="2"></TD>
</TR>
<TR>
<TD>Salaire annuel (euro)</TD>
<TD><INPUT type="text" maxLength="12" size="12" name="txtSalaire" value="60000"></TD>
</TR>
<TR>
<TD>Impôt à payer :
</TD>
<TD>4300 euro(s)</TD>
</TR>
</TABLE>
<hr>
<P>
<INPUT type="submit" value="Calculer">
</P>
</form>
<form method="post" action="main.aspx?action=effacer">
<INPUT type="submit" value="Effacer">
</form>
</body>
</html>
Abbiamo calcolato correttamente l’imposta da versare: 4300 euro. Da questo esempio trarremo la conclusione che non bisogna lasciarsi ingannare dal fatto che si stia scrivendo un'applicazione web destinata a client che sono browser. Un'applicazione web è un servizio TCP/IP e questo protocollo di rete non permette di determinare la natura dell'applicazione client di un servizio. Pertanto, non è possibile sapere se il client di un'applicazione web sia un browser o meno. Seguiamo quindi due regole:
- alla ricezione di una richiesta da parte di un client, non si formulano ipotesi sul client e si verifica che i parametri previsti nella richiesta siano effettivamente presenti e validi
- si elabora una risposta destinata ai browser, quindi in genere documenti HTML
Un’applicazione web può essere progettata per servire contemporaneamente diversi tipi di client, ad esempio browser e telefoni cellulari. È quindi possibile includere in ogni richiesta un nuovo parametro che indichi il tipo di client. In questo modo, un browser richiederà il calcolo dell’imposta tramite una richiesta all’URL http://machine/impots/main.aspx?client=browser&action=calcolo, mentre il cellulare effettuerà una richiesta all’URL http://machine/impots/main.aspx?client=mobile&action=calcul. La struttura MVC semplifica la scrittura di un’applicazione di questo tipo. Il codice risulterà il seguente:

Il blocco [Classes métier, Classes d'accès aux données] rimane invariato. Si tratta infatti di una parte che non interessa al cliente. Il blocco [Contrôleur] subisce poche modifiche, ma deve tenere conto di un nuovo parametro nella richiesta, ovvero il parametro [client] che indica con quale tipo di cliente si ha a che fare. Il blocco [vues] deve generare viste per ogni tipo di cliente. Potrebbe essere interessante tenere conto, fin dalla fase di progettazione dell’applicazione, della presenza del parametro [client] nella richiesta, anche se l’obiettivo a breve o medio termine riguarda solo i browser. Se in futuro l’applicazione dovesse gestire un nuovo tipo di client, sarà sufficiente scrivere solo le viste adatte a quest’ultimo.
6.2. Esempio 2
6.2.1. Il problema
In questa sede ci proponiamo di affrontare lo stesso problema di prima, ma modificando la fonte dei dati dell’oggetto [impot] creato dall’applicazione web. Nella versione precedente, quella utilizzata forniva i valori di tabelle "hardcoded" nel codice. Questa volta, la nuova fonte di dati li preleverà da una fonte di dati ODBC associata a un database MySQL.
6.2.2. La fonte dati ODBC
I dati si troveranno in una tabella denominata [IMPOTS] di un database MySQL denominato [dbimpots]. Il contenuto di questa tabella sarà il seguente:

Il proprietario del database è l'utente [admimpots] con password [mdpimpots]. Associamo una fonte di dati ODBC a questo database. Prima di procedere, ricordiamo innanzitutto i diversi modi per accedere a un database con la piattaforma .NET.
Esistono numerosi database per le piattaforme Windows. Per accedervi, le applicazioni utilizzano programmi chiamati driver.

Nello schema sopra riportato, il driver presenta due interfacce:
- l’interfaccia I1 presentata all’applicazione
- l'interfaccia I2 verso il database
Per evitare che un'applicazione scritta per un database B1 debba essere riscritta in caso di migrazione verso un database B2 diverso, è stato effettuato un lavoro di standardizzazione sull'interfaccia I1. Se si utilizzano database che impiegano driver “normalizzati”, il database B1 verrà fornito con un driver P1, il database B2 con un driver P2, e l’interfaccia I1 di questi due driver sarà identica. Pertanto non sarà necessario riscrivere l’applicazione. Sarà così possibile, ad esempio, migrare un database ACCESS verso un database MySQL senza modificare l'applicazione.
Esistono due tipi di driver standardizzati:
- i driver ODBC (Open DataBase Connectivity)
- i driver OLE e DB (Object Linking and Embedding DataBase)
I driver ODBC consentono l'accesso ai database. Le fonti di dati per i driver OLE e DB sono più varie: database, sistemi di messaggistica, rubriche, ... Non ci sono limiti. Qualsiasi fonte di dati può essere oggetto di un driver Ole DB, se un editore lo decide. Il vantaggio è ovviamente notevole: si ottiene un accesso uniforme a una grande varietà di dati.
La piattaforma .NET 1.1 viene fornita con tre tipi di classi di accesso ai dati:
- le classi SQL e Server.NET, per accedere ai database SQL Server di Microsoft
- le classi Ole Db.NET, per accedere ai database SGBD che offrono un driver OLE DB
- le classi odbc.net, per accedere ai database SGBD che offrono un driver ODBC
Il SGBD MySQL dispone da tempo di un driver ODBC. È proprio questo che utilizziamo ora. Su Windows, scegliamo l'opzione [Menu Démarrer/Panneau de configuration/Outils d'administration/Sources ODBC 32 bits]. A seconda della versione di Windows, questo percorso può variare leggermente. Si ottiene la seguente applicazione che ci consentirà di creare la nostra sorgente ODBC:

Creeremo una fonte dati di sistema, c.a.d. Una fonte dati a cui potrà accedere qualsiasi utente del computer utiliser.Aussi; nella schermata sopra selezioniamo la scheda [Source de données système]. La pagina visualizzata presenta un pulsante [Ajouter] che utilizzeremo per creare una nuova fonte dati ODBC:

La procedura guidata richiede di selezionare il driver ODBC da utilizzare. Windows include una serie di driver ODBC preinstallati. Il driver ODBC di MySQL non fa parte del pacchetto. È quindi necessario installarlo prima. È possibile trovarlo su Internet digitando la stringa "MySQL ODBC" o "MyODBC" in un motore di ricerca. In questo caso, abbiamo installato il driver [MySQL ODBC 3.51]. Lo selezioniamo e inseriamo [Terminer]:

È necessario fornire alcune informazioni:
il nome che identificherà la fonte di dati ODBC. Qualsiasi applicazione Windows potrà accedere alla fonte tramite questo nome | |
un testo arbitrario che descrive la fonte di dati | |
il nome del computer che ospita SGBD MySQL. In questo caso si tratta del computer locale. Potrebbe trattarsi di un computer remoto. Ciò consentirebbe a un’applicazione Windows di accedere a un database remoto senza alcuna codifica specifica. Questo è uno dei principali vantaggi della fonte ODBC. | |
un SGBD MySQL può gestire più database. Qui si specifica quale si desidera gestire: dbimpots | |
nome di un utente registrato all'interno di SGBD MySQL. È con questo nome che avverranno gli accessi alla fonte dei dati. In questo caso: admimpots | |
la password di questo utente. In questo caso: mdpimpots | |
porta di lavoro di SGBD MySQL. Per impostazione predefinita è la porta 3306. Non l'abbiamo modificata |
Fatto ciò, verifichiamo la validità dei nostri parametri di connessione con il pulsante [Test Data Source]:

A questo punto, siamo certi della nostra fonte di dati ODBC. Ora possiamo utilizzarla. Ripetiamo l’operazione [OK] tutte le volte necessarie per uscire dalla procedura guidata ODBC.
Se il lettore non dispone di SGBD mySQL, può scaricarlo gratuitamente all'indirizzo [http://www.mysql.com]. Di seguito illustriamo la procedura per creare una fonte ODBC con Access. I primi passaggi sono identici a quanto descritto in precedenza. Si aggiunge una nuova fonte di dati di sistema:

Il driver selezionato sarà [Microsoft Access Driver]. Si esegue [Terminer] per passare alla definizione della fonte ODBC:

Le informazioni da fornire sono le seguenti:
il nome che identificherà la fonte di dati ODBC. Qualsiasi applicazione Windows potrà accedere alla fonte tramite questo nome | |
un testo arbitrario che descrive la fonte di dati | |
il nome completo del file ACCESS da utilizzare |
6.2.3. Una nuova classe di accesso ai dati
Torniamo alla struttura MVC della nostra applicazione:

Nello schema sopra riportato, la classe [impotsData] ha il compito di recuperare i dati. In questo caso dovrà farlo dalla base MySQL [dbimpots]. Sappiamo già dalla versione precedente di questa applicazione che [impotsData] è una classe astratta da cui occorre derivare ogni volta che si desidera adattarla a una nuova fonte di dati. Ricordiamo la struttura di questa classe astratta:
Imports System.Collections
Namespace st.istia.univangers.fr
Public MustInherit Class impotsData
Protected limites() As Decimal
Protected coeffr() As Decimal
Protected coeffn() As Decimal
Protected checked As Boolean
Protected valide As Boolean
' metodo di accesso ai dati
Public MustOverride Function getData() As Object()
' metodo di verifica dei dati
Protected Function checkData() As Integer
' verifica i dati acquisiti
...
End Function
' verifica la validità del contenuto di una tabella
Protected Function check(ByRef tableau() As Decimal, ByVal n As Integer) As Boolean
...
End Function
End Class
End Namespace
La classe che deriva da [impotsData] deve implementare due metodi:
- un costruttore, se il costruttore senza argomenti di [impotsData] non è adatto alle sue esigenze
- il metodo [getData] che restituisce i tre array (limiti, coeffr, coeffn)
Creiamo la classe [impotsODBC] che recupererà i dati (limiti, coeffr, coeffn) da una fonte ODBC a cui assegneremo il nome:
Imports System.Data.Odbc
Imports System.Data
Imports System.Collections
Imports System
Namespace st.istia.univangers.fr
Public Class impotsODBC
Inherits impotsData
' variabili di istanza
Protected DSNimpots As String
' costruttore
Public Sub New(ByVal DSNimpots As String)
' si annotano le tre informazioni
Me.DSNimpots = DSNimpots
End Sub
Public Overrides Function getdata() As Object()
' inizializza i tre array limites, coeffr, coeffn a partire
' del contenuto della tabella [impots] del database ODBC DSNimpots
' limiti, coeffr, coeffn sono le tre colonne di questa tabella
' può generare diverse eccezioni
Dim connectString As String = "DSN=" + DSNimpots + ";" ' chaîne de connexion à la base
Dim impotsConn As OdbcConnection = Nothing ' la connexion
Dim sqlCommand As OdbcCommand = Nothing ' la commande SQL
' la query SELECT
Dim selectCommand As String = "select limites,coeffr,coeffn from impots"
' tabelle per il recupero dei dati
Dim aLimites As New ArrayList
Dim aCoeffR As New ArrayList
Dim aCoeffN As New ArrayList
Try
' si tenta di accedere al database
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
' si crea un oggetto comando
sqlCommand = New OdbcCommand(selectCommand, impotsConn)
' si esegue la query
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
' Analisi della tabella recuperata
While myReader.Read()
' i dati della riga corrente vengono inseriti nelle tabelle
aLimites.Add(myReader("limites"))
aCoeffR.Add(myReader("coeffr"))
aCoeffN.Add(myReader("coeffn"))
End While
' liberazione delle risorse
myReader.Close()
impotsConn.Close()
Catch e As Exception
Throw New Exception("Erreur d'accès à la base de données (" + e.Message + ")")
End Try
' gli array dinamici vengono trasferiti in array statici
Me.limites = New Decimal(aLimites.Count - 1) {}
Me.coeffr = New Decimal(aLimites.Count - 1) {}
Me.coeffn = New Decimal(aLimites.Count - 1) {}
Dim i As Integer
For i = 0 To aLimites.Count - 1
limites(i) = Decimal.Parse(aLimites(i).ToString())
coeffR(i) = Decimal.Parse(aCoeffR(i).ToString())
coeffN(i) = Decimal.Parse(aCoeffN(i).ToString())
Next i
' Si verificano i dati acquisiti
Dim erreur As Integer = checkData()
' se i dati non sono validi, viene generata un'eccezione
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' altrimenti si restituiscono le tre tabelle
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Diamo un'occhiata al produttore:
' costruttore
Public Sub New(ByVal DSNimpots As String)
' si annotano le tre informazioni
Me.DSNimpots = DSNimpots
End Sub
Come parametro riceve il nome della fonte ODBC in cui si trovano i dati da acquisire. Il costruttore si limita a memorizzare questo nome. Il metodo [getData] ha il compito di leggere i dati dalla tabella [impots] e di inserirli in tre array (limiti, coeffr, coeffn). Commentiamo il suo codice:
- i parametri di connessione alla fonte dati ODBC sono definiti, ma la fonte dati non è aperta
' stringa di connessione al database
Dim connectString As String = "DSN=" + DSNimpots + ";"
' si crea un oggetto di connessione al database - questa connessione non è aperta
Dim impotsConn As OdbcConnection = New OdbcConnection(connectString)
- Si definiscono tre oggetti [ArrayList] per recuperare i dati dalla tabella [impots]:
' tabelle per recuperare i dati
Dim aLimites As New ArrayList
Dim aCoeffR As New ArrayList
Dim aCoeffN As New ArrayList
- Tutto il codice di accesso al database è racchiuso in un blocco try/catch per gestire eventuali errori di accesso. Si apre la connessione al database:
' si tenta di accedere al database
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
- si esegue il comando [select] sulla connessione aperta. Si ottiene un oggetto [OdbcDataReader] che ci consentirà di scorrere le righe della tabella risultante dal select:
' si crea un oggetto comando
Dim sqlCommand As OdbcCommand = New OdbcCommand(selectCommand, impotsConn)
' si esegue la query
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
- Si scorre la tabella dei risultati, riga per riga. A tal fine si utilizza il metodo [Read] dell’oggetto [OdbcDataReader] ottenuto in precedenza. Questo metodo svolge due operazioni:
- avanza di una riga nella tabella. All’inizio ci si trova prima della prima riga
- restituisce il valore booleano [true] se è stato possibile avanzare, [false] in caso contrario; quest’ultimo caso indica che tutte le righe sono state elaborate.
Le colonne della riga corrente dell’oggetto [OdbcDataReader] si ottengono tramite OdbcDataReader. Si ottiene un oggetto che rappresenta il valore della colonna. Si percorre l’intera tabella per inserire il suo contenuto nei tre oggetti [ArrayList]:
' Analisi della tabella recuperata
While myReader.Read()
' i dati della riga corrente vengono inseriti nelle tabelle
aLimites.Add(myReader("limites"))
aCoeffR.Add(myReader("coeffr"))
aCoeffN.Add(myReader("coeffn"))
- Fatto ciò, liberiamo le risorse associate alla connessione:
- Il contenuto dei tre oggetti [ArrayList] viene trasferito in tre array standard:
' gli array dinamici vengono trasferiti in array statici
limites = New Decimal(aLimites.Count - 1) {}
coeffr = New Decimal(aLimites.Count - 1) {}
coeffn = New Decimal(aLimites.Count - 1) {}
Dim i As Integer
For i = 0 To aLimites.Count - 1
limites(i) = CType(aLimites(i), Decimal)
coeffR(i) = CType(aCoeffR(i), Decimal)
coeffN(i) = CType(aCoeffN(i), Decimal)
Next i
- una volta che i dati della tabella [impots] sono stati inseriti nelle tre tabelle, non resta che verificarne il contenuto utilizzando il metodo [checkData] della classe base [impotsData]:
' Si verificano i dati acquisiti
Dim erreur As Integer = checkData()
' se i dati non sono validi, viene generata un'eccezione
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' altrimenti si restituiscono le tre tabelle
Return New Object() {limites, coeffr, coeffn}
6.2.4. Test della classe di accesso ai dati
Un programma di test potrebbe essere il seguente:
Option Explicit On
Option Strict On
' spazi dei nomi
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' pagina di test
Module testimpots
Sub Main(ByVal arguments() As String)
' programma interattivo per il calcolo delle imposte
' l'utente digita tre dati dalla tastiera: coniugato nbEnfants stipendio
' il programma visualizza quindi l’imposta da pagare
Const syntaxe1 As String = "pg DSNimpots"
Const syntaxe2 As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"
' verifica dei parametri del programma
If arguments.Length <> 1 Then
' messaggio di errore
Console.Error.WriteLine(syntaxe1)
' fine
Environment.Exit(1)
End If
' si recuperano gli argomenti
Dim DSNimpots As String = arguments(0)
' creazione di un oggetto imposta
Dim objImpot As impot = Nothing
Try
objImpot = New impot(New impotsODBC(DSNimpots))
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(2)
End Try
' ciclo infinito
While True
' all'inizio nessun errore
Dim erreur As Boolean = False
' vengono richiesti i parametri per il calcolo dell'imposta
Console.Out.Write("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :")
Dim paramètres As String = Console.In.ReadLine().Trim()
' Cosa fare?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' verifica del numero di argomenti nella riga inserita
Dim args As String() = paramètres.Split(Nothing)
Dim nbParamètres As Integer = args.Length
If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe2)
erreur = True
End If
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Integer
If Not erreur Then
' Verifica della validità dei parametri
' coniugato
marié = args(0).ToLower()
If marié <> "o" And marié <> "n" Then
Console.Error.WriteLine((syntaxe2 + ControlChars.Lf + "Argument marié incorrect : tapez o ou n"))
erreur = True
End If
' nbEnfants
nbEnfants = 0
Try
nbEnfants = Integer.Parse(args(1))
If nbEnfants < 0 Then
Throw New Exception
End If
Catch
Console.Error.WriteLine(syntaxe2 + "\nArgument nbEnfants incorrect : tapez un entier positif ou nul")
erreur = True
End Try
' stipendio
salaire = 0
Try
salaire = Integer.Parse(args(2))
If salaire < 0 Then
Throw New Exception
End If
Catch
Console.Error.WriteLine(syntaxe2 + "\nArgument salaire incorrect : tapez un entier positif ou nul")
erreur = True
End Try
End If
If Not erreur Then
' i parametri sono corretti - si calcola l'imposta
Console.Out.WriteLine(("impôt=" & objImpot.calculer(marié = "o", nbEnfants, salaire).ToString + " euro(s)"))
End If
End While
End Sub
End Module
End Namespace
L'applicazione viene avviata con un parametro:
- DSNimpots: nome della fonte dati ODBC da utilizzare
Il calcolo dell'imposta viene effettuato tramite un oggetto di tipo [impot] creato al momento dell'avvio dell'applicazione:
' creazione di un oggetto imposta
Dim objImpôt As impot = Nothing
Try
objImpot = New impot(New impotsODBC(DSNimpots))
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(1)
End Try
Una volta avviata, l'applicazione chiede ripetutamente all'utente di digitare sulla tastiera le tre informazioni necessarie per calcolare la sua imposta:
- il proprio stato civile: o per sposato, n per non sposato
- il numero di figli
- il suo stipendio annuale
L'insieme delle classi viene compilato:
dos>vbc /r:system.dll /r:system.data.dll /t:library /out:impot.dll impots.vb impotsArray.vb impotsData.vb impotsODBC.vb
dos>dir
01/04/2004 19:34 7 168 impot.dll
01/04/2004 19:31 1 360 impots.vb
21/04/2004 08:23 1 311 impotsArray.vb
21/04/2004 08:26 1 634 impotsData.vb
01/04/2004 19:34 2 735 impotsODBC.vb
01/04/2004 19:32 3 210 testimpots.vb
A sua volta, viene compilato il programma di test:
dir>dir
01/04/2004 19:34 7 168 impot.dll
01/04/2004 19:31 1 360 impots.vb
21/04/2004 08:23 1 311 impotsArray.vb
21/04/2004 08:26 1 634 impotsData.vb
01/04/2004 19:34 2 735 impotsODBC.vb
01/04/2004 19:34 6 144 testimpots.exe
01/04/2004 19:32 3 210 testimpots.vb
Il programma di test viene eseguito innanzitutto con la fonte dati ODBC MySQL:
dos>testimpots odbc-mysql-dbimpots
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 60000
impôt=4300 euro(s)
Si passa dalla fonte ODBC a una fonte Access:
dos>testimpots odbc-access-dbimpots
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 60000
impôt=4300 F
6.2.5. Le viste dell'applicazione web
Sono quelle dell'applicazione precedente: formulaire.aspx e erreurs.aspx
6.2.6. I controller dell’applicazione [global.asax, main.aspx]
È necessario modificare solo il controller [global.asax]. Esso è infatti responsabile della creazione dell'oggetto [impot] all'avvio dell'applicazione. L'unico parametro del costruttore di questo oggetto è l'oggetto di tipo [impotsData], incaricato di recuperare i dati. Questo parametro varia quindi per ogni nuovo tipo di fonte di dati. Il controller [global.asax.vb] diventa il seguente:
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' si crea un oggetto imposta
Dim objImpot As impot
Try
objImpot = New impot(New impotsODBC(ConfigurationSettings.AppSettings("DSNimpots")))
' inserimento dell'oggetto nell'applicazione
Application("objImpot") = objImpot
' nessun errore
Application("erreur") = False
Catch ex As Exception
'si è verificato un errore, lo si registra nell'applicazione
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
La fonte dati dell'oggetto [impot] è ora un oggetto [impotODBC]. Quest'ultimo ha come parametro il nome DSN della fonte dati ODBC da utilizzare. Anziché inserire questo nome in modo statico nel codice, lo si inserisce nel file di configurazione [web.config] dell’applicazione:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DSNimpots" value="odbc-mysql-dbimpots" />
</appSettings>
</configuration>
È noto che il valore di una chiave C della sezione <appSettings> del file [web.config] viene ottenuto nel codice dell'applicazione tramite [ConfigurationSettings.AppSettings(C)].
Per individuare la causa dell’eccezione, il messaggio relativo viene registrato nell’applicazione in modo che rimanga disponibile per le richieste. Il controllo [main.aspx.vb] includerà questo messaggio nel proprio elenco di errori:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' prima di tutto, si verifica se l'applicazione è riuscita a inizializzarsi correttamente
If CType(Application("erreur"), Boolean) Then
' si reindirizza alla pagina degli errori
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible...(" + Application("message").ToString + ")")
context.Items("erreurs") = erreurs
context.Items("lien") = ""
context.Items("href") = ""
Server.Transfer("erreurs.aspx")
End If
' si recupera l'azione da eseguire
...
6.2.7. Riepilogo delle modifiche
L'applicazione è pronta per essere testata. Elenchiamo le modifiche apportate rispetto alla versione precedente:
- è stata creata una nuova classe per l'accesso ai dati
- il controller [global.asax.vb] è stato modificato in due punti: creazione dell'oggetto [impot] e registrazione nell'applicazione del messaggio relativo a un'eventuale eccezione
- il controller [main.aspx.vb] è stato modificato in un punto per visualizzare il messaggio di eccezione precedente
- È stato aggiunto un file [web.config]
Il lavoro di modifica è stato effettuato essenzialmente in 1, c.a.d, al di fuori dell’applicazione web. Ciò è stato reso possibile grazie all’architettura MVC dell’applicazione, che separa il controller dalle classi di business. È proprio questo il vantaggio di tale architettura. Si potrebbe dimostrare che, con un file di configurazione adeguato, sarebbe stato possibile evitare qualsiasi modifica al controller dell’applicazione. È possibile inserire nel file il nome della classe di accesso ai dati da istanziare dinamicamente, nonché i vari parametri necessari per tale istanziazione. Con queste informazioni, [global.asax] può istanziare l’oggetto di accesso ai dati. Cambiare la fonte di dati equivale quindi a:
- creare la classe di accesso a tale fonte, se non esiste ancora
- modificare il file [web.config] per consentire la creazione dinamica di un'istanza di tale classe in [global.asax]
6.2.8. Test dell'applicazione web
Tutti i file precedenti vengono inseriti in una cartella <application-path>.

In questa cartella viene creata una sottocartella [bin] in cui viene inserito l’assembly [impot.dll] derivante dalla compilazione dei file delle classi di business: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb]. Di seguito viene riportato il comando di compilazione necessario:
dos>vbc /r:system.dll /r:system.data.dll /t:library /out:impot.dll impots.vb impotsArray.vb impotsData.vb impotsODBC.vb
dos>dir
01/04/2004 19:34 7 168 impot.dll
01/04/2004 19:31 1 360 impots.vb
21/04/2004 08:23 1 311 impotsArray.vb
21/04/2004 08:26 1 634 impotsData.vb
01/04/2004 19:34 2 735 impotsODBC.vb
01/04/2004 19:32 3 210 testimpots.vb
Il file [impot.dll] sopra indicato deve essere collocato in <application-path>\bin affinché l'applicazione web possa accedervi. Il server Cassini viene avviato con i parametri (<application-path>,/impots2). I test danno gli stessi risultati della versione precedente, poiché la presenza del database è trasparente per l'utente. Per illustrare comunque tale presenza, facciamo in modo che la fonte ODBC non sia disponibile arrestando i file SGBD e MySQL e richiediamo l'URL [http://localhost/impots2/main.aspx]. Otteniamo la seguente risposta:

6.3. Esempio 3
6.3.1. Il problema
In questa sede ci proponiamo di affrontare lo stesso problema modificando nuovamente la fonte dei dati dell’oggetto [impot] creato dall’applicazione web. Questa volta, la nuova fonte dei dati sarà un database ACCESS a cui si accederà tramite un driver OLEDB. Il nostro obiettivo è illustrare un altro modo per accedere a un database.
6.3.2. La fonte dati OLEDB
I dati si troveranno in una tabella denominata [IMPOTS] di un database ACCESS. Il contenuto di questa tabella sarà il seguente:

6.3.3. La classe di accesso ai dati
Torniamo alla struttura MVC della nostra applicazione:

- Nello schema sopra riportato, la classe [impotsData] ha il compito di recuperare i dati. Questa volta dovrà farlo da una fonte OLEDB.
Creiamo la classe [impotsOLEDB] che andrà a recuperare i dati (limiti, coeffr, coeffn) da una fonte ODBC a cui assegneremo il nome:
Imports System.Data
Imports System.Collections
Imports System
Imports System.Xml
Imports System.Data.OleDb
Namespace st.istia.univangers.fr
Public Class impotsOLEDB
Inherits impotsData
' variabili di istanza
Protected chaineConnexion As String
' costruttore
Public Sub New(ByVal chaineConnexion As String)
' si annotano le tre informazioni
Me.chaineConnexion = chaineConnexion
End Sub
Public Overrides Function getData() As Object()
' inizializza i tre array limites, coeffr, coeffn a partire
' del contenuto della tabella [impots] del database OLEDB [chaineConnexion]
' limiti, coeffr, coeffn sono le tre colonne di questa tabella
' può generare diverse eccezioni
' si crea un oggetto DataAdapter per leggere i dati dalla fonte OLEDB
Dim adaptateur As New OleDbDataAdapter("select limites,coeffr,coeffn from impots", chaineConnexion)
' si crea un'immagine in memoria del risultato della selezione
Dim contenu As New DataTable("impots")
Try
adaptateur.Fill(contenu)
Catch e As Exception
Throw New Exception("Erreur d'accès à la base de données (" + e.Message + ")")
End Try
' si recupera il contenuto della tabella impots
Dim lignesImpots As DataRowCollection = contenu.Rows
' si definiscono le dimensioni delle tabelle di ricezione
Me.limites = New Decimal(lignesImpots.Count - 1) {}
Me.coeffr = New Decimal(lignesImpots.Count - 1) {}
Me.coeffn = New Decimal(lignesImpots.Count - 1) {}
' si trasferisce il contenuto della tabella impots negli array
Dim i As Integer
Dim ligne As DataRow
Try
For i = 0 To lignesImpots.Count - 1
' riga i della tabella
ligne = lignesImpots.Item(i)
' si recupera il contenuto della riga
limites(i) = CType(ligne.Item(0), Decimal)
coeffr(i) = CType(ligne.Item(1), Decimal)
coeffn(i) = CType(ligne.Item(2), Decimal)
Next
Catch
Throw New Exception("Les données des tranches d'impôts n'ont pas le bon type")
End Try
' si verificano i dati acquisiti
Dim erreur As Integer = checkData()
' se i dati non sono validi, si genera un'eccezione
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' altrimenti si restituiscono i tre array
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Diamo un'occhiata al produttore:
' costruttore
Public Sub New(ByVal chaineConnexion As String)
' si annotano le tre informazioni
Me.chaineConnexion = chaineConnexion
End Sub
Riceve come parametro la stringa di connessione della sorgente OLEDB, nella quale si trovano i dati da acquisire. Il costruttore si limita a memorizzarla. Una stringa di connessione contiene tutti i parametri necessari al driver OLEDB per connettersi alla sorgente OLEDB. In genere è piuttosto complessa. Per scoprire quella delle basi ACCESS, è possibile avvalersi dello strumento [WebMatrix]. Si avvia questo strumento. Esso offre una finestra che consente di connettersi a una fonte di dati:
![]() ![]() | ![]() ![]() |
Grazie all’icona indicata dalla freccia sopra, è possibile creare una connessione a due tipi di database Microsoft: SQL Server e ACCESS. Scegliamo ACCESS:

Abbiamo utilizzato il pulsante [...] per selezionare il database ACCESS. Confermiamo la procedura guidata. Nella scheda [Data], alcune icone indicano la connessione:

Ora creiamo un nuovo file .aspx tramite [Files/New File]:

Otteniamo una pagina vuota su cui possiamo progettare la nostra interfaccia web:

Trascinamo dalla scheda [Data] la tabella [impots] sul foglio sopra riportato. Otteniamo il seguente risultato:

Facciamo clic con il tasto destro sull’oggetto [AccessDataSourceControl] qui sotto per accedere alle sue proprietà:

La stringa di connessione OLEDB al database ACCESS è indicata dalla proprietà [ConnectionString] sopra riportata:
Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\serge\devel\aspnet\poly\chap5\impots\3\impots.mdb
Si nota che questa stringa è composta da una parte fissa e da una parte variabile, che corrisponde semplicemente al nome del file ACCESS. Sfrutteremo questa caratteristica per generare la stringa di connessione alla nostra fonte di dati OLEDB.
Torniamo ora alla nostra classe [impotsOLEDB]. Il metodo [getData] ha il compito di leggere i dati dalla tabella [impots] e di inserirli in tre array (limites, coeffr, coeffn). Commentiamo il suo codice:
- definiamo l’oggetto [DataAdapter] che ci consentirà di trasferire in memoria il risultato di una query SELECT SQL. A tal fine, definiamo la query [select] da eseguire e la associamo all’oggetto [DataAdapter]. Il costruttore di quest’ultimo richiede anche la stringa di connessione che dovrà utilizzare per connettersi alla fonte OLEDB
' si crea un oggetto DataAdapter per leggere i dati dalla sorgente OLEDB
Dim adaptateur As New OleDbDataAdapter("select limites,coeffr,coeffn from impots", chaineConnexion)
- si esegue il comando [select] tramite il metodo [Fill] dell’oggetto [DataAdapter]. Il risultato di [select] viene inserito in un oggetto [DataTable] creato appositamente. Un oggetto [DataTable] rappresenta in memoria una tabella del database, c.a.d, ovvero un insieme di righe e colonne. Gestiamo un'eccezione che può verificarsi se, ad esempio, la stringa di connessione non è corretta.
' si crea un'immagine in memoria del risultato della selezione
Dim contenu As New DataTable("impots")
Try
adaptateur.Fill(contenu)
Catch e As Exception
Throw New Exception("Erreur d'accès à la base de données (" + e.Message + ")")
End Try
- in [contenu], abbiamo la tabella [impots] importata da [select]. Un oggetto [DataTable] è una tabella, quindi un insieme di righe. Queste sono accessibili tramite la proprietà [rows] di [datatable]:
' si recupera il contenuto della tabella impots
Dim lignesImpots As DataRowCollection = contenu.Rows
- ogni elemento della collezione [lignesImpots] è un oggetto di tipo [DataRow] che rappresenta una riga della tabella. Questa presenta colonne accessibili tramite l’oggetto [DataRow] tramite la sua proprietà [Item]. [DataRow].[Item(i)] è la colonna n. i della riga [DataRow]. Scorrendo la collezione delle righe (la collezione DataRows di lignesImpots) e le collezioni delle colonne di ciascuna riga, è possibile ottenere l’intera tabella:
' si definiscono le dimensioni delle tabelle di ricezione
Me.limites = New Decimal(lignesImpots.Count - 1) {}
Me.coeffr = New Decimal(lignesImpots.Count - 1) {}
Me.coeffn = New Decimal(lignesImpots.Count - 1) {}
' si trasferisce il contenuto della tabella impots negli array
Dim i As Integer
Dim ligne As DataRow
Try
For i = 0 To lignesImpots.Count - 1
' riga i della tabella
ligne = lignesImpots.Item(i)
' si recupera il contenuto della riga
limites(i) = CType(ligne.Item(0), Decimal)
coeffr(i) = CType(ligne.Item(1), Decimal)
coeffn(i) = CType(ligne.Item(2), Decimal)
Next
Catch
Throw New Exception("Les données des tranches d'impôts n'ont pas le bon type")
End Try
- una volta che i dati della tabella [impots] sono stati inseriti nelle tre tabelle, non resta che verificarne il contenuto utilizzando il metodo [checkData] della classe base [impotsData]:
' si verificano i dati acquisiti
Dim erreur As Integer = checkData()
' se i dati non sono validi, si genera un'eccezione
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' altrimenti si restituiscono i tre array
Return New Object() {limites, coeffr, coeffn}
6.3.4. Test della classe di accesso ai dati
Un programma di test potrebbe essere il seguente:
Option Explicit On
Option Strict On
' spazi dei nomi
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' pagina di test
Module testimpots
Sub Main(ByVal arguments() As String)
' programma interattivo per il calcolo delle imposte
' l'utente digita tre dati dalla tastiera: coniugato nbEnfants stipendio
' il programma visualizza quindi l’imposta da pagare
Const syntaxe1 As String = "pg bdACCESS"
Const syntaxe2 As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"
' verifica dei parametri del programma
If arguments.Length <> 1 Then
' messaggio di errore
Console.Error.WriteLine(syntaxe1)
' fine
Environment.Exit(1)
End If
' si recuperano gli argomenti
Dim chemin As String = arguments(0)
' si prepara la stringa di connessione
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + chemin
' creazione di un oggetto fiscale
Dim objImpot As impot = Nothing
Try
objImpot = New impot(New impotsOLEDB(chaineConnexion))
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(2)
End Try
' ciclo infinito
While True
' all'inizio nessun errore
Dim erreur As Boolean = False
' si richiedono i parametri per il calcolo dell'imposta
Console.Out.Write("Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :")
Dim paramètres As String = Console.In.ReadLine().Trim()
' C'è qualcosa da fare?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' verifica del numero di argomenti nella riga inserita
Dim args As String() = paramètres.Split(Nothing)
Dim nbParamètres As Integer = args.Length
If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe2)
erreur = True
End If
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Integer
If Not erreur Then
' Verifica della validità dei parametri
' coniugato
marié = args(0).ToLower()
If marié <> "o" And marié <> "n" Then
Console.Error.WriteLine((syntaxe2 + ControlChars.Lf + "Argument marié incorrect : tapez o ou n"))
erreur = True
End If
' nbEnfants
nbEnfants = 0
Try
nbEnfants = Integer.Parse(args(1))
If nbEnfants < 0 Then
Throw New Exception
End If
Catch
Console.Error.WriteLine(syntaxe2 + "\nArgument nbEnfants incorrect : tapez un entier positif ou nul")
erreur = True
End Try
' stipendio
salaire = 0
Try
salaire = Integer.Parse(args(2))
If salaire < 0 Then
Throw New Exception
End If
Catch
Console.Error.WriteLine(syntaxe2 + "\nArgument salaire incorrect : tapez un entier positif ou nul")
erreur = True
End Try
End If
If Not erreur Then
' i parametri sono corretti - si calcola l'imposta
Console.Out.WriteLine(("impôt=" & objImpot.calculer(marié = "o", nbEnfants, salaire).ToString + " euro(s)"))
End If
End While
End Sub
End Module
End Namespace
L'applicazione viene avviata con un parametro:
- bdACCESS: nome del file ACCESS da elaborare
Il calcolo dell'imposta viene effettuato tramite un oggetto di tipo [impot] creato al momento dell'avvio dell'applicazione:
' si recuperano gli argomenti
Dim chemin As String = arguments(0)
' si prepara la stringa di connessione
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + chemin
' creazione di un oggetto imposta
Dim objImpot As impot = Nothing
Try
objImpot = New impot(New impotsOLEDB(chaineConnexion))
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(2)
End Try
La stringa di connessione alla fonte OLEDB è stata generata sulla base delle informazioni ottenute con [WebMatrix].
Una volta avviata, l'applicazione richiede ripetutamente all'utente di digitare sulla tastiera le tre informazioni necessarie per calcolare la sua imposta:
- il proprio stato civile: o per sposato, n per non sposato
- il numero di figli
- il suo stipendio annuale
Viene compilato l'insieme delle classi:
dos>vbc /r:system.dll /r:system.data.dll /t:library /out:impot.dll impots.vb impotsArray.vb impotsData.vb impotsOLEDB.vb
Il file [impots.mdb] viene inserito nella cartella dell'applicazione di test, che viene avviata come segue:
dos>testimpots impots.mdb
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 60000
impôt=4300 euro(s)
È possibile avviare l’applicazione con un file ACCESS non corretto:
dos>testimpots xx
L'erreur suivante s'est produite : Erreur d'accès à la base de données (Ficher 'D:\data\serge\devel\aspnet\poly\chap5\impots\3\xx' introuvable.)
6.3.5. Le viste dell'applicazione web
Sono quelle dell'applicazione precedente: formulaire.aspx e erreurs.aspx
6.3.6. I controller dell'applicazione [global.asax, main.aspx]
È necessario modificare solo il controller [global.asax]. Esso, infatti, si occupa di creare l'oggetto [impot] all'avvio dell'applicazione. L'unico parametro del costruttore di questo oggetto è l'oggetto di tipo [impotsData] incaricato di recuperare i dati. Questo parametro cambia quindi poiché si cambia la fonte dei dati. Il controller [global.asax.vb] diventa il seguente:
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' si crea un oggetto imposta
Dim objImpot As impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
' si inserisce l'oggetto nell'applicazione
Application("objImpot") = objImpot
' nessun errore
Application("erreur") = False
Catch ex As Exception
'si è verificato un errore, lo si registra nell'applicazione
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
La fonte dati dell'oggetto [impot] è ora un oggetto [impotOLEDB]. Quest'ultimo ha come parametro la stringa di connessione della fonte dati OLEDB da utilizzare. Questa è inserita nel file di configurazione [web.config] dell'applicazione:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="chaineConnexion"
value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\serge\devel\aspnet\poly\chap5\impots2\impots.mdb" />
</appSettings>
</configuration>
Il controller [main.aspx] rimane invariato.
6.3.7. Riepilogo delle modifiche
L'applicazione è pronta per essere testata. Elenchiamo le modifiche apportate rispetto alla versione precedente:
- è stata creata una nuova classe per l’accesso ai dati
- il controller [global.asax.vb] è stato modificato in un punto: creazione dell'oggetto [impot]
- è stato aggiunto un file [web.config]
6.3.8. Test dell’applicazione web
Tutti i file precedenti sono collocati in una cartella <application-path>.

In questa cartella viene creata una sottocartella [bin] in cui viene inserito l'assembly [impot.dll] derivante dalla compilazione dei file delle classi di business: [impots.vb, impotsData.vb, impotsArray.vb, impotsOLEDB.vb]. Di seguito viene riportato il comando di compilazione necessario:
dos>vbc /r:system.dll /r:system.data.dll /t:library /out:impot.dll impots.vb impotsArray.vb impotsData.vb impotsOLEDB.vb
Il file [impot.dll] generato da questo comando deve essere collocato in <application-path>\bin affinché l'applicazione web possa accedervi. Il server Cassini viene avviato con i parametri (<application-path>,/impots3). I test danno gli stessi risultati della versione precedente.
6.4. Esempio 4
6.4.1. Il problema
Ora intendiamo trasformare la nostra applicazione in un'applicazione di simulazione del calcolo delle imposte. Un utente potrà effettuare calcoli successivi delle imposte, che gli verranno presentati in una nuova vista simile a questa:

6.4.2. La struttura MVC dell’applicazione
La struttura MVC dell’applicazione diventa la seguente:

Viene visualizzata una nuova vista [simulations.aspx], di cui abbiamo appena fornito uno screenshot. La classe di accesso ai dati sarà la classe [impotsODBC] dell’esempio 2.
6.4.3. Le viste dell’applicazione web
La vista [erreurs.aspx] non cambia. La vista [formulaire.aspx] subisce una leggera modifica. Infatti, l’importo dell’imposta non compare più in questa vista. Ora si trova nella vista [simulations.aspx]. Pertanto, all’avvio, la pagina presentata all’utente è la seguente:

Inoltre, la vista [formulaire] include uno script JavaScript che verifica la validità dei dati inseriti prima di inviarli al server, come mostra l’esempio seguente:

Il codice di presentazione è il seguente:
<%@ page src="formulaire.aspx.vb" inherits="formulaire" AutoEventWireup="false"%>
<html>
<head>
<title>Impôt</title>
<script language="javascript">
function calculer(){
// verifica dei parametri prima di inviarli al server
with(document.frmImpots){
//numero di figli
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
// il modello non è stato verificato
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
txtEnfants.focus();
return;
}//if
//stipendio
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
// il modello non è stato verificato
alert("Le salaire n'a pas été donné ou est incorrect");
txtSalaire.focus();
return;
}//se
// Va bene - inviamo il modulo al server
submit();
}//con
}//calcolare
</script>
</head>
<body>
<P>Calcul de votre impôt</P>
<HR width="100%" SIZE="1">
<form name="frmImpots" method="post" action="main.aspx?action=calcul">
<TABLE border="0">
<TR>
<TD>Etes-vous marié(e)</TD>
<TD>
<INPUT type="radio" value="oui" name="rdMarie" <%=rdouichecked%>>Oui
<INPUT type="radio" value="non" name="rdMarie" <%=rdnonchecked%>>Non</TD>
</TR>
<TR>
<TD>Nombre d'enfants</TD>
<TD><INPUT type="text" size="3" maxLength="3" name="txtEnfants" value="<%=txtEnfants%>"></TD>
</TR>
<TR>
<TD>Salaire annuel (euro)</TD>
<TD><INPUT type="text" maxLength="12" size="12" name="txtSalaire" value="<%=txtSalaire%>"></TD>
</TR>
</TABLE>
<hr>
<P>
<INPUT type="button" value="Calculer" onclick="calculer()">
</P>
</form>
<form method="post" action="main.aspx?action=effacer">
<INPUT type="submit" value="Effacer">
</form>
</body>
</html>
I campi dinamici della pagina sono quelli delle versioni precedenti. Il campo dinamico relativo all'importo dell'imposta è stato rimosso. Il pulsante [Calculer] non è più un pulsante di tipo [submit]. È di tipo [button] e, quando viene cliccato, viene eseguita la funzione JavaScript [calculer()]:
<INPUT type="button" value="Calculer" onclick="calculer()">
Al modulo è stato assegnato il nome [frmImpots] per poterlo referenziare nello script [calculer]:
<form name="frmImpots" method="post" action="main.aspx?action=calcul">
La funzione JavaScript [calculer] utilizza espressioni regolari per verificare la validità dei campi dei moduli [document.frmImpots.txtEnfants] e [document.frmImpots.txtSalaire]. Se i valori inseriti sono corretti, vengono inviati al server tramite [document.frmImpots.submit()].
La pagina di presentazione ottiene i propri campi dinamici dal seguente controller [formulaire.aspx.vb]:
Imports System.Collections.Specialized
Public Class formulaire
Inherits System.Web.UI.Page
' campi della pagina
Protected rdouichecked As String
Protected rdnonchecked As String
Protected txtEnfants As String
Protected txtSalaire As String
Protected txtImpot As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' recuperiamo la richiesta precedente dal contesto
Dim form As NameValueCollection = Context.Items("formulaire")
' si prepara la pagina da visualizzare
' pulsanti di opzione
rdouichecked = ""
rdnonchecked = "checked"
If form("rdMarie").ToString = "oui" Then
rdouichecked = "checked"
rdnonchecked = ""
End If
' il resto
txtEnfants = CType(form("txtEnfants"), String)
txtSalaire = CType(form("txtSalaire"), String)
End Sub
End Class
Il controller [formulaire.aspx.vb] è identico alle versioni precedenti, tranne per il fatto che non deve più recuperare il campo [txtImpot] dal contesto, poiché tale campo è stato rimosso dalla pagina.
La vista [simulations.aspx] si presenta visivamente come segue:

e corrisponde al seguente codice di presentazione:
<%@ page src="simulations.aspx.vb" inherits="simulations" autoeventwireup="false" %>
<HTML>
<HEAD>
<title>simulations</title>
</HEAD>
<body>
<P>Résultats des simulations</P>
<HR width="100%" SIZE="1">
<table>
<tr>
<th>
Marié</th>
<th>
Enfants</th>
<th>
Salaire annuel (euro)</th>
<th>
Impôt à payer (euro)</th>
</tr>
<%=simulationsHTML%>
</table>
<p></p>
<a href="<%=href%>">
<%=lien%>
</a>
</body>
</HTML>
Questo codice presenta tre campi dinamici:
codice HTML di un elenco di simulazioni sotto forma di righe di tabella HTML | |
URL di un link | |
testo del link |
Sono generati dal modulo di controllo [simulations.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Public Class simulations
Inherits System.Web.UI.Page
Protected simulationsHTML As String = ""
Protected href As String
Protected lien As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'si recuperano le simulazioni dal contesto
Dim simulations As ArrayList = CType(context.Items("simulations"), ArrayList)
' ogni simulazione è un array di 4 elementi di tipo string
Dim simulation() As String
Dim i, j As Integer
For i = 0 To simulations.Count - 1
simulation = CType(simulations(i), String())
simulationsHTML += "<tr>"
For j = 0 To simulation.Length - 1
simulationsHTML += "<td>" + simulation(j) + "</td>"
Next
simulationsHTML += "</tr>" + ControlChars.CrLf
Next
' si recuperano gli altri elementi del contesto
href = context.Items("href").ToString
lien = context.Items("lien").ToString
End Sub
End Class
Il controller della pagina recupera le informazioni inserite dal controller dell'applicazione nel contesto della pagina:
oggetto ArrayList contenente l'elenco delle simulazioni da visualizzare. Ogni elemento è un array di 4 stringhe di caratteri che rappresentano le informazioni (stato civile, figli, stipendio, imposta) della simulazione. | |
URL di un link | |
testo del link |
6.4.4. I controller [global.asax, main.aspx]
Ricordiamo lo schema MVC della nostra applicazione:

Il controller [main.aspx] deve gestire tre azioni:
- init: corrisponde alla prima richiesta del client. Il controller visualizza la vista [formulaire.aspx]
- calcolo: corrisponde alla richiesta di calcolo dell’imposta. Se i dati del modulo di inserimento sono corretti, l’imposta viene calcolata tramite la classe di business [impotsODBC]. Il controller restituisce al client la vista [simulations.aspx] con il risultato della simulazione corrente e di tutte quelle precedenti. Se i dati del modulo di inserimento non sono corretti, il controller restituisce la vista [erreurs.aspx] con l’elenco degli errori e un link per tornare al modulo.
- ritorno: corrisponde al ritorno al modulo dopo un errore. Il controller visualizza la vista [formulaire.aspx] così come era stata convalidata prima dell’errore.
In questa nuova versione, è cambiata solo l’azione [calcul]. Infatti, se i dati sono validi, essa deve portare alla vista [simulations.aspx], mentre in precedenza portava alla vista [formulaire.aspx]. Il controller [main.aspx.vb] diventa il seguente:
Imports System
...
Public Class main
Inherits System.Web.UI.Page
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' prima di tutto, si verifica se l'applicazione è riuscita a inizializzarsi correttamente
...
' si esegue l'azione
Select Case action
Case "init"
' inizializzazione dell'applicazione
initAppli()
Case "calcul"
' calcolo imposta
calculImpot()
Case "retour"
' ritorno al modulo
retourFormulaire()
Case "effacer"
' inizializzazione dell'applicazione
initAppli()
Case Else
' azione sconosciuta = inizializzazione
initAppli()
End Select
End Sub
...
Private Sub calculImpot()
' si salvano i dati inseriti
Session.Item("formulaire") = Request.Form
' si verifica la validità dei dati inseriti
Dim erreurs As ArrayList = checkData()
' se ci sono errori, li si segnala
If erreurs.Count <> 0 Then
' si prepara la pagina degli errori
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
context.Items("erreurs") = erreurs
Server.Transfer("erreurs.aspx")
End If
' in questo caso non ci sono errori: si calcola l'imposta
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
Request.Form("rdMarie") = "oui", _
CType(Request.Form("txtEnfants"), Integer), _
CType(Request.Form("txtSalaire"), Long))
' si aggiunge il risultato alle simulazioni esistenti
Dim simulations As ArrayList
If Not Session.Item("simulations") Is Nothing Then
simulations = CType(Session.Item("simulations"), ArrayList)
Else
simulations = New ArrayList
End If
' aggiunta della simulazione corrente
Dim simulation() As String = New String() {Request.Form("rdMarie").ToString, _
Request.Form("txtEnfants").ToString, Request.Form("txtSalaire").ToString, _
impot.ToString}
simulations.Add(simulation)
' si inseriscono le simulazioni nella sessione e nel contesto
context.Items("simulations") = simulations
Session.Item("simulations") = simulations
' visualizzazione della pagina dei risultati
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
Server.Transfer("simulations.aspx", True)
End Sub
...
End Class
Abbiamo riportato sopra solo ciò che era necessario per comprendere le modifiche presenti esclusivamente nella funzione [calculImpots]:
- innanzitutto la funzione salva il modulo [Request.Form] nella sessione, in modo da poterlo rigenerare nello stato in cui è stato convalidato. Questa operazione va eseguita in ogni caso, poiché sia che l’operazione porti alla risposta [erreurs.aspx] sia che porti alla risposta [simulations.aspx], si ritorna al modulo tramite il link [Retour au formulaire]. Per ripristinarlo correttamente, è necessario averne precedentemente salvato i valori nella sessione.
- Se i dati inseriti risultano corretti, la funzione inserisce la simulazione corrente (stato civile, figli, stipendio, imposta) nell’elenco delle simulazioni. Quest’ultimo si trova nella sessione associata alla chiave "simulazioni".
- L’elenco delle simulazioni viene reinserito nella sessione per un utilizzo futuro. Viene inoltre inserito nel contesto corrente poiché è lì che lo attende la vista [simulations.aspx]
- La vista [simulations.aspx] viene visualizzata una volta inserite nel contesto le altre informazioni che richiede
6.4.5. Riepilogo delle modifiche
L'applicazione è pronta per essere testata. Elenchiamo le modifiche apportate rispetto alle versioni precedenti:
- è stata creata una nuova vista
- il controller [main.aspx.vb] è stato modificato in un punto: elaborazione dell’azione [calcul]
6.4.6. Test dell’applicazione web
Il lettore è invitato a eseguire i test. Ricordiamo la procedura. Tutti i file dell’applicazione sono collocati in una cartella <application-path>. All’interno di questa cartella viene creata una sottocartella [bin] in cui è collocato l’assembly [impot.dll] derivante dalla compilazione dei file delle classi di business: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb. Il file [impot.dll] generato da questo comando deve essere collocato in <application-path>\bin affinché l’applicazione web possa accedervi. Il server Cassini viene avviato con i parametri (<application-path>,/impots4).
6.5. Conclusion
Gli esempi precedenti hanno illustrato, su un caso concreto, i meccanismi comunemente utilizzati nello sviluppo web. Abbiamo utilizzato sistematicamente l’architettura MVC per il suo valore didattico. Si sarebbero potuti trattare questi stessi esempi in modo diverso e forse più semplice senza questa architettura. Tuttavia, essa offre grandi vantaggi non appena l’applicazione diventa un po’ complessa con più pagine.
Potremmo proseguire con i nostri esempi in diversi modi. Eccone alcuni:
- l’utente potrebbe voler conservare le proprie simulazioni nel tempo. Potrebbe effettuare delle simulazioni in un giorno J e ritrovarle, ad esempio, il giorno J+3. Una possibile soluzione a questo problema è l’uso dei cookie. Sappiamo che il token di sessione tra il server e un client viene trasportato tramite questo meccanismo. Si potrebbe utilizzare lo stesso meccanismo anche per trasportare le simulazioni tra il client e il server.
- Contemporaneamente all’invio della pagina con i risultati delle simulazioni, il server invia nelle sue intestazioni HTTP un cookie contenente una stringa di caratteri che rappresenta le simulazioni. Poiché queste si trovano in un oggetto [ArrayList], è necessario effettuare una trasformazione di tale oggetto in [String]. Il server assegnerebbe al cookie una durata, ad esempio 30 giorni.
- Il browser client memorizza i cookie ricevuti in un file e li rinvia ogni volta che effettua una richiesta al server che glieli ha inviati, purché siano ancora validi (durata di validità non superata). Ai fini delle simulazioni, il server riceverà una stringa di caratteri [String] che dovrà trasformare nell’oggetto [ArrayList].
I cookie vengono gestiti da [Response.Cookies] al momento dell’invio al client e da [Request.Cookies] al momento della ricezione sul server.
- Il meccanismo descritto sopra può diventare piuttosto oneroso se il numero di simulazioni è elevato. Inoltre, capita spesso che un utente pulisca periodicamente i propri cookie cancellandoli tutti, anche se autorizza il proprio browser a utilizzarli. Pertanto, prima o poi si perderà il cookie relativo alle simulazioni. Si potrebbe quindi decidere di memorizzarle sul server anziché sul client, ad esempio in un database. Per associare le simulazioni a un utente specifico, l’applicazione potrebbe iniziare con una fase di identificazione che richieda un nome utente e una password, a loro volta memorizzati in un database o in qualsiasi altro tipo di archivio dati.
- Potremmo inoltre voler rendere più sicuro il funzionamento della nostra applicazione. Attualmente essa si basa su due presupposti:
- l’utente passa sempre attraverso il controller [main.aspx]
- e, in tal caso, utilizza sempre le azioni proposte nella pagina che gli è stata inviata
Cosa succede, ad esempio, se l’utente richiede direttamente l’URL [http://localhost/impots4/formulaire.aspx]? Questo caso è poco probabile, poiché l’utente non è a conoscenza dell’esistenza di tale URL. Tuttavia, va previsto. Può essere gestito dal controller dell’applicazione [global.asax], che intercetta tutte le richieste inviate all’applicazione. In questo modo, può verificare che la risorsa richiesta sia effettivamente [main.aspx].
Un caso più probabile è che un utente non utilizzi le azioni presenti nella pagina che il server gli ha inviato. Ad esempio, cosa succede se l’utente richiede direttamente l’URL [http://localhost/impots4/main.aspx?action=retour] senza prima compilare il modulo? Proviamo. Otteniamo la seguente risposta:

Si è verificato un crash del server. È normale. Per l'azione [retour], il controller si aspetta di trovare nella sessione un oggetto [NameValueCollection] che rappresenti i valori del modulo che deve visualizzare. Non li trova. Il meccanismo del controller permette di fornire una soluzione elegante a questo problema. Per ogni richiesta, il controller [main.aspx] può verificare che l’azione richiesta sia effettivamente una delle azioni della pagina precedentemente inviata all’utente. È possibile utilizzare il seguente meccanismo:
- il controller, prima di inviare la risposta al cliente, memorizza nella sessione di quest’ultimo un’informazione che identifica quella pagina
- quando riceve una nuova richiesta dal cliente, verifica che l’azione richiesta appartenga effettivamente all’ultima pagina inviata a quel cliente
- le informazioni che collegano le pagine e le azioni autorizzate in tali pagine possono essere inserite nel file di configurazione [web.config] dell’applicazione.
- L’esperienza dimostra che i controller delle applicazioni hanno un’ampia base comune ed è possibile realizzare un controller generico, la cui specializzazione per una determinata applicazione avviene tramite un file di configurazione. È questa la strada seguita, ad esempio, dallo strumento [Struts] nell’ambito della programmazione web in Java.



