6. Esempi
In questo capitolo illustreremo 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 le proprie imposte. Stiamo considerando il caso semplificato di un contribuente che deve dichiarare solo il proprio stipendio (dati del 2004 relativi al reddito del 2003):
- Calcoliamo il numero di scaglioni fiscali per il dipendente come nbParts = nbEnfants / 2 + 1 se è celibe, e nbEnfants / 2 + 2 se è coniugato, dove nbEnfants è il numero di figli.
- Se ha almeno tre figli, ha diritto a una quota aggiuntiva pari a metà
- Il suo reddito imponibile R è calcolato come R = 0,72 * S, dove S è il suo stipendio annuale
- Calcoliamo il suo coefficiente familiare QF = R / nbParts
- Calcoliamo la sua imposta I. Consideriamo la seguente tabella:
4262 | 0 | 0 |
8382 | 0,0683 | 291,09 |
14.753 | 0,1914 | 1.322,92 |
23.888 | 0,2826 | 2.668,39 |
38.868 | 0,3738 | 4.846,98 |
47.932 | 0,4262 | 6.883,66 |
0 | 0,4809 | 9.505,54 |
Ogni riga ha 3 campi. Per calcolare l'imposta I, trova la prima riga in cui QF <= campo1. Ad esempio, se QF = 5000, la riga trovata sarà
L'imposta I è quindi pari a 0,0683*R - 291,09*nbParts. Se QF è tale che la condizione QF<=campo1 non viene mai soddisfatta, vengono utilizzati i coefficienti dell'ultima riga. Qui:
il che dà 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 di controller sarà gestito dalla pagina [main.aspx]. Ci saranno tre possibili 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 nel modulo di input sono corretti, l'imposta viene calcolata utilizzando la classe di business [impots]. Il controller restituisce al client la vista [form.aspx] così come è stata convalidata, insieme all'imposta calcolata. Se i dati nel modulo di input sono errati, il controller restituirà la vista [errors.aspx] con un elenco di errori e un link per tornare al modulo.
- return: corrisponde al ritorno al modulo dopo un errore. Il controller visualizza la vista [form.aspx] così come era stata convalidata prima dell'errore.
Il controller [main.aspx] non sa nulla dei calcoli delle imposte. È semplicemente responsabile della gestione del dialogo client-server e dell'esecuzione delle azioni richieste dal client. Per l'azione [calculate], si affiderà alla classe di business [tax].
6.1.3. La classe di business
La classe **impot** sarà definita come segue:
' imported namespaces
Imports System
' class
Namespace st.istia.univangers.fr
Public Class impot
Private limites(), coeffR(), coeffN() As Decimal
' manufacturer
Public Sub New(ByRef source As impotsData)
' data required for tax calculation
' come from an external source [source]
' we retrieve them - there may be an exception
Dim data() As Object = source.getData
limites = CType(data(0), Decimal())
coeffR = CType(data(1), Decimal())
coeffN = CType(data(2), Decimal())
End Sub
' tAX CALCULATION
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
' calculating the number of shares
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
' calculation of taxable income & family quota
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' tAX CALCULATION
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 tax fornendo al suo costruttore una fonte dati di tipo [impotsData]. Questa classe dispone di un metodo pubblico [getData] che recupera i tre array di dati necessari per calcolare l’imposta, come descritto in precedenza. Questo metodo è in grado di gestire un’eccezione se i dati non possono essere recuperati o se risultano errati. Una volta creato l'oggetto [tax], il suo metodo calculate può essere chiamato ripetutamente per calcolare l'imposta del contribuente in base al suo stato civile (coniugato o celibe), al numero di figli e allo stipendio annuale.
6.1.4. La classe di accesso ai dati
La classe [impotsData] è la classe che fornisce l'accesso ai dati. Si tratta di una classe astratta. È necessario creare una classe derivata per ogni nuova possibile fonte di dati (array, file flat, database, console, ecc.). 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
' data access method
Public MustOverride Function getData() As Object()
' data verification method
Protected Function checkData() As Integer
' verifies acquired data
' we need data
valide = Not limites Is Nothing AndAlso Not coeffr Is Nothing AndAlso Not coeffn Is Nothing
If Not valide Then Return 1
' we must have 3 arrays of the same size
If valide Then valide = limites.Length = coeffr.Length AndAlso limites.Length = coeffn.Length
If Not valide Then Return 2
' tables must be non-empty
valide = limites.Length <> 0
If Not valide Then Return 3
' each array must contain elements >=0 in ascending order
valide = check(limites, limites.Length - 1) AndAlso check(coeffr, coeffr.Length) AndAlso check(coeffn, coeffn.Length)
If Not valide Then Return 4
' all is good
Return 0
End Function
' checks the validity of an array's contents
Protected Function check(ByRef tableau() As Decimal, ByVal n As Integer) As Boolean
' array must have its first n elements >=0 and in strictly ascending order
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
' it's good
Return True
End Function
End Class
End Namespace
La classe ha i seguenti attributi protetti:
array dei limiti delle fasce di imposta | |
array di coefficienti applicati al reddito imponibile | |
tabella dei coefficienti applicati al numero di azioni | |
Booleano che indica se i dati (limits, coeffr, coeffn) sono stati verificati | |
Booleano che indica se i dati (limits, coeffr, coeffn) sono validi |
La classe non ha un costruttore. Ha un metodo astratto [getData] che le classi derivate devono implementare. Lo scopo di questo metodo è:
- assegnare valori ai tre array limits, 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 (limits, coeffr, coeffn). Ciò solleva le classi derivate dalla necessità di implementarli. Dovranno semplicemente utilizzarli.
La prima classe derivata che useremo è la seguente:
Imports System.Collections
Imports System
Namespace st.istia.univangers.fr
Public Class impotsArray
Inherits impotsData
' constructor with no arguments
Public Sub New()
' initializing tables with constants
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
' builder with three input tables
Public Sub New(ByRef limites() As Decimal, ByRef coeffr() As Decimal, ByRef coeffn() As Decimal)
' data storage
Me.limites = limites
Me.coeffr = coeffr
Me.coeffn = coeffn
checked = False
End Sub
Public Overrides Function getData() As Object()
' check data if necessary
Dim erreur As Integer
If Not checked Then erreur = checkData() : checked = True
' if invalid, then throw an exception
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' otherwise we return the three tables
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Questa classe, denominata [impotsArray], ha due costruttori:
- un costruttore senza argomenti che inizializza gli attributi (limits, coeffr, coeffn) della classe base con array hard-coded
- un costruttore che inizializza gli attributi (limits, coeffr, coeffn) della classe base con array passati come parametri
Il metodo [getData], che consente alle classi esterne di recuperare gli array (limits, coeffr, coeffn), verifica semplicemente 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 verificata. In questo modo, la fase di debug dell'applicazione web può concentrarsi sui livelli del controller e della vista. Un programma di test potrebbe presentarsi come segue:
' options
Option Strict On
Option Explicit On
' namespaces
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
Module test
Sub Main()
' interactive tax calculator
' the user enters three data points on the keyboard: married nbEnfants salary
' the program then displays the tax payable
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"
' tax object creation
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
' infinite loop
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Long
While True
' tax calculation parameters are requested
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()
' anything to do?
If paramètres Is Nothing OrElse paramètres = "" Then
Exit While
End If
' check the number of arguments in the input line
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
' checking the validity of parameters
If Not erreur Then
' married
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
' salary
Try
salaire = Integer.Parse(args(2))
If salaire < 0 Then
Throw New Exception
End If
Catch
erreur = True
End Try
End If
' if the parameters are correct - the tax is calculated
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 richiede all'utente di inserire le tre informazioni necessarie per calcolare le imposte:
- stato civile: o per sposato, n per non sposato
- numero di figli
- stipendio annuo
Il calcolo dell'imposta viene effettuato utilizzando un oggetto di tipo [tax] creato all'avvio dell'applicazione:
' tax object creation
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 origine dati, utilizziamo un oggetto di tipo [impotsArray]. Viene utilizzato il costruttore senza argomenti per questa classe, fornendo ai tre array (limites, coeffr, coeffn) valori hard-coded. La creazione di un oggetto [impot] può teoricamente generare un'eccezione perché, per crearsi, l'oggetto richiederà i dati (limites, coeffr, coeffn) dalla sua origine dati, che gli è stata passata come parametro, e questo recupero dei dati potrebbe innescare un'eccezione. Tuttavia, in questo caso, il metodo utilizzato per ottenere i dati (valori hard-coded) non può causare un'eccezione. Tuttavia, abbiamo lasciato attiva la gestione delle eccezioni per richiamare l'attenzione del lettore sulla possibilità che l'oggetto [impot] possa essere costruito in modo errato.
Ecco un esempio del programma precedente in azione:
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 tutte le classi [impot, impotsData, impotsArray] in un unico 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. Viste dell'applicazione web
L'applicazione avrà due viste: [form.aspx] e [errors.aspx]. Illustriamo il funzionamento dell'applicazione utilizzando alcuni screenshot. La vista [form.aspx] viene visualizzata quando l'URL [main.aspx] viene richiesto per la prima volta:

L'utente compila il modulo:

e utilizza il pulsante [Calcola] per ottenere il seguente risultato:

È possibile che vengano inseriti dati errati:

Cliccando sul pulsante [Calcola] si ottiene quindi una risposta diversa [errors.aspx]:

Può utilizzare il link [Back to Form] in alto per tornare alla vista [form.aspx] com'era prima dell'errore:

6.1.7. La vista [form.aspx]
La pagina [form.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 in questa pagina sono i seguenti:
"checked" se la casella di controllo [yes] deve essere selezionata, "" in caso contrario | |
lo stesso vale per la casella di controllo [no] | |
valore da inserire nel campo di immissione [txtChildren] | |
valore da inserire nel campo di immissione [txtSalary] | |
Valore da inserire nel campo di immissione [txtTax] |
La pagina presenta due moduli, ciascuno con un pulsante [submit]. Il pulsante [Calculate] funge da pulsante [submit] per il seguente modulo:
<form method="post" action="main.aspx?action=calcul">
...
<P>
<INPUT type="submit" value="Calculer">
</P>
</form>
Possiamo vedere che i parametri del modulo verranno inviati al controller con [action=calcul]. Il pulsante [Clear] è il pulsante [submit] per il seguente modulo:
<form method="post" action="main.aspx?action=effacer">
<INPUT type="submit" value="Effacer">
</form>
Possiamo vedere che i parametri del modulo verranno inviati al controller con [action=effacer]. Qui, il modulo non ha parametri. Conta solo l'azione.
I campi in [formulaire.aspx] vengono calcolati da [formulaire.aspx.vb]:
Imports System.Collections.Specialized
Public Class formulaire
Inherits System.Web.UI.Page
' page fields
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
' we retrieve the previous request in the
Dim form As NameValueCollection = Context.Items("formulaire")
' prepare the page to be displayed
' radio buttons
rdouichecked = ""
rdnonchecked = "checked"
If form("rdMarie").ToString = "oui" Then
rdouichecked = "checked"
rdnonchecked = ""
End If
' the rest
txtEnfants = CType(form("txtEnfants"), String)
txtSalaire = CType(form("txtSalaire"), String)
txtImpot = CType(Context.Items("txtImpot"), String)
End Sub
End Class
I campi in [main.aspx] vengono calcolati sulla base di due informazioni inserite dal controller nel contesto della pagina:
- Context.Items("form"): un dizionario 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] visualizza eventuali errori che potrebbero verificarsi durante l'esecuzione 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 per un elenco di errori | |
URL di un link | |
testo del link |
Questi campi vengono calcolati dal controller della pagina in [errors.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Public Class erreurs
Inherits System.Web.UI.Page
' page parameter
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
' retrieve context elements
Dim erreurs As ArrayList = CType(context.Items("erreurs"), ArrayList)
href = context.Items("href").ToString
lien = context.Items("lien").ToString
' we generate the HTML code from the list
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]
Rivediamo l'architettura MVC della nostra applicazione:

Logica dell'applicazioneClient
Il controller [main.aspx] deve gestire tre azioni:
- init: corrisponde alla prima richiesta del client. Il controller visualizza la vista [formulaire.aspx]
- calcul: corrisponde alla richiesta di calcolo dell'imposta. Se i dati nel modulo di input sono corretti, l'imposta viene calcolata utilizzando la classe di business [taxes]. Il controller restituisce al client la vista [form.aspx] così come è stata convalidata, insieme all'imposta calcolata. Se i dati nel modulo di input sono errati, il controller restituisce la vista [errors.aspx] con un elenco di errori e un link per tornare al modulo.
- return: corrisponde al ritorno al modulo dopo un errore. Il controller visualizza la vista [form.aspx] così come era stata convalidata prima dell'errore.
È inoltre noto che ogni richiesta all'applicazione passa attraverso il controller [global.asax], se presente. Abbiamo quindi, nel punto di ingresso dell'applicazione, una catena di due controller:
- [global.asax], che, a causa dell'architettura ASP.NET, riceve tutte le richieste all'applicazione
- [main.aspx], che, per scelta dello sviluppatore, riceve anch'esso tutte le richieste all'applicazione
La necessità di [main.aspx] deriva dal fatto che avremo una sessione da gestire. Abbiamo visto che [global.asax] non è adatto come controller in questo caso. Potremmo fare a meno di [global.asax] del tutto qui. Tuttavia, lo useremo per eseguire codice all'avvio dell'applicazione. Il diagramma MVC sopra riportato mostra che dovremo creare un oggetto [tax] per calcolare l'imposta. Non è necessario creare questo oggetto più volte; una volta è sufficiente. Lo creeremo quindi all'avvio dell'applicazione durante l'evento [Application_Start] gestito dal controller [global.asax]. Il codice per questo è 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)
' create an impot object
Dim objImpot As impot
Try
objImpot = New impot(New impotsArray)
' put the object in the application
Application("objImpot") = objImpot
' no error
Application("erreur") = False
Catch ex As Exception
'there has been an error, we note it in the application
Application("erreur") = True
End Try
End Sub
End Class
Una volta creato, l'oggetto [import] viene memorizzato nell'applicazione. È qui che le richieste provenienti da diversi client lo recupereranno. Poiché la creazione dell'oggetto [import] potrebbe fallire, gestiamo eventuali eccezioni e impostiamo una chiave [error] nell'applicazione per indicare se si è verificato o meno un errore durante la creazione dell'oggetto [import].
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
' first of all, we check whether the application has initialized correctly
If CType(Application("erreur"), Boolean) Then
' redirects to error page
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
' retrieve the action to be performed
Dim action As String
If Request.QueryString("action") Is Nothing Then
action = "init"
Else
action = Request.QueryString("action").ToString.ToLower
End If
' execute the action
Select Case action
Case "init"
' init application
initAppli()
Case "calcul"
' tax calculation
calculImpot()
Case "retour"
' back to form
retourFormulaire()
Case "effacer"
' init application
initAppli()
Case Else
' unknown action = init
initAppli()
End Select
End Sub
Private Sub initAppli()
' the pre-filled form is displayed
Context.Items("formulaire") = initForm()
Context.Items("txtImpot") = ""
Server.Transfer("formulaire.aspx", True)
End Sub
Private Function initForm() As NameValueCollection
' initialize the form
Dim form As New NameValueCollection
form.Set("rdMarie", "non")
form.Set("txtEnfants", "")
form.Set("txtSalaire", "")
Return form
End Function
Private Sub calculImpot()
' check the validity of the data entered
Dim erreurs As ArrayList = checkData()
' if there are errors, we report them
If erreurs.Count <> 0 Then
' save entries
Session.Item("formulaire") = Request.Form
' prepare the error page
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
context.Items("erreurs") = erreurs
Server.Transfer("erreurs.aspx")
End If
' no errors here - the tax is calculated
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
Request.Form("rdMarie") = "oui", _
CType(Request.Form("txtEnfants"), Integer), _
CType(Request.Form("txtSalaire"), Long))
' the result page is displayed
context.Items("txtImpot") = impot.ToString + " euro(s)"
context.Items("formulaire") = Request.Form
Server.Transfer("formulaire.aspx", True)
End Sub
Private Sub retourFormulaire()
' displays the form with values taken from the session
Context.Items("formulaire") = Session.Item("formulaire")
Context.Items("txtImpot") = ""
Server.Transfer("formulaire.aspx", True)
End Sub
Private Function checkData() As ArrayList
' initially no errors
Dim erreurs As New ArrayList
Dim erreur As Boolean = False
' married radio button
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
' no. of children
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
' salary
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
' return the list of errors
Return erreurs
End Function
End Class
Il controller inizia verificando che l'applicazione sia stata inizializzata correttamente:
' first of all, we check whether the application has initialized correctly
If CType(Application("erreur"), Boolean) Then
' redirects to error page
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 è stata inizializzata correttamente (non è stato possibile creare l'oggetto [import] necessario per il calcolo), visualizza la pagina di errore con i parametri appropriati. In questo caso, non è necessario inserire il link di ritorno nel modulo poiché l'intera applicazione non è disponibile. Un messaggio di errore generico viene inserito in [Context.Items("errors")] di tipo [ArrayList].
Se il controller determina che l'applicazione è operativa, analizza l'azione che gli viene richiesto di eseguire tramite il parametro [action]. Abbiamo già incontrato questa modalità di funzionamento molte volte. L'elaborazione di ciascun tipo di azione viene delegata a una funzione.
6.1.9.1. Le azioni init e delete
Queste due azioni devono visualizzare il modulo di input vuoto. Ricordiamo che questo modulo (vedi viste) ha due parametri:
- Context.Items("form"): un dizionario [NameValueCollection] contenente i valori dei campi HTML [rdmarie,txtEnfants,txtSalaire]
- Context.Items("txtImpot"): valore fiscale
La funzione [initAppli] inizializza questi due parametri per visualizzare un modulo vuoto.
6.1.9.2. L'azione di calcolo
Questa azione deve calcolare l'imposta dovuta in base ai dati inseriti nel modulo e restituire il modulo precompilato con i valori inseriti e l'importo dell'imposta calcolata. La funzione [calculImpot] responsabile di questo compito inizia verificando che i dati del modulo siano corretti:
- il campo [rdMarie] deve essere presente e avere il valore [yes] o [no]
- 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 non sono validi, il controller visualizza la vista [erreurs.aspx] dopo aver prima impostato i valori attesi per essa nel contesto:
- I messaggi di errore vengono inseriti in un oggetto [ArrayList], che viene poi aggiunto al contesto [Context.Items("errors")]
- 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 client, i valori inseriti nel modulo (Request.Form) vengono inseriti nella sessione, associati alla chiave "form". Ciò consentirà a una richiesta successiva di recuperarli.
Ci si potrebbe chiedere se sia utile verificare che i campi [rdMarie, txtEnfants, txtSalaire] siano presenti nella richiesta inviata dal cliente. Ciò non è necessario se siamo certi che il nostro cliente sia un browser che ha ricevuto la vista [formulaire.aspx] contenente questi campi. Non possiamo mai esserne certi. Mostreremo un esempio più avanti in cui il client è l'applicazione [curl] che abbiamo già incontrato. Interrogheremo l'applicazione senza inviare i campi che si aspetta e vedremo come reagisce. Si tratta di una regola che è stata enunciata più volte e che ribadiamo qui: un'applicazione non deve mai fare supposizioni sul tipo di client che la interroga. Per sicurezza, deve presumere che possa essere interrogata da un'applicazione programmata che potrebbe inviarle stringhe di parametri inaspettate. Deve comportarsi correttamente in tutti i casi.
Nel nostro caso, abbiamo verificato che i campi [rdMarie, txtEnfants, txtSalaire] fossero presenti nella richiesta, ma non abbiamo controllato se potesse contenerne altri. In questa applicazione, verrebbero ignorati. Tuttavia, sempre come misura di sicurezza, sarebbe utile registrare questo tipo di richiesta in un file di log e inviare un avviso all'amministratore dell'applicazione in modo che sia a conoscenza del fatto che l'applicazione sta ricevendo richieste "strane". Analizzandole nel file di log, potrebbe rilevare un potenziale attacco all'applicazione e quindi adottare le misure necessarie per proteggerla.
Se i dati previsti sono corretti, il controller avvia il calcolo delle imposte utilizzando l'oggetto [tax] memorizzato nell'applicazione. Quindi memorizza le due informazioni previste dalla vista [form.aspx] nel contesto:
- Context.Items("formulaire"): un dizionario [NameValueCollection] contenente i valori dei campi HTML [rdmarie,txtEnfants,txtSalaire], qui [Request.Form)], ovvero i valori precedentemente inseriti nel modulo
- Context.Items("txtImpot"): il valore dell'imposta appena calcolato
Il lettore attento potrebbe essersi chiesto durante la lettura di quanto sopra: dato che l'oggetto [impot] creato all'avvio dell'applicazione è condiviso tra tutte le richieste, potrebbero esserci conflitti di accesso che portano al danneggiamento dei dati dell'oggetto [impot]? Per rispondere a questa domanda, dobbiamo tornare al codice della classe [impot]. Le richieste chiamano il metodo [impot].calculateTax per ottenere l'imposta dovuta. Quindi questo è il codice che dobbiamo esaminare:
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
' calculating the number of shares
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
' calculation of taxable income & family quota
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' tAX CALCULATION
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. Un altro thread esegue quindi il metodo. Quali sono i rischi? Per scoprirlo, abbiamo aggiunto il seguente codice:
Dim impot As Long = CLng(revenu * coeffR(i) - nbParts * coeffN(i))
' wait 10 seconds
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]? Poiché 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 il thread viene sospeso. Quando il thread 2 si avvia, il suo contesto viene configurato con un nuovo stack e quindi con una nuova variabile locale [impot]. Quando a sua volta il thread 2 viene sospeso, anche il suo contesto viene salvato. Quando il thread 1 viene riavviato, il suo contesto viene ripristinato, compreso lo stack. Recupera 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 condotti con la pausa di 10 secondi descritta sopra hanno confermato che le richieste simultanee hanno effettivamente prodotto il risultato atteso.
6.1.9.3. L'azione di ritorno
Questa azione corrisponde al clic sul link [Torna al modulo] nella vista [errors.aspx] per tornare alla vista [form.aspx], che è precompilata con i valori precedentemente inseriti e salvati nella sessione. La funzione [returnForm] recupera queste informazioni. I due parametri previsti dalla vista [form.aspx] vengono inizializzati:
- Context.Items("form") con i valori precedentemente inseriti e salvati nella sessione
- Context.Items("txtImpot") con la stringa vuota
6.1.10. Test dell'applicazione web
Tutti i file sopra indicati sono collocati in una cartella denominata <application-path>.

In questa cartella viene creata una sottocartella [bin], nella quale viene collocato l'assembly [impot.dll] — generato dalla compilazione dei file delle classi di business: [impots.vb, impotsData.vb, impotsArray.vb] —. Il comando di compilazione richiesto è mostrato di seguito:
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 collocato in <percorso-applicazione>\bin affinché l'applicazione web possa accedervi. Il server Cassini viene avviato con i parametri (<percorso-applicazione>,/impots1). Utilizzando un browser, richiediamo l'URL [http://localhost/impots1/main.aspx]:

Compiliamo il modulo:

Quindi avviamo il calcolo delle imposte utilizzando il pulsante [Calcola]. Otteniamo la seguente risposta:

Quindi inseriamo dati errati:

Cliccando sul pulsante [Calcola] si ottiene la seguente risposta:

Cliccando sul link [Torna al modulo] si torna al modulo così com'era al momento dell'invio:

Infine, cliccando sul pulsante [Cancella] si resetta 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 dell'invio, il browser rinvierà i valori di tali parametri al server. Un altro client potrebbe non farlo, e in tal caso il server riceverebbe una richiesta con parametri mancanti. Il server deve sapere come comportarsi in questo caso. Un altro esempio è la convalida dei dati sul lato client. Se il modulo contiene dati da convalidare, questa convalida può essere eseguita sul lato client utilizzando script inclusi nel documento contenente il modulo. Il browser invierà il modulo solo se tutti i dati convalidati sul lato client sono validi. Si potrebbe quindi essere tentati, sul lato server, di presumere che riceveremo dati convalidati e non voler eseguire questa convalida una seconda volta. Sarebbe un errore. Infatti, un client diverso da un browser potrebbe inviare dati non validi al server e l'applicazione web potrebbe quindi comportarsi in modo imprevisto. Illustreremo questi punti utilizzando il client [curl].
Per prima cosa, 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 alcun parametro:
dos>curl --cookie ASP.NET_SessionId=ivthkl45tjdjrzznevqsf255 --include --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: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 vedere che l'applicazione web ha restituito la vista [errors] con tre messaggi di errore per i tre parametri mancanti. Ora inviamo alcuni 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 rilevati correttamente. 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 recuperato con successo l'imposta dovuta: 4.300 euro. La lezione che possiamo trarre da questo esempio è che non dobbiamo lasciarci fuorviare dal fatto che stiamo scrivendo un'applicazione web destinata a client che sono browser. Un'applicazione web è un servizio TCP/IP e questo protocollo di rete non rivela la natura dell'applicazione client di un servizio. Pertanto, non possiamo sapere se il client di un'applicazione web sia un browser o meno. Seguiamo quindi due regole:
- Alla ricezione di una richiesta da un client, non facciamo ipotesi sul client e verifichiamo che i parametri previsti nella richiesta siano presenti e validi
- costruiamo una risposta destinata ai browser, che generalmente consiste in documenti HTML
Un'applicazione web può essere realizzata per servire contemporaneamente diversi tipi di client, come browser e telefoni cellulari. Possiamo quindi includere un nuovo parametro in ogni richiesta che indichi il tipo di client. Pertanto, un browser richiederà il calcolo delle imposte tramite una richiesta all'URL http://machine/impots/main.aspx?client=navigateur&action=calcul, mentre un telefono cellulare invierà una richiesta all'URL http://machine/impots/main.aspx?client=mobile&action=calcul. La struttura MVC facilita lo sviluppo di un'applicazione di questo tipo. Essa assume la seguente forma:

Il blocco [Classi di business, classi di accesso ai dati] rimane invariato. Questo perché si tratta di un componente indipendente dal client. Il blocco [Controller] cambia solo leggermente, ma deve tenere conto di un nuovo parametro nella richiesta: il parametro [client], che indica il tipo di client gestito. Il blocco [Viste] deve generare viste per ogni tipo di client. Potrebbe essere utile tenere conto della presenza del parametro [client] nella query fin dalla fase di progettazione dell'applicazione, anche se l'obiettivo a breve o medio termine è limitato ai browser. Se in seguito l'applicazione dovesse supportare un nuovo tipo di client, sarà necessario scrivere solo le viste su misura per quel client.
6.2. Esempio 2
6.2.1. Il problema
Qui proponiamo di affrontare lo stesso problema di prima, ma modificando l'origine dati per l'oggetto [tax] creato dall'applicazione web. Nella versione precedente, l'origine dati forniva valori provenienti da array hard-coded nel codice. Questa volta, la nuova origine dati li recupererà da un'origine dati ODBC associata a un database MySQL.
6.2.2. L'origine dati ODBC
I dati si troveranno in una tabella denominata [IMPOTS] in un database MySQL denominato [dbimpots]. Il contenuto di questa tabella sarà il seguente:

Il proprietario del database è l'utente [admimpots] con password [mdpimpots]. Associamo un'origine dati ODBC a questo database. Prima di farlo, esaminiamo i diversi modi per accedere a un database utilizzando la piattaforma .NET.
Esistono molti database disponibili per le piattaforme Windows. Per accedervi, le applicazioni utilizzano programmi chiamati driver.

Nel diagramma sopra, il driver ha due interfacce:
- l'interfaccia I1 presentata all'applicazione
- l'interfaccia I2 verso il database
Per evitare che un'applicazione scritta per il database B1 debba essere riscritta in caso di migrazione a un database diverso B2, sono stati compiuti sforzi di standardizzazione sull'interfaccia I1. Se si utilizzano database che impiegano driver "standardizzati", al database B1 verrà fornito il driver P1, al database B2 il driver P2 e l'interfaccia I1 di questi due driver sarà identica. Pertanto, non sarà necessario riscrivere l'applicazione. Ad esempio, è possibile migrare un database Access a un database MySQL senza modificare l'applicazione.
Esistono due tipi di driver standardizzati:
- driver ODBC (Open DataBase Connectivity)
- Driver OLE DB (Object Linking and Embedding DataBase)
I driver ODBC forniscono l'accesso ai database. Le origini dati per i driver OLE DB sono più varie: database, sistemi di posta elettronica, directory, ecc. Non ci sono limiti. Qualsiasi origine dati può essere oggetto di un driver OLE DB se un fornitore decide di farlo. Il vantaggio è ovviamente significativo: si ha un accesso uniforme a un'ampia varietà di dati.
La piattaforma .NET 1.1 include tre tipi di classi di accesso ai dati:
- classi SQL Server.NET, per l'accesso ai database Microsoft SQL Server
- le classi Ole Db.NET, per l'accesso ai database da DBMS che offrono un driver OLE DB
- Classi ODBC.NET, per accedere a database da DBMS che offrono un driver ODBC
Il DBMS MySQL dispone da tempo di un driver ODBC. È quello che useremo ora. In Windows, selezioniamo [Menu Start/Pannello di controllo/Strumenti di amministrazione/Origini ODBC a 32 bit]. A seconda della versione di Windows, questo percorso può variare leggermente. Si aprirà la seguente applicazione, che ci consentirà di creare la nostra origine ODBC:

Creeremo una fonte dati di sistema, ovvero una fonte dati che qualsiasi utente del computer può utilizzare. Pertanto, in alto, selezioniamo la scheda [Fonte dati di sistema]. La pagina visualizzata presenta un pulsante [Aggiungi], che utilizzeremo per creare una nuova fonte dati ODBC:

La procedura guidata richiede di selezionare il driver ODBC da utilizzare. Windows viene fornito con una serie di driver ODBC preinstallati. Il driver ODBC di MySQL non è incluso in questo set. È quindi necessario installarlo prima. È possibile trovarlo online digitando i termini di ricerca "MySQL ODBC" o "MyODBC" in un motore di ricerca. In questo caso, abbiamo installato il driver [MySQL ODBC 3.51]. Lo selezioniamo e facciamo clic su [Fine]:

Dovrai fornire le seguenti informazioni:
il nome che identificherà l'origine dati ODBC. Qualsiasi applicazione Windows potrà accedere all'origine utilizzando questo nome | |
Qualsiasi testo che descriva l'origine dati | |
Il nome del computer che ospita il DBMS MySQL. In questo caso, si tratta del computer locale. Potrebbe essere un computer remoto. Ciò consentirebbe a un'applicazione Windows di accedere a un database remoto senza alcuna codifica speciale. Questo è uno dei principali vantaggi della sorgente ODBC. | |
Un DBMS MySQL può gestire più database. Qui specifichiamo quale vogliamo gestire: dbimpots | |
Il nome di un utente registrato nel sistema di gestione del database MySQL. L'accesso alla fonte dati avverrà con il nome di questo utente. In questo caso: admimpots | |
La password di questo utente. In questo caso: mdpimpots | |
Porta operativa del DBMS MySQL. Per impostazione predefinita, è la porta 3306. Non l'abbiamo modificata |
Una volta fatto questo, verifichiamo la validità delle nostre impostazioni di connessione utilizzando il pulsante [Test Data Source]:

Una volta fatto ciò, possiamo fidarci della nostra origine dati ODBC. Ora possiamo utilizzarla. Facciamo clic su [OK] tutte le volte necessarie per uscire dalla procedura guidata ODBC.
Se il lettore non dispone del DBMS MySQL, può scaricarlo gratuitamente all'indirizzo [http://www.mysql.com]. Di seguito, illustriamo i passaggi per creare una sorgente ODBC con Access. I primi passaggi sono identici a quelli descritti in precedenza. Aggiungiamo una nuova sorgente dati di sistema:

Il driver selezionato sarà [Microsoft Access Driver]. Fare clic su [Fine] per procedere alla definizione della sorgente ODBC:

Le informazioni da fornire sono le seguenti:
Il nome che identificherà l'origine dati ODBC. Qualsiasi applicazione Windows potrà accedere all'origine utilizzando questo nome | |
Qualsiasi testo che descriva l'origine dati | |
Nome completo del file di Access da utilizzare |
6.2.3. Una nuova classe di accesso ai dati
Rivediamo la struttura MVC della nostra applicazione:

Nel diagramma sopra, la classe [impotsData] è responsabile del recupero dei dati. In questo caso, recupererà i dati dal database MySQL [dbimpots]. Come abbiamo appreso nella versione precedente di questa applicazione, [impotsData] è una classe astratta che deve essere derivata ogni volta che vogliamo adattarla a una nuova fonte di dati. Rivediamo 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
' data access method
Public MustOverride Function getData() As Object()
' data verification method
Protected Function checkData() As Integer
' verifies acquired data
...
End Function
' checks the validity of an array's contents
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 predefinito di [impotsData] non è adatto
- il metodo [getData], che restituisce i tre array (limites, coeffr, coeffn)
Creiamo la classe [impotsODBC], che recupererà i dati (limits, coeffr, coeffn) da una sorgente ODBC di cui forniremo il nome:
Imports System.Data.Odbc
Imports System.Data
Imports System.Collections
Imports System
Namespace st.istia.univangers.fr
Public Class impotsODBC
Inherits impotsData
' instance variables
Protected DSNimpots As String
' manufacturer
Public Sub New(ByVal DSNimpots As String)
' we note the three pieces of information
Me.DSNimpots = DSNimpots
End Sub
Public Overrides Function getdata() As Object()
' initializes the three limit tables, coeffr, coeffn from
' the contents of the [impots] table in the ODBC DSNimpots database
' limites, coeffr, coeffn are the three columns of this table
' can launch various exceptions
Dim connectString As String = "DSN=" + DSNimpots + ";" ' base connection chain
Dim impotsConn As OdbcConnection = Nothing ' the connection
Dim sqlCommand As OdbcCommand = Nothing ' the SQL command
' the SELECT query
Dim selectCommand As String = "select limites,coeffr,coeffn from impots"
' tables to retrieve data
Dim aLimites As New ArrayList
Dim aCoeffR As New ArrayList
Dim aCoeffN As New ArrayList
Try
' attempt to access the database
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
' create a command object
sqlCommand = New OdbcCommand(selectCommand, impotsConn)
' execute the query
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
' Using the recovered table
While myReader.Read()
' the data of the current line are put in the tables
aLimites.Add(myReader("limites"))
aCoeffR.Add(myReader("coeffr"))
aCoeffN.Add(myReader("coeffn"))
End While
' freeing up resources
myReader.Close()
impotsConn.Close()
Catch e As Exception
Throw New Exception("Erreur d'accès à la base de données (" + e.Message + ")")
End Try
' dynamic tables are placed in static tables
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
' verify acquired data
Dim erreur As Integer = checkData()
' if invalid data, throws an exception
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' otherwise we return the three tables
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Diamo un'occhiata al costruttore:
' manufacturer
Public Sub New(ByVal DSNimpots As String)
' we note the three pieces of information
Me.DSNimpots = DSNimpots
End Sub
Accetta come parametro il nome dell'origine dati ODBC contenente i dati da recuperare. Il costruttore si limita a memorizzare questo nome. Il metodo [getData] è responsabile della lettura dei dati dalla tabella [impots] e del loro inserimento in tre array (limites, coeffr, coeffn). Commentiamo il codice:
- i parametri per la connessione all'origine dati ODBC sono definiti, ma la connessione non è aperta
' base connection chain
Dim connectString As String = "DSN=" + DSNimpots + ";"
' a database connection object is created - this connection is not open
Dim impotsConn As OdbcConnection = New OdbcConnection(connectString)
- Definire tre oggetti [ArrayList] per recuperare i dati dalla tabella [impots]:
' tables to retrieve data
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. Apriamo la connessione al database:
' on tente d'accéder à la base de données
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
- Eseguiamo il comando [select] sulla connessione aperta. Otteniamo un oggetto [OdbcDataReader] che ci permetterà di iterare attraverso le righe della tabella risultante dal select:
' on crée un objet command
Dim sqlCommand As OdbcCommand = New OdbcCommand(selectCommand, impotsConn)
' on exécute la requête
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
- Percorriamo la tabella dei risultati, riga per riga. Per farlo, utilizziamo il metodo [Read] dell'oggetto [OdbcDataReader] ottenuto in precedenza. Questo metodo esegue due operazioni:
- Avanza di una riga nella tabella. Inizialmente, il cursore è posizionato 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. Questo restituisce un oggetto che rappresenta il valore della colonna. Iteriamo attraverso l'intera tabella per popolare il suo contenuto nei tre oggetti [ArrayList]:
' Exploitation de la table récupérée
While myReader.Read()
' les données de la ligne courante sont mis dans les tableaux
aLimites.Add(myReader("limites"))
aCoeffR.Add(myReader("coeffr"))
aCoeffN.Add(myReader("coeffn"))
- Una volta fatto questo, liberiamo le risorse associate alla connessione:
- Il contenuto dei tre oggetti [ArrayList] viene trasferito in tre array standard:
' dynamic tables are placed in static tables
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 caricati i dati della tabella [impots] nei tre array, non resta che verificarne il contenuto utilizzando il metodo [checkData] della classe base [impotsData]:
' verify acquired data
Dim erreur As Integer = checkData()
' if invalid data, throws an exception
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' otherwise we return the three tables
Return New Object() {limites, coeffr, coeffn}
6.2.4. Test per la classe di accesso ai dati
Un programma di test potrebbe essere simile a questo:
Option Explicit On
Option Strict On
' namespaces
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' test pg
Module testimpots
Sub Main(ByVal arguments() As String)
' interactive tax calculator
' the user enters three data points on the keyboard: married nbEnfants salary
' the program then displays the tax payable
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"
' checking program parameters
If arguments.Length <> 1 Then
' error msg
Console.Error.WriteLine(syntaxe1)
' end
Environment.Exit(1)
End If
' retrieve the arguments
Dim DSNimpots As String = arguments(0)
' tax object creation
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
' infinite loop
While True
' initially no errors
Dim erreur As Boolean = False
' tax calculation parameters are requested
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()
' anything to do?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' check the number of arguments in the input line
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
' checking the validity of parameters
' married
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
' salary
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
' parameters are correct - tax is calculated
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 dell'origine dati ODBC da utilizzare
Il calcolo delle imposte viene eseguito utilizzando un oggetto di tipo [tax] creato all'avvio dell'applicazione:
' tax object creation
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 inizializzata, l'applicazione richiede ripetutamente all'utente di inserire le tre informazioni necessarie per calcolare l'imposta:
- stato civile: o per sposato, n per non sposato
- il numero di figli
- stipendio annuo
Tutte le classi sono compilate:
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 programma di test viene quindi compilato:
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 inizialmente con l'origine dati ODBC di 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)
Passiamo dall'origine ODBC a un'origine 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. Viste dell'applicazione web
Sono le stesse dell'applicazione precedente: formulaire.aspx e erreurs.aspx
6.2.6. Controller dell'applicazione [global.asax, main.aspx]
È necessario modificare solo il controller [global.asax]. È responsabile della creazione dell'oggetto [impot] all'avvio dell'applicazione. Il costruttore di questo oggetto ha un unico parametro: l'oggetto [impotsData], responsabile del recupero dei dati. Questo parametro cambia quindi per ogni nuovo tipo di origine 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)
' create an impot object
Dim objImpot As impot
Try
objImpot = New impot(New impotsODBC(ConfigurationSettings.AppSettings("DSNimpots")))
' put the object in the application
Application("objImpot") = objImpot
' no error
Application("erreur") = False
Catch ex As Exception
'there has been an error, we note it in the application
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
L'origine dati per l'oggetto [impot] è ora un oggetto [impotODBC]. Questo oggetto accetta come parametro il nome DSN dell'origine dati ODBC da utilizzare. Anziché codificare in modo rigido questo nome nel codice, lo inseriamo 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>
Sappiamo che il valore di una chiave C nella sezione <appSettings> del file [web.config] viene recuperato nel codice dell'applicazione utilizzando [ConfigurationSettings.AppSettings(C)].
Per determinare la causa dell'eccezione, registriamo il messaggio di eccezione nell'applicazione in modo che rimanga disponibile per le query. 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
' first of all, we check whether the application has initialized correctly
If CType(Application("erreur"), Boolean) Then
' redirects to error page
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
' retrieve the action to be performed
...
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 di accesso ai dati
- il controller [global.asax.vb] è stato modificato in due punti: la costruzione dell'oggetto [import] e la registrazione del messaggio relativo a qualsiasi eccezione nell'applicazione
- 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 svolto principalmente in 1, ovvero al di fuori dell'applicazione web. Ciò è stato reso possibile dall'architettura MVC dell'applicazione, che separa il controller dalle classi di business. Questo è il punto centrale di tale architettura. Si potrebbe dimostrare che, con un file [web.config] appropriato, qualsiasi modifica al controller dell'applicazione avrebbe potuto essere evitata. È possibile specificare in [web.config] il nome della classe di accesso ai dati da istanziare dinamicamente, ad esempio —, nonché i vari parametri richiesti per tale istanziazione. Con queste informazioni, [global.asax] può istanziare l'oggetto di accesso ai dati. La modifica dell'origine dati equivale quindi a:
- creare la classe di accesso ai dati per quella fonte, se non esiste ancora
- modificare il file [web.config] per consentire la creazione dinamica di un'istanza di questa classe in [global.asax]
6.2.8. Test dell'applicazione web
Tutti i file sopra indicati sono collocati in una cartella denominata <application-path>.

In questa cartella viene creata una sottocartella [bin], nella quale viene collocato l'assembly [impot.dll] — generato dalla compilazione dei file delle classi di business: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb] —. Il comando di compilazione richiesto è mostrato di seguito:
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 <percorso-applicazione>\bin affinché l'applicazione web possa accedervi. Il server Cassini viene avviato con i parametri (<percorso-applicazione>,/impots2). I test danno gli stessi risultati della versione precedente, poiché la presenza del database è trasparente per l'utente. Per dimostrare questa presenza, tuttavia, ci assicuriamo che la sorgente ODBC non sia disponibile arrestando il DBMS MySQL e richiediamo l'URL [http://localhost/impots2/main.aspx]. Riceviamo la seguente risposta:

6.3. Esempio 3
6.3.1. Il problema
Qui proponiamo di affrontare lo stesso problema modificando nuovamente l'origine dati dell'oggetto [impot] creato dall'applicazione web. Questa volta, la nuova origine dati sarà un database ACCESS accessibile tramite un driver OLEDB. Il nostro obiettivo è dimostrare un altro modo per accedere a un database.
6.3.2. L'origine dati OLEDB
I dati si troveranno in una tabella denominata [IMPOTS] in un database ACCESS. Il contenuto di questa tabella sarà il seguente:

6.3.3. La classe di accesso ai dati
Rivediamo la struttura MVC della nostra applicazione:

- Nel diagramma sopra, la classe [impotsData] è responsabile del recupero dei dati. Questa volta, dovrà farlo da una fonte OLEDB.
Creiamo la classe [impotsOLEDB], che recupererà i dati (limits, coeffr, coeffn) da una fonte ODBC che chiameremo:
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
' instance variables
Protected chaineConnexion As String
' manufacturer
Public Sub New(ByVal chaineConnexion As String)
' we note the three pieces of information
Me.chaineConnexion = chaineConnexion
End Sub
Public Overrides Function getData() As Object()
' initializes the three limit tables, coeffr, coeffn from
' the contents of the [impots] table in the OLEDB [chaineConnexion] database
' limits, coeffr, coeffn are the three columns of this table
' can launch various exceptions
' create a DataAdapter object to read data from source OLEDB
Dim adaptateur As New OleDbDataAdapter("select limites,coeffr,coeffn from impots", chaineConnexion)
' create a memory image of the select result
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
' retrieve the contents of the impots table
Dim lignesImpots As DataRowCollection = contenu.Rows
' dimensioning of reception panels
Me.limites = New Decimal(lignesImpots.Count - 1) {}
Me.coeffr = New Decimal(lignesImpots.Count - 1) {}
Me.coeffn = New Decimal(lignesImpots.Count - 1) {}
' transfer the contents of the impots table to the tables
Dim i As Integer
Dim ligne As DataRow
Try
For i = 0 To lignesImpots.Count - 1
' table line i
ligne = lignesImpots.Item(i)
' retrieve the contents of the line
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
' verify acquired data
Dim erreur As Integer = checkData()
' if invalid data, throws an exception
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' otherwise we return the three tables
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Diamo un'occhiata al costruttore:
' manufacturer
Public Sub New(ByVal chaineConnexion As String)
' we note the three pieces of information
Me.chaineConnexion = chaineConnexion
End Sub
Riceve come parametro la stringa di connessione per l'origine OLEDB contenente i dati da recuperare. Il costruttore si limita a memorizzarla. Una stringa di connessione contiene tutti i parametri richiesti dal driver OLEDB per connettersi all'origine OLEDB. In genere è piuttosto complessa. Per trovare la stringa di connessione per i database ACCESS, è possibile utilizzare lo strumento [WebMatrix]. Avviare questo strumento. Esso fornisce una finestra per la connessione a un'origine dati:
![]() ![]() | ![]() ![]() |
Utilizzando l'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 [Dati], le icone rappresentano la connessione:

Ora, creiamo un nuovo file .aspx tramite [File/Nuovo file]:

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

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

Facciamo clic con il tasto destro sull'oggetto [AccessDataSourceControl] sottostante per accedere alle sue proprietà:

La stringa di connessione OLEDB al database ACCESS è fornita dalla proprietà [ConnectionString] sopra:
Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\serge\devel\aspnet\poly\chap5\impots\3\impots.mdb
Possiamo vedere che questa stringa è composta da una parte fissa e una parte variabile, che è semplicemente il nome del file ACCESS. Useremo questo fatto per generare la stringa di connessione alla nostra origine dati OLEDB.
Torniamo ora alla nostra classe [impotsOLEDB]. Il metodo [getData] è responsabile della lettura dei dati dalla tabella [impots] e del loro inserimento 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 SQL SELECT. A tal fine, definiamo la query [select] da eseguire e la associamo all'oggetto [DataAdapter]. Il costruttore di [DataAdapter] richiede anche la stringa di connessione che utilizzerà per connettersi all'origine OLEDB
' on crée un objet DataAdapter pour lire les données de la source OLEDB
Dim adaptateur As New OleDbDataAdapter("select limites,coeffr,coeffn from impots", chaineConnexion)
- Eseguiamo il comando [select] utilizzando il metodo [Fill] dell'oggetto [DataAdapter]. Il risultato del [select] viene caricato in un oggetto [DataTable] creato a questo scopo. Un oggetto [DataTable] è la rappresentazione in memoria di una tabella di database, ovvero un insieme di righe e colonne. Gestiamo un'eccezione che potrebbe verificarsi se, ad esempio, la stringa di connessione non è corretta.
' on crée une image en mémoire du résultat du select
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 [content], abbiamo la tabella [taxes] restituita dall'istruzione [select]. Un oggetto [DataTable] è una tabella, ovvero un insieme di righe. Queste sono accessibili tramite la proprietà [rows] di [DataTable]:
' retrieve the contents of the impots table
Dim lignesImpots As DataRowCollection = contenu.Rows
- Ogni elemento della raccolta [taxRows] è un oggetto [DataRow] che rappresenta una riga della tabella. Questa riga ha colonne accessibili tramite l'oggetto [DataRow] attraverso la sua proprietà [Item]. [DataRow].[Item(i)] è la colonna numero i di [DataRow]. Iterando attraverso la raccolta di righe (la raccolta DataRows di taxRows) e la raccolta di colonne per ogni riga, possiamo recuperare l'intera tabella:
' dimensioning of reception panels
Me.limites = New Decimal(lignesImpots.Count - 1) {}
Me.coeffr = New Decimal(lignesImpots.Count - 1) {}
Me.coeffn = New Decimal(lignesImpots.Count - 1) {}
' transfer the contents of the impots table to the tables
Dim i As Integer
Dim ligne As DataRow
Try
For i = 0 To lignesImpots.Count - 1
' table line i
ligne = lignesImpots.Item(i)
' retrieve the contents of the line
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 caricati i dati della tabella [taxes] nei tre array, non resta che verificarne il contenuto utilizzando il metodo [checkData] della classe base [taxData]:
' verify acquired data
Dim erreur As Integer = checkData()
' if invalid data, throws an exception
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' otherwise we return the three tables
Return New Object() {limites, coeffr, coeffn}
6.3.4. Test per la classe di accesso ai dati
Un programma di test potrebbe essere simile a questo:
Option Explicit On
Option Strict On
' namespaces
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' test pg
Module testimpots
Sub Main(ByVal arguments() As String)
' interactive tax calculator
' the user enters three data points on the keyboard: married nbEnfants salary
' the program then displays the tax payable
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"
' checking program parameters
If arguments.Length <> 1 Then
' error msg
Console.Error.WriteLine(syntaxe1)
' end
Environment.Exit(1)
End If
' retrieve the arguments
Dim chemin As String = arguments(0)
' prepare the connection chain
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + chemin
' tax object creation
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
' infinite loop
While True
' initially no errors
Dim erreur As Boolean = False
' tax calculation parameters are requested
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()
' anything to do?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' check the number of arguments in the input line
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
' checking the validity of parameters
' married
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
' salary
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
' parameters are correct - tax is calculated
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 utilizzare
Il calcolo delle imposte viene eseguito utilizzando un oggetto di tipo [tax] creato all'avvio dell'applicazione:
' retrieve the arguments
Dim chemin As String = arguments(0)
' prepare the connection chain
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + chemin
' tax object creation
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 creata utilizzando le informazioni ottenute con [WebMatrix].
Una volta inizializzata, l'applicazione richiede ripetutamente all'utente di inserire le tre informazioni necessarie per calcolare le imposte:
- stato civile: o per sposato, n per non sposato
- numero di figli
- stipendio annuo
Tutte le classi sono state compilate:
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 prova e l'applicazione 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 eseguire l'applicazione con un file ACCESS errato:
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 le stesse dell'applicazione precedente: formulaire.aspx e erreurs.aspx
6.3.6. Controller dell'applicazione [global.asax, main.aspx]
È necessario modificare solo il controller [global.asax]. Esso è responsabile della creazione dell'oggetto [impot] all'avvio dell'applicazione. Il costruttore di questo oggetto ha un unico parametro: l'oggetto [impotsData] responsabile del recupero dei dati. Questo parametro cambia quindi poiché stiamo cambiando le origini 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)
' create an impot object
Dim objImpot As impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
' put the object in the application
Application("objImpot") = objImpot
' no error
Application("erreur") = False
Catch ex As Exception
'there has been an error, we note it in the application
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
L'origine dati per l'oggetto [impot] è ora un oggetto [impotOLEDB]. Questo oggetto accetta come parametro la stringa di connessione per l'origine dati OLEDB da utilizzare. Questa stringa è memorizzata 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 alla versione precedente:
- è stata creata una nuova classe di accesso ai dati
- il controller [global.asax.vb] è stato modificato in un punto: la costruzione dell'oggetto [import]
- È stato aggiunto un file [web.config]
6.3.8. Test dell'applicazione web
Tutti i file sopra indicati sono collocati in una cartella denominata <percorso-applicazione>.

In questa cartella viene creata una sottocartella [bin], nella quale viene collocato l'assembly [impot.dll] — generato dalla compilazione dei file delle classi di business: [impots.vb, impotsData.vb, impotsArray.vb, impotsOLEDB.vb] —. Il comando di compilazione richiesto è mostrato di seguito:
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 <percorso-applicazione>\bin in modo che l'applicazione web possa accedervi. Il server Cassini viene avviato con i parametri (<percorso-applicazione>,/impots3). I test producono gli stessi risultati della versione precedente.
6.4. Esempio 4
6.4.1. Il problema
Proponiamo ora di trasformare la nostra applicazione in un'applicazione di simulazione del calcolo delle imposte. Un utente potrà eseguire calcoli fiscali successivi, che gli verranno presentati in una nuova vista simile a questa:

6.4.2. La struttura MVC dell'applicazione
La struttura MVC dell'applicazione risulta così:

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] rimane invariata. 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 [form] include uno script JavaScript che convalida i dati inseriti prima di inviarli al server, come mostrato nell'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(){
// vérification des paramètres avant de les envoyer au serveur
with(document.frmImpots){
//nbre d'enfants
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
// le modéle n'est pas vérifié
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
txtEnfants.focus();
return;
}//if
//salaire
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
// le modéle n'est pas vérifié
alert("Le salaire n'a pas été donné ou est incorrect");
txtSalaire.focus();
return;
}//if
// c'est bon - on envoie le formulaire au serveur
submit();
}//with
}//calculer
</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 nella pagina sono gli stessi delle versioni precedenti. Il campo dinamico per l'importo dell'imposta è stato rimosso. Il pulsante [Calcola] non è più un pulsante [invia]. È un [pulsante] e, quando viene cliccato, viene eseguita la funzione JavaScript [calculate()]:
<INPUT type="button" value="Calculer" onclick="calculer()">
Abbiamo assegnato al modulo il nome [frmImpots] in modo da poterlo richiamare nello script [calculer]:
<form name="frmImpots" method="post" action="main.aspx?action=calcul">
La funzione JavaScript [calculate] utilizza espressioni regolari per convalidare i campi nei 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 suoi campi dinamici dal seguente controller [formulaire.aspx.vb]:
Imports System.Collections.Specialized
Public Class formulaire
Inherits System.Web.UI.Page
' page fields
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
' we retrieve the previous request in the
Dim form As NameValueCollection = Context.Items("formulaire")
' prepare the page to be displayed
' radio buttons
rdouichecked = ""
rdnonchecked = "checked"
If form("rdMarie").ToString = "oui" Then
rdouichecked = "checked"
rdnonchecked = ""
End If
' the rest
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é questo campo è stato rimosso dalla pagina.
La vista [simulations.aspx] appare 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 contiene tre campi dinamici:
Codice HTML per un elenco di simulazioni sotto forma di righe di una tabella HTML | |
URL di un link | |
testo del link |
Sono generati dal controller [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
'simulations are retrieved from the context
Dim simulations As ArrayList = CType(context.Items("simulations"), ArrayList)
' each simulation is an array of 4 string elements
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
' recover the other elements of the context
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 che rappresentano le informazioni della simulazione (coniugato, figli, stipendio, imposte). | |
URL di un link | |
Testo del link |
6.4.4. I controller [global.asax, main.aspx]
Esaminiamo l'architettura MVC della nostra applicazione:

Il controller [main.aspx] deve gestire tre azioni:
- init: corrisponde alla prima richiesta del client. Il controller visualizza la vista [form.aspx]
- calcul: corrisponde alla richiesta di calcolo delle imposte. Se i dati nel modulo di input sono corretti, l'imposta viene calcolata utilizzando la classe di business [impotsODBC]. Il controller restituisce al client la vista [simulations.aspx] con il risultato della simulazione corrente più tutte quelle precedenti. Se i dati nel modulo di input non sono corretti, il controller restituisce la vista [erreurs.aspx] con l'elenco degli errori e un link per tornare al modulo.
- return: corrisponde al ritorno al modulo dopo un errore. Il controller visualizza la vista [form.aspx] così come era stata convalidata prima dell'errore.
In questa nuova versione, è cambiata solo l'azione [calcul]. Infatti, se i dati sono validi, deve portare alla vista [simulations.aspx], mentre in precedenza portava alla vista [form.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
' first of all, we check whether the application has initialized correctly
...
' execute the action
Select Case action
Case "init"
' init application
initAppli()
Case "calcul"
' tax calculation
calculImpot()
Case "retour"
' back to form
retourFormulaire()
Case "effacer"
' init application
initAppli()
Case Else
' unknown action = init
initAppli()
End Select
End Sub
...
Private Sub calculImpot()
' save entries
Session.Item("formulaire") = Request.Form
' check the validity of the data entered
Dim erreurs As ArrayList = checkData()
' if there are errors, we report them
If erreurs.Count <> 0 Then
' prepare the error page
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
context.Items("erreurs") = erreurs
Server.Transfer("erreurs.aspx")
End If
' no errors here - the tax is calculated
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
Request.Form("rdMarie") = "oui", _
CType(Request.Form("txtEnfants"), Integer), _
CType(Request.Form("txtSalaire"), Long))
' the result is added to existing simulations
Dim simulations As ArrayList
If Not Session.Item("simulations") Is Nothing Then
simulations = CType(Session.Item("simulations"), ArrayList)
Else
simulations = New ArrayList
End If
' add current simulation
Dim simulation() As String = New String() {Request.Form("rdMarie").ToString, _
Request.Form("txtEnfants").ToString, Request.Form("txtSalaire").ToString, _
impot.ToString}
simulations.Add(simulation)
' put simulations in session and context
context.Items("simulations") = simulations
Session.Item("simulations") = simulations
' the result page is displayed
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
Server.Transfer("simulations.aspx", True)
End Sub
...
End Class
Abbiamo incluso sopra solo ciò che è necessario per comprendere le modifiche presenti esclusivamente nella funzione [calculImpots]:
- In primo luogo, la funzione salva il modulo [Request.Form] nella sessione in modo che il modulo possa essere rigenerato nello stato in cui è stato convalidato. Ciò deve essere fatto in tutti i casi, poiché sia che l'operazione dia come risultato la risposta [erreurs.aspx] o la risposta [simulations.aspx], si ritorna al modulo tramite il link [Torna al modulo]. Per ripristinare correttamente il modulo, i suoi valori devono essere stati salvati in precedenza nella sessione.
- Se i dati inseriti sono corretti, la funzione aggiunge la simulazione corrente (stato civile, figli, stipendio, imposte) all'elenco delle simulazioni. Tale elenco si trova nella sessione associata alla chiave "simulations".
- L'elenco delle simulazioni viene memorizzato nella sessione per un uso futuro. Viene inoltre inserito nel contesto corrente poiché è lì che la vista [simulations.aspx] si aspetta di trovarlo
- La vista [simulations.aspx] viene visualizzata una volta che le altre informazioni di cui ha bisogno sono state inserite nel contesto
6.4.5. Riepilogo delle modifiche
L'applicazione è pronta per essere testata. Elenchiamo le modifiche apportate alle versioni precedenti:
- È stata creata una nuova vista
- Il controller [main.aspx.vb] è stato modificato in un punto: la gestione dell'azione [calcul]
6.4.6. Test dell'applicazione web
Il lettore è invitato a eseguire i test. Ecco un promemoria della procedura. Tutti i file dell'applicazione sono collocati in una cartella denominata <percorso-applicazione>. All'interno di questa cartella viene creata una sottocartella [bin], contenente l'assembly [impot.dll] generato dalla compilazione dei file delle classi di business: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb]. Il file [impot.dll] prodotto da questo comando deve essere collocato in <application-path>\bin in modo che l'applicazione web possa accedervi. Il server Cassini viene avviato con i parametri (<application-path>,/impots4).
6.5. Conclusione
Gli esempi precedenti hanno illustrato, in uno scenario concreto, i meccanismi comunemente utilizzati nello sviluppo web. Abbiamo utilizzato costantemente l'architettura MVC per il suo valore didattico. Avremmo potuto gestire questi stessi esempi in modo diverso e forse più semplice senza questa architettura. Tuttavia, essa offre vantaggi significativi non appena l'applicazione diventa un po' complessa con più pagine.
Potremmo continuare i nostri esempi in vari modi. Eccone alcuni:
- L'utente potrebbe voler salvare le proprie simulazioni nel tempo. Potrebbe eseguire simulazioni il giorno D e recuperarle il giorno D+3, ad esempio. Una possibile soluzione a questo problema è l'uso dei cookie. Sappiamo che il token di sessione tra il server e un client viene trasmesso tramite questo meccanismo. Potremmo anche utilizzare questo meccanismo per trasmettere le simulazioni tra il client e il server.
- Contemporaneamente all'invio della pagina contenente i risultati della simulazione, il server invia un cookie nelle sue intestazioni HTTP contenente una stringa che rappresenta le simulazioni. Poiché queste si trovano in un oggetto [ArrayList], tale oggetto deve essere convertito in una [String]. Il server imposterebbe una durata per il cookie, ad esempio 30 giorni.
- Il browser del client memorizza i cookie ricevuti in un file e li rinvia ogni volta che effettua una richiesta al server che li ha inviati, a condizione che siano ancora validi (durata non superata). Il server riceverà una stringa di caratteri [String] per le simulazioni, che dovrà convertire in un oggetto [ArrayList].
I cookie sono gestiti da [Response.Cookies] quando vengono inviati al client e da [Request.Cookies] quando vengono ricevuti sul server.
- Il meccanismo sopra descritto può diventare piuttosto dispendioso in termini di risorse se il numero di simulazioni è elevato. Inoltre, è comune che un utente cancelli periodicamente i propri cookie eliminandoli tutti, anche se altrimenti consente al proprio browser di utilizzarli. Quindi, prima o poi, i cookie di simulazione andranno persi. Potremmo quindi volerli memorizzare sul server piuttosto che sul client, ad esempio in un database. Per collegare le simulazioni a un utente specifico, l'applicazione potrebbe iniziare con una fase di autenticazione che richiede un nome utente e una password, che a loro volta sono memorizzati in un database o in qualsiasi altro tipo di archivio dati.
- Potremmo anche voler rendere più sicura l'operazione 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 disponibili sulla pagina che gli è stata inviata
Cosa succede, ad esempio, se l'utente richiede direttamente l'URL [http://localhost/impots4/formulaire.aspx]? Questo scenario è improbabile poiché l'utente non è a conoscenza di questo URL. Tuttavia, deve essere preso in considerazione. Può essere gestito dal controller dell'applicazione [global.asax], che elabora tutte le richieste effettuate all'applicazione. Può quindi verificare che la risorsa richiesta sia effettivamente [main.aspx].
Uno scenario più probabile è che un utente non utilizzi le azioni disponibili 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:

Il server va in crash. Questo è normale. Per l'azione [return], 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 fornisce 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. Possiamo utilizzare il seguente meccanismo:
- Prima di inviare la risposta al client, il controller memorizza le informazioni che identificano questa pagina nella sessione del client
- quando riceve una nuova richiesta dal client, verifica che l'azione richiesta appartenga effettivamente all'ultima pagina inviata a quel client
- Le informazioni che collegano le pagine e le azioni consentite su tali pagine possono essere inserite nel file di configurazione [web.config] dell'applicazione.
- L'esperienza dimostra che i controller delle applicazioni condividono un'ampia base comune e che è possibile costruire un controller generico, la cui specializzazione per una data applicazione viene gestita tramite un file di configurazione. Questo è l'approccio adottato, ad esempio, dallo strumento [Struts] nel campo della programmazione web Java.



