6. Exemplos
Neste capítulo, iremos ilustrar o que vimos anteriormente através de uma série de exemplos.
6.1. Exemplo 1
6.1.1. O Problema
Esta aplicação deve permitir que um utilizador calcule os seus impostos. Estamos a considerar o caso simplificado de um contribuinte que tem apenas o seu salário para declarar (valores de 2004 para rendimentos de 2003):
- Calculamos o número de escalões de imposto para o trabalhador como nbParts = nbEnfants / 2 + 1 se for solteiro, e nbEnfants / 2 + 2 se for casado, sendo nbEnfants o número de filhos.
- Se tiver pelo menos três filhos, tem direito a uma meia quota adicional
- O seu rendimento tributável R é calculado como R = 0,72 * S, em que S é o seu salário anual
- Calculamos o seu coeficiente familiar QF = R / nbParts
- Calculamos o seu imposto I. Considere a seguinte tabela:
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 |
Cada linha tem 3 campos. Para calcular o imposto I, encontre a primeira linha em que QF <= campo1. Por exemplo, se QF = 5000, a linha encontrada será
O imposto I é então igual a 0,0683*R - 291,09*nbParts. Se QF for tal que a condição QF<=campo1 nunca for satisfeita, então são utilizados os coeficientes da última linha. Aqui:
o que dá imposto I = 0,4809*R - 9505,54*nbParts.
6.1.2. A estrutura MVC da aplicação
A estrutura MVC da aplicação será a seguinte:

A função de controlador será desempenhada pela página [main.aspx]. Haverá três ações possíveis:
- init: corresponde ao primeiro pedido do cliente. O controlador exibirá a vista [formulaire.aspx]
- calcul: corresponde ao pedido para calcular o imposto. Se os dados no formulário de entrada estiverem corretos, o imposto é calculado utilizando a classe de negócio [impots]. O controlador devolve a vista [form.aspx] ao cliente tal como foi validada, juntamente com o imposto calculado. Se os dados no formulário de entrada estiverem incorretos, o controlador devolverá a vista [errors.aspx] com uma lista de erros e um link para regressar ao formulário.
- return: corresponde ao regresso ao formulário após um erro. O controlador apresenta a vista [form.aspx] tal como foi validada antes do erro.
O controlador [main.aspx] não tem conhecimento sobre cálculos de impostos. É simplesmente responsável por gerir o diálogo cliente-servidor e executar as ações solicitadas pelo cliente. Para a ação [calculate], irá basear-se na classe de negócios [tax].
6.1.3. A classe de negócios
A classe **impot** será definida da seguinte forma:
' 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
Um objeto de imposto, fornecendo ao seu construtor uma fonte de dados do tipo [impotsData]. Esta classe possui um método público [getData] que recupera as três matrizes de dados necessárias para calcular o imposto, conforme descrito anteriormente. Este método pode lidar com uma exceção caso os dados não possam ser recuperados ou sejam considerados incorretos. Assim que o objeto [tax] for criado, o seu método calculate pode ser chamado repetidamente para calcular o imposto do contribuinte com base no seu estado civil (casado ou solteiro), número de filhos e salário anual.
6.1.4. A classe de acesso aos dados
A classe [impotsData] é a classe que fornece acesso aos dados. É uma classe abstrata. Deve ser criada uma classe derivada para cada nova fonte de dados possível (matrizes, ficheiros simples, bases de dados, consola, etc.). A sua definição é a seguinte:
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
A classe tem os seguintes atributos protegidos:
matriz de limites de escalões fiscais | |
matriz de coeficientes aplicados ao rendimento tributável | |
tabela de coeficientes aplicados ao número de ações | |
Booleano que indica se os dados (limites, coeffr, coeffn) foram verificados | |
Booleano que indica se os dados (limites, coeffr, coeffn) são válidos |
A classe não tem construtor. Possui um método abstrato [getData] que as classes derivadas devem implementar. O objetivo deste método é:
- atribuir valores às três matrizes limits, coeffr, coeffn
- lançar uma exceção se os dados não puderem ser obtidos ou se se revelarem inválidos.
A classe fornece os métodos protegidos [checkData] e [check] que verificam a validade dos atributos (limites, coeffr, coeffn). Isto dispensa as classes derivadas da necessidade de os implementar. Estas terão simplesmente de os utilizar.
A primeira classe derivada que iremos utilizar é a seguinte:
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
Esta classe, denominada [impotsArray], possui dois construtores:
- um construtor sem argumentos que inicializa os atributos (limits, coeffr, coeffn) da classe base com matrizes codificadas
- um construtor que inicializa os atributos (limits, coeffr, coeffn) da classe base com matrizes que lhe são passadas como parâmetros
O método [getData], que permite que classes externas recuperem as matrizes (limits, coeffr, coeffn), verifica simplesmente a validade das três matrizes utilizando o método [checkData] da classe base. Lança uma exceção se os dados forem inválidos.
6.1.5. Testar classes de negócio e classes de acesso a dados
É importante incluir apenas classes de negócio e de acesso a dados que tenham sido verificadas como corretas numa aplicação web. Desta forma, a fase de depuração da aplicação web pode concentrar-se nas camadas de controlador e de visualização. Um programa de teste pode ter o seguinte aspeto:
' 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
A aplicação solicita ao utilizador que introduza as três informações necessárias para calcular o seu imposto:
- estado civil: o para casado, n para solteiro
- número de filhos
- salário anual
O cálculo do imposto é efetuado utilizando um objeto do tipo [tax] criado no momento do arranque da aplicação:
' 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
Como fonte de dados, utilizamos um objeto do tipo [impotsArray]. É utilizado o construtor sem argumentos desta classe, fornecendo às três matrizes (limites, coeffr, coeffn) valores codificados. A criação de um objeto [impot] pode, teoricamente, lançar uma exceção porque, para se criar, o objeto solicitará os dados (limites, coeffr, coeffn) da sua fonte de dados, que lhe foi passada como parâmetro, e esta recuperação de dados pode desencadear uma exceção. No entanto, neste caso, o método utilizado para obter os dados (valores codificados) não pode causar uma exceção. Contudo, mantivemos o tratamento de exceções para chamar a atenção do leitor para a possibilidade de o objeto [impot] poder ser construído incorretamente.
Aqui está um exemplo do programa anterior em ação:
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
Compilamos todas as classes [impot, impotsData, impotsArray] num único 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
Compilamos o programa de teste:
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
Podemos executar os testes:
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. Visualizações da aplicação web
A aplicação terá duas visualizações: [form.aspx] e [errors.aspx]. Vamos ilustrar como a aplicação funciona utilizando capturas de ecrã. A visualização [form.aspx] é apresentada quando o URL [main.aspx] é solicitado pela primeira vez:

O utilizador preenche o formulário:

e utiliza o botão [Calcular] para obter o seguinte resultado:

Podem introduzir dados incorretos:

Clicar no botão [Calcular] devolve então uma resposta diferente [errors.aspx]:

Pode utilizar o link [Voltar ao formulário] acima para regressar à vista [form.aspx] tal como estava antes do erro:

6.1.7. A visualização [form.aspx]
A página [form.aspx] terá o seguinte aspeto:
<%@ 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>
Os campos dinâmicos nesta página são os seguintes:
"checked" se a caixa de seleção [sim] estiver marcada, "" caso contrário | |
o mesmo para a caixa de seleção [não] | |
valor a inserir no campo de entrada [txtChildren] | |
valor a ser inserido no campo de entrada [txtSalary] | |
Valor a introduzir no campo de entrada [txtTax] |
A página tem dois formulários, cada um com um botão [submit]. O botão [Calculate] é o botão [submit] para o seguinte formulário:
<form method="post" action="main.aspx?action=calcul">
...
<P>
<INPUT type="submit" value="Calculer">
</P>
</form>
Podemos ver que os parâmetros do formulário serão enviados para o controlador com [action=calcul]. O botão [Clear] é o botão [submit] para o seguinte formulário:
<form method="post" action="main.aspx?action=effacer">
<INPUT type="submit" value="Effacer">
</form>
Podemos ver que os parâmetros do formulário serão enviados para o controlador com [action=effacer]. Aqui, o formulário não tem parâmetros. Apenas a ação importa.
Os campos em [formulaire.aspx] são calculados por [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
Os campos em [main.aspx] são calculados com base em duas informações colocadas pelo controlador no contexto da página:
- Context.Items("form"): um dicionário NameValueCollection contendo os valores dos campos HTML [rdmarie,txtEnfants,txtSalaire]
- Context.Items("txtImpot"): valor do imposto
6.1.8. A vista [erreurs.aspx]
A vista [erreurs.aspx] apresenta quaisquer erros que possam ocorrer durante a execução da aplicação. O seu código de apresentação é o seguinte:
<%@ 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>
A página tem três campos dinâmicos:
Código HTML para uma lista de erros | |
URL de um link | |
texto do link |
Estes campos são calculados pelo controlador da página em [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
O controlador da página recupera as informações colocadas pelo controlador da aplicação no contexto da página:
Objeto ArrayList contendo a lista de mensagens de erro a exibir | |
URL de um link | |
texto do link |
Agora que sabemos o que o utilizador da aplicação vê, podemos passar à escrita do controlador da aplicação.
6.1.9. Os controladores [global.asax, main.aspx]
Vamos rever a arquitetura MVC da nossa aplicação:

Lógica da aplicaçãoCliente
O controlador [main.aspx] deve tratar de três ações:
- init: corresponde ao primeiro pedido do cliente. O controlador apresenta a vista [formulaire.aspx]
- calcul: corresponde ao pedido de cálculo do imposto. Se os dados no formulário de entrada estiverem corretos, o imposto é calculado utilizando a classe de negócios [taxes]. O controlador devolve a vista [form.aspx] ao cliente tal como foi validada, juntamente com o imposto calculado. Se os dados no formulário de entrada estiverem incorretos, o controlador devolve a vista [errors.aspx] com uma lista de erros e um link para regressar ao formulário.
- return: corresponde ao regresso ao formulário após um erro. O controlador apresenta a vista [form.aspx] tal como foi validada antes do erro.
Sabe-se também que todos os pedidos à aplicação passam pelo controlador [global.asax], se este existir. Temos, portanto, no ponto de entrada da aplicação, uma cadeia de dois controladores:
- [global.asax], que, devido à arquitetura ASP.NET, recebe todas as solicitações para a aplicação
- [main.aspx], que, por escolha do programador, também recebe todos os pedidos para a aplicação
A necessidade de [main.aspx] decorre do facto de termos uma sessão para gerir. Vimos que [global.asax] não é adequado como controlador neste caso. Poderíamos prescindir totalmente de [global.asax] aqui. No entanto, iremos utilizá-lo para executar código quando a aplicação for iniciada. O diagrama MVC acima mostra que precisaremos de criar um objeto [tax] para calcular o imposto. Não há necessidade de criar este objeto várias vezes; uma vez é suficiente. Iremos, portanto, criá-lo no arranque da aplicação durante o evento [Application_Start] tratado pelo controlador [global.asax]. O código para isto é o seguinte:
[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
Uma vez criado, o objeto [impot] é armazenado na aplicação. É aqui que os pedidos de diferentes clientes o irão recuperar. Uma vez que a criação do objeto [impot] pode falhar, tratamos quaisquer exceções e definimos uma chave [error] na aplicação para indicar se ocorreu ou não um erro durante a criação do objeto [impot].
O código do controlador [main.aspx, main.aspx.vb] será o seguinte:
[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
O controlador começa por verificar se a aplicação foi inicializada corretamente:
' 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 o controlador detetar que a aplicação não conseguiu inicializar corretamente (o objeto [impot] necessário para o cálculo não pôde ser criado), exibe a página de erro com os parâmetros apropriados. Aqui, não há necessidade de colocar o link de retorno no formulário, uma vez que toda a aplicação está indisponível. Uma mensagem de erro geral é colocada em [Context.Items("errors")] do tipo [ArrayList].
Se o controlador determinar que a aplicação está operacional, analisa então a ação que lhe é solicitada através do parâmetro [action]. Já nos deparámos com este modo de funcionamento muitas vezes. O processamento de cada tipo de ação é delegado a uma função.
6.1.9.1. As ações init e delete
Estas duas ações devem apresentar o formulário de entrada vazio. Recorde-se que este formulário (ver vistas) tem dois parâmetros:
- Context.Items("form"): um dicionário [NameValueCollection] contendo os valores dos campos HTML [rdmarie,txtEnfants,txtSalaire]
- Context.Items("txtImpot"): valor do imposto
A função [initAppli] inicializa estes dois parâmetros para apresentar um formulário vazio.
6.1.9.2. A ação de cálculo
Esta ação deve calcular o imposto devido com base nos dados introduzidos no formulário e devolver o formulário pré-preenchido com os valores introduzidos e o montante do imposto calculado. A função [calculImpot] responsável por esta tarefa começa por verificar se os dados do formulário estão corretos:
- o campo [rdMarie] deve estar presente e ter o valor [yes] ou [no]
- o campo [txtEnfants] deve estar presente e ser um número inteiro >=0
- o campo [txtSalaire] deve estar presente e ser um número inteiro >=0
Se os dados introduzidos forem inválidos, o controlador apresenta a vista [erreurs.aspx] após definir primeiro os valores esperados para a mesma no contexto:
- As mensagens de erro são colocadas num objeto [ArrayList], que é então adicionado ao contexto [Context.Items("errors")]
- O URL do link de retorno e o texto desse link também são colocados no contexto.
Antes de passar o controlo para a página [erreurs.aspx], que enviará a resposta ao cliente, os valores introduzidos no formulário (Request.Form) são colocados na sessão, associados à chave "form". Isto permitirá que um pedido subsequente os recupere.
Poder-se-á questionar aqui se é útil verificar se os campos [rdMarie, txtEnfants, txtSalaire] estão presentes na solicitação enviada pelo cliente. Isto é desnecessário se tivermos a certeza de que o nosso cliente é um navegador que recebeu a visualização [formulaire.aspx] contendo esses campos. Nunca podemos ter certeza disso. Mostraremos um exemplo um pouco mais adiante em que o cliente é a aplicação [curl] que já conhecemos. Iremos consultar a aplicação sem enviar os campos que ela espera e ver como reage. Esta é uma regra que já foi referida várias vezes e que reiteramos aqui: uma aplicação nunca deve fazer suposições sobre o tipo de cliente que a consulta. Por segurança, deve assumir que pode ser consultada por uma aplicação programada que lhe possa enviar cadeias de parâmetros inesperadas. Deve comportar-se corretamente em todos os casos.
No nosso caso, verificámos que os campos [rdMarie, txtEnfants, txtSalaire] estavam presentes na solicitação, mas não verificámos se poderia conter outros. Nesta aplicação, estes seriam ignorados. No entanto, mais uma vez como medida de segurança, seria útil registar este tipo de pedido num ficheiro de registo e enviar um alerta ao administrador da aplicação para que este tenha conhecimento de que a aplicação está a receber pedidos «estranhos». Ao analisar estes no ficheiro de registo, poderia detetar um potencial ataque à aplicação e, em seguida, tomar as medidas necessárias para a proteger.
Se os dados esperados estiverem corretos, o controlador inicia o cálculo do imposto utilizando o objeto [tax] armazenado na aplicação. Em seguida, armazena as duas informações esperadas pela vista [form.aspx] no contexto:
- Context.Items("formulaire"): um dicionário [NameValueCollection] contendo os valores dos campos HTML [rdmarie,txtEnfants,txtSalaire], aqui [Request.Form)], ou seja, os valores previamente introduzidos no formulário
- Context.Items("txtImpot"): o valor do imposto que acaba de ser calculado
O leitor atento pode ter-se questionado ao ler o texto acima: uma vez que o objeto [impot] criado no arranque da aplicação é partilhado entre todos os pedidos, poderão existir conflitos de acesso que levem à corrupção dos dados do objeto [impot]? Para responder a esta questão, precisamos de voltar ao código da classe [impot]. As solicitações chamam o método [impot].calculateTax para obter o imposto devido. Portanto, este é o código que precisamos de examinar:
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
Suponha que um segmento esteja a executar o método anterior e seja interrompido. Outro segmento executa então o método. Quais são os riscos? Para descobrir, adicionámos o seguinte código:
Dim impot As Long = CLng(revenu * coeffR(i) - nbParts * coeffN(i))
' wait 10 seconds
Thread.Sleep(10000)
Return impot
A thread 1, após calcular o valor [impot1] da variável local [impot], é interrompida. A thread 2 é então executada e calcula um novo valor [impot2] para a mesma variável [impot] antes de ser interrompida. A thread 1 recupera o controlo. O que encontra na variável local [impot]? Uma vez que esta variável é local a um método, é armazenada numa estrutura de memória chamada pilha. Esta pilha faz parte do contexto da thread, que é guardado quando a thread é suspensa. Quando a thread 2 é iniciada, o seu contexto é configurado com uma nova pilha e, portanto, uma nova variável local [impot]. Quando a thread 2 for suspensa por sua vez, o seu contexto também será guardado. Quando a thread 1 for reiniciada, o seu contexto é restaurado, incluindo a sua pilha. Em seguida, recupera a sua variável local [impot] e não a da thread 2. Estamos, portanto, numa situação em que não há conflitos de acesso entre os pedidos. Os testes realizados com a pausa de 10 segundos descrita acima confirmaram que os pedidos simultâneos produziram, de facto, o resultado esperado.
6.1.9.3. A ação de retorno
Esta ação corresponde a clicar na ligação [Voltar ao Formulário] na vista [errors.aspx] para regressar à vista [form.aspx], que é pré-preenchida com os valores anteriormente introduzidos e guardados na sessão. A função [returnForm] recupera esta informação. Os dois parâmetros esperados pela vista [form.aspx] são inicializados:
- Context.Items("form") com os valores previamente introduzidos e guardados na sessão
- Context.Items("txtImpot") com a string vazia
6.1.10. Testar a aplicação web
Todos os ficheiros acima referidos são colocados numa pasta denominada <application-path>.

Nesta pasta, é criada uma subpasta [bin], na qual é colocado o assembly [impot.dll] — gerado a partir da compilação dos ficheiros da classe de negócios: [impots.vb, impotsData.vb, impotsArray.vb]. O comando de compilação necessário é apresentado abaixo:
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
O ficheiro [impot.dll] acima deve ser colocado em <caminho-da-aplicação>\bin para que a aplicação web possa aceder ao mesmo. O servidor Cassini é iniciado com os parâmetros (<caminho-da-aplicação>,/impots1). Utilizando um navegador, solicitamos o URL [http://localhost/impots1/main.aspx]:

Preenchemos o formulário:

Em seguida, iniciamos o cálculo do imposto utilizando o botão [Calcular]. Obtemos a seguinte resposta:

Em seguida, introduzimos dados incorretos:

Clicar no botão [Calcular] produz a seguinte resposta:

Clicar na ligação [Voltar ao formulário] leva-nos de volta ao formulário tal como estava quando foi enviado:

Por fim, clicar no botão [Limpar] reinicia a página:

6.1.11. Utilizando o cliente [curl]
É importante testar aplicações web com clientes que não sejam navegadores. Se enviar a um navegador um formulário com parâmetros a serem enviados quando este for submetido, o navegador reenviará os valores desses parâmetros para o servidor. Outro cliente poderá não fazer isso, e nesse caso o servidor receberia um pedido com parâmetros em falta. O servidor deve saber o que fazer nessa situação. Outro exemplo é a validação de dados do lado do cliente. Se o formulário contiver dados a serem validados, essa validação pode ser realizada do lado do cliente utilizando scripts incluídos no documento que contém o formulário. O navegador só enviará o formulário se todos os dados validados do lado do cliente forem válidos. Pode-se então sentir a tentação, no lado do servidor, de assumir que receberemos dados validados e não querer realizar esta validação uma segunda vez. Isso seria um erro. De facto, um cliente que não seja um navegador poderia enviar dados inválidos para o servidor, e a aplicação web poderia então comportar-se de forma inesperada. Iremos ilustrar estes pontos utilizando o cliente [curl].
Primeiro, solicitamos a 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>
O servidor enviou-nos o código HTML do formulário. Nos cabeçalhos HTTP, temos o cookie de sessão. Iremos utilizá-lo em pedidos subsequentes para manter a sessão. Vamos solicitar a ação [calcul] sem fornecer quaisquer parâmetros:
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>
Podemos ver que a aplicação web devolveu a vista [errors] com três mensagens de erro para os três parâmetros em falta. Agora vamos enviar alguns parâmetros incorretos:
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>
Os três erros foram detetados corretamente. Agora vamos enviar parâmetros válidos:
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>
Recuperámos com sucesso o imposto devido: 4.300 euros. A lição a retirar deste exemplo é que não devemos deixar-nos enganar pelo facto de estarmos a escrever uma aplicação web destinada a clientes que são navegadores. Uma aplicação web é um serviço TCP/IP, e este protocolo de rede não revela a natureza da aplicação cliente de um serviço. Por conseguinte, não podemos saber se o cliente de uma aplicação web é um navegador ou não. Seguimos, portanto, duas regras:
- Ao receber um pedido de um cliente, não fazemos suposições sobre o cliente e verificamos se os parâmetros esperados no pedido estão presentes e são válidos
- construímos uma resposta destinada a navegadores, que geralmente consiste em documentos HTML
Uma aplicação web pode ser construída para servir diferentes clientes simultaneamente, tais como navegadores e telemóveis. Podemos então incluir um novo parâmetro em cada pedido indicando o tipo de cliente. Assim, um navegador solicitará um cálculo de impostos através de uma solicitação para a URL http://machine/impots/main.aspx?client=navigateur&action=calcul, enquanto um telemóvel enviará uma solicitação para a URL http://machine/impots/main.aspx?client=mobile&action=calcul. A estrutura MVC facilita o desenvolvimento de tal aplicação. Assume a seguinte forma:

O bloco [Classes de Negócio, Classes de Acesso a Dados] permanece inalterado. Isto porque é um componente independente do cliente. O bloco [Controlador] muda apenas ligeiramente, mas deve ter em conta um novo parâmetro na solicitação: o parâmetro [client], que indica o tipo de cliente que está a ser tratado. O bloco [Visualizações] deve gerar visualizações para cada tipo de cliente. Pode valer a pena ter em conta a presença do parâmetro [client] na consulta logo na fase de conceção da aplicação, mesmo que o objetivo a curto ou médio prazo se limite aos navegadores. Se a aplicação precisar posteriormente de suportar um novo tipo de cliente, apenas será necessário escrever visualizações adaptadas a esse cliente.
6.2. Exemplo 2
6.2.1. O Problema
Aqui, propomos abordar o mesmo problema de antes, mas modificando a fonte de dados para o objeto [tax] criado pela aplicação web. Na versão anterior, a fonte de dados fornecia valores a partir de matrizes codificadas no código. Desta vez, a nova fonte de dados irá recuperá-los de uma fonte de dados ODBC associada a uma base de dados MySQL.
6.2.2. A fonte de dados ODBC
Os dados estarão localizados numa tabela chamada [IMPOTS] numa base de dados MySQL chamada [dbimpots]. O conteúdo desta tabela será o seguinte:

O proprietário da base de dados é o utilizador [admimpots] com a palavra-passe [mdpimpots]. Associamos uma fonte de dados ODBC a esta base de dados. Antes de o fazer, vamos primeiro rever as diferentes formas de aceder a uma base de dados utilizando a plataforma .NET.
Existem muitas bases de dados disponíveis para plataformas Windows. Para aceder às mesmas, as aplicações utilizam programas chamados controladores.

No diagrama acima, o controlador tem duas interfaces:
- a interface I1 apresentada à aplicação
- a interface I2 para a base de dados
Para evitar que uma aplicação escrita para a base de dados B1 tenha de ser reescrita caso se migre para uma base de dados diferente, a B2, foram envidados esforços de normalização na interface I1. Se forem utilizadas bases de dados que utilizem controladores «normalizados», a base de dados B1 será fornecida com o controlador P1, a base de dados B2 com o controlador P2, e a interface I1 destes dois controladores será idêntica. Assim, a aplicação não precisará de ser reescrita. Por exemplo, pode-se migrar uma base de dados Access para uma base de dados MySQL sem alterar a aplicação.
Existem dois tipos de controladores padronizados:
- Drivers ODBC (Open DataBase Connectivity)
- Drivers OLE DB (Object Linking and Embedding DataBase)
Os controladores ODBC fornecem acesso a bases de dados. As fontes de dados para os controladores OLE DB são mais variadas: bases de dados, sistemas de e-mail, diretórios, etc. Não há limites. Qualquer fonte de dados pode ser objeto de um controlador OLE DB, se um fornecedor assim o decidir. A vantagem é obviamente significativa: tem acesso uniforme a uma ampla variedade de dados.
A plataforma .NET 1.1 inclui três tipos de classes de acesso a dados:
- Classes SQL Server.NET, para aceder a bases de dados Microsoft SQL Server
- as classes Ole Db.NET, para aceder a bases de dados de SGBDs que oferecem um controlador OLE DB
- Classes ODBC.NET, para aceder a bases de dados de SGBDs que oferecem um controlador ODBC
O SGBD MySQL dispõe há muito de um controlador ODBC. É este que iremos utilizar agora. No Windows, selecionamos [Menu Iniciar/Painel de Controlo/Ferramentas Administrativas/Fontes ODBC de 32 bits]. Dependendo da versão do Windows, este caminho pode variar ligeiramente. Isto abre a seguinte aplicação, que nos permitirá criar a nossa fonte ODBC:

Iremos criar uma fonte de dados do sistema, ou seja, uma fonte de dados que qualquer utilizador da máquina possa utilizar. Por isso, acima, selecionamos o separador [Fonte de dados do sistema]. A página apresentada tem um botão [Adicionar], que utilizamos para criar uma nova fonte de dados ODBC:

O assistente solicita que selecione o controlador ODBC a utilizar. O Windows vem com vários controladores ODBC pré-instalados. O controlador ODBC do MySQL não está incluído neste conjunto. Por isso, deve instalá-lo primeiro. Pode encontrá-lo online digitando os termos de pesquisa «MySQL ODBC» ou «MyODBC» num motor de busca. Aqui, instalámos o controlador [MySQL ODBC 3.51]. Selecionamo-lo e clicamos em [Concluir]:

Terá de fornecer as seguintes informações:
o nome que identificará a fonte de dados ODBC. Qualquer aplicação do Windows poderá aceder à fonte utilizando este nome | |
Qualquer texto que descreva a fonte de dados | |
O nome da máquina que hospeda o SGBD MySQL. Aqui, trata-se da máquina local. Pode ser uma máquina remota. Isto permitiria a uma aplicação Windows aceder a uma base de dados remota sem qualquer codificação especial. Esta é uma grande vantagem da fonte ODBC. | |
Um SGBD MySQL pode gerir várias bases de dados. Aqui, especificamos qual queremos gerir: dbimpots | |
O nome de um utilizador registado no sistema de gestão de bases de dados MySQL. O acesso à fonte de dados será efetuado com o nome deste utilizador. Aqui: admimpots | |
A palavra-passe deste utilizador. Aqui: mdpimpots | |
Porta de funcionamento do SGBD MySQL. Por predefinição, esta é a porta 3306. Não a alterámos |
Depois de fazer isto, testamos a validade das nossas definições de ligação utilizando o botão [Testar fonte de dados]:

Depois de concluir este passo, já temos a certeza de que a nossa fonte de dados ODBC está pronta. Podemos agora utilizá-la. Clicamos em [OK] tantas vezes quantas forem necessárias para sair do assistente ODBC.
Se o leitor não tiver o SGBD MySQL, pode descarregá-lo gratuitamente em [http://www.mysql.com]. Abaixo, descrevemos os passos para criar uma fonte ODBC com o Access. Os primeiros passos são idênticos aos descritos anteriormente. Adicionamos uma nova fonte de dados do sistema:

O controlador selecionado será [Microsoft Access Driver]. Clique em [Concluir] para avançar para a definição da fonte ODBC:

As informações a fornecer são as seguintes:
O nome que identificará a fonte de dados ODBC. Qualquer aplicação Windows poderá aceder à fonte utilizando este nome | |
Qualquer texto que descreva a fonte de dados | |
O nome completo do ficheiro do Access a ser utilizado |
6.2.3. Uma nova classe de acesso a dados
Vamos rever a estrutura MVC da nossa aplicação:

No diagrama acima, a classe [impotsData] é responsável por recuperar dados. Neste caso, irá recuperar dados da base de dados MySQL [dbimpots]. Tal como aprendemos na versão anterior desta aplicação, [impotsData] é uma classe abstrata que deve ser derivada sempre que quisermos adaptá-la a uma nova fonte de dados. Vamos rever a estrutura desta classe abstrata:
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
A classe que deriva de [impotsData] deve implementar dois métodos:
- um construtor, caso o construtor padrão de [impotsData] não seja adequado
- o método [getData], que devolve os três vetores (limites, coeffr, coeffn)
Criamos a classe [impotsODBC], que irá recuperar os dados (limites, coeffr, coeffn) de uma fonte ODBC cujo nome iremos fornecer:
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
Vamos dar uma olhada no construtor:
' manufacturer
Public Sub New(ByVal DSNimpots As String)
' we note the three pieces of information
Me.DSNimpots = DSNimpots
End Sub
Recebe como parâmetro o nome da fonte de dados ODBC que contém os dados a recuperar. O construtor limita-se a armazenar este nome. O método [getData] é responsável por ler os dados da tabela [impots] e colocá-los em três matrizes (limites, coeffr, coeffn). Vamos comentar o código:
- os parâmetros para a ligação à fonte de dados ODBC estão definidos, mas a ligação não está aberta
' 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)
- Defina três objetos [ArrayList] para recuperar dados da tabela [impots]:
' tables to retrieve data
Dim aLimites As New ArrayList
Dim aCoeffR As New ArrayList
Dim aCoeffN As New ArrayList
- Todo o código de acesso à base de dados está incluído num bloco try/catch para lidar com quaisquer erros de acesso. Abrimos a ligação à base de dados:
' on tente d'accéder à la base de données
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
- Executamos o comando [select] na ligação aberta. Obtemos um objeto [OdbcDataReader] que nos permitirá percorrer as linhas da tabela resultante do 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()
- Percorremos a tabela de resultados, linha a linha. Para isso, utilizamos o método [Read] do objeto [OdbcDataReader] obtido anteriormente. Este método faz duas coisas:
- Avança uma linha na tabela. Inicialmente, o cursor está posicionado antes da primeira linha
- retorna o valor booleano [true] se conseguiu avançar, [false] caso contrário, indicando que todas as linhas foram processadas.
As colunas da linha atual do objeto [OdbcDataReader] são obtidas através de OdbcDataReader. Isto devolve um objeto que representa o valor da coluna. Percorremos toda a tabela para preencher o seu conteúdo nos três objetos [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"))
- Depois de feito isto, libertamos os recursos associados à ligação:
- O conteúdo dos três objetos [ArrayList] é transferido para três matrizes padrão:
' 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
- Depois de os dados da tabela [impots] terem sido carregados nas três matrizes, basta verificar o seu conteúdo utilizando o método [checkData] da 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. Testes para a classe de acesso aos dados
Um programa de teste poderia ter o seguinte aspeto:
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
A aplicação é iniciada com um parâmetro:
- DSNimpots: nome da fonte de dados ODBC a ser utilizada
O cálculo do imposto é realizado utilizando um objeto do tipo [tax] criado quando a aplicação é iniciada:
' 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
Uma vez inicializada, a aplicação solicita repetidamente ao utilizador que introduza as três informações necessárias para calcular o seu imposto:
- estado civil: o para casado, n para solteiro
- o número de filhos
- salário anual
Todas as classes são compiladas:
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
O programa de teste é então compilado:
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
O programa de teste é executado primeiro com a fonte de dados ODBC do 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)
Mudamos a fonte ODBC para uma fonte Access:
dos>testimpots odbc-access-dbimpots
Paramètres du calcul de l'impôt au format marié nbEnfants salaire ou rien pour arrêter :o 2 60000
impôt=4300 F
6.2.5. Visualizações da aplicação web
São as mesmas da aplicação anterior: formulaire.aspx e erreurs.aspx
6.2.6. Controladores da aplicação [global.asax, main.aspx]
Apenas o controlador [global.asax] precisa de ser modificado. É responsável por criar o objeto [impot] quando a aplicação é iniciada. O construtor deste objeto tem um único parâmetro: o objeto [impotsData], que é responsável por recuperar os dados. Este parâmetro muda, portanto, para cada novo tipo de fonte de dados. O controlador [global.asax.vb] passa a ser o seguinte:
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
A fonte de dados para o objeto [impot] é agora um objeto [impotODBC]. Este objeto recebe o nome DSN da fonte de dados ODBC a ser utilizada como parâmetro. Em vez de codificar este nome no código, colocamo-lo no ficheiro de configuração [web.config] da aplicação:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DSNimpots" value="odbc-mysql-dbimpots" />
</appSettings>
</configuration>
Sabemos que o valor de uma chave C na secção <appSettings> do ficheiro [web.config] é recuperado no código da aplicação utilizando [ConfigurationSettings.AppSettings(C)].
Para determinar a causa da exceção, registamos a mensagem de exceção na aplicação para que permaneça disponível para consultas. O controlo [main.aspx.vb] incluirá esta mensagem na sua lista de erros:
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. Resumo das alterações
A aplicação está pronta para ser testada. Vamos enumerar as alterações introduzidas em relação à versão anterior:
- foi criada uma nova classe de acesso a dados
- o controlador [global.asax.vb] foi modificado em dois pontos: a construção do objeto [import] e o registo da mensagem relacionada com qualquer exceção na aplicação
- O controlador [main.aspx.vb] foi modificado num ponto para exibir a mensagem de exceção anterior
- Foi adicionado um ficheiro [web.config]
O trabalho de modificação foi feito principalmente em 1, ou seja, fora da aplicação web. Isto foi possível graças à arquitetura MVC da aplicação, que separa o controlador das classes de negócio. Esse é o objetivo desta arquitetura. Foi possível demonstrar que, com um ficheiro [web.config] adequado, qualquer modificação no controlador da aplicação poderia ter sido evitada. É possível especificar no [web.config] o nome da classe de acesso a dados a ser instanciada dinamicamente — tal como — bem como os vários parâmetros necessários para esta instanciação. Com esta informação, o [global.asax] pode instanciar o objeto de acesso a dados. Alterar a fonte de dados equivale então a:
- criar a classe de acesso aos dados para essa fonte, caso ainda não exista
- modificar o ficheiro [web.config] para permitir a criação dinâmica de uma instância desta classe no [global.asax]
6.2.8. Testar a aplicação web
Todos os ficheiros acima referidos são colocados numa pasta denominada <application-path>.

Nesta pasta, é criada uma subpasta [bin], na qual é colocado o assembly [impot.dll] — gerado a partir da compilação dos ficheiros da classe de negócios: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb]. O comando de compilação necessário é apresentado abaixo:
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
O ficheiro [impot.dll] acima deve ser colocado em <caminho-da-aplicação>\bin para que a aplicação web possa aceder ao mesmo. O servidor Cassini é iniciado com os parâmetros (<caminho-da-aplicação>,/impots2). Os testes produzem os mesmos resultados que na versão anterior, uma vez que a presença da base de dados é transparente para o utilizador. Para demonstrar essa presença, no entanto, garantimos que a fonte ODBC não está disponível, parando o SGBD MySQL e solicitando a URL [http://localhost/impots2/main.aspx]. Recebemos a seguinte resposta:

6.3. Exemplo 3
6.3.1. O Problema
Aqui, propomos resolver o mesmo problema modificando novamente a fonte de dados do objeto [impot] criado pela aplicação web. Desta vez, a nova fonte de dados será uma base de dados ACCESS acedida através de um controlador OLEDB. O nosso objetivo é demonstrar outra forma de aceder a uma base de dados.
6.3.2. A fonte de dados OLEDB
Os dados estarão localizados numa tabela denominada [IMPOTS] numa base de dados ACCESS. O conteúdo desta tabela será o seguinte:

6.3.3. A Classe de Acesso aos Dados
Vamos rever a estrutura MVC da nossa aplicação:

- No diagrama acima, a classe [impotsData] é responsável por recuperar os dados. Desta vez, terá de o fazer a partir de uma fonte OLEDB.
Criamos a classe [impotsOLEDB], que irá recuperar os dados (limits, coeffr, coeffn) de uma fonte ODBC a que daremos o nome:
Imports System.Data
Imports System.Collections
Imports System
Imports System.Xml
Imports System.Data.OleDb
Namespace st.istia.univangers.fr
Public Class impotsOLEDB
Inherits impotsData
' 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
Vamos dar uma olhada no construtor:
' manufacturer
Public Sub New(ByVal chaineConnexion As String)
' we note the three pieces of information
Me.chaineConnexion = chaineConnexion
End Sub
Recebe como parâmetro a cadeia de ligação para a fonte OLEDB que contém os dados a recuperar. O construtor limita-se a armazená-la. Uma cadeia de ligação contém todos os parâmetros necessários ao controlador OLEDB para se ligar à fonte OLEDB. É geralmente bastante complexa. Para encontrar a cadeia de ligação para bases de dados ACCESS, pode utilizar a ferramenta [WebMatrix]. Inicie esta ferramenta. Ela disponibiliza uma janela para se ligar a uma fonte de dados:
![]() ![]() | ![]() ![]() |
Usando o ícone indicado pela seta acima, pode criar uma ligação a dois tipos de bases de dados da Microsoft: SQL Server e ACCESS. Vamos escolher o ACCESS:

Utilizámos o botão [...] para selecionar a base de dados ACCESS. Confirmamos o assistente. No separador [Dados], os ícones representam a ligação:

Agora, vamos criar um novo ficheiro .aspx através de [Ficheiros/Novo Ficheiro]:

Aparece uma página em branco na qual podemos projetar a nossa interface web:

Vamos arrastar a tabela [impots] da guia [Dados] para a folha acima. Obtemos o seguinte resultado:

Clique com o botão direito do rato no objeto [AccessDataSourceControl] abaixo para aceder às suas propriedades:

A cadeia de ligação OLEDB à base de dados ACCESS é fornecida pela propriedade [ConnectionString] acima:
Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\serge\devel\aspnet\poly\chap5\impots\3\impots.mdb
Podemos ver que esta cadeia é composta por uma parte fixa e uma parte variável, que é simplesmente o nome do ficheiro ACCESS. Vamos utilizar este facto para gerar a cadeia de ligação à nossa fonte de dados OLEDB.
Voltemos agora à nossa classe [impotsOLEDB]. O método [getData] é responsável por ler dados da tabela [impots] e colocá-los em três matrizes (limites, coeffr, coeffn). Vamos comentar o seu código:
- Definimos o objeto [DataAdapter], que nos permitirá transferir o resultado de uma consulta SQL SELECT para a memória. Para tal, definimos a consulta [select] a ser executada e associamo-la ao objeto [DataAdapter]. O construtor do [DataAdapter] também requer a cadeia de ligação que irá utilizar para se ligar à fonte 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)
- Executamos o comando [select] utilizando o método [Fill] do objeto [DataAdapter]. O resultado do [select] é carregado num objeto [DataTable] criado para este fim. Um objeto [DataTable] é a representação em memória de uma tabela de base de dados, ou seja, um conjunto de linhas e colunas. Tratamos uma exceção que pode ocorrer se, por exemplo, a cadeia de ligação estiver incorreta.
' 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
- Em [content], temos a tabela [taxes] devolvida pela instrução [select]. Um objeto [DataTable] é uma tabela, ou seja, um conjunto de linhas. Estas são acessíveis através da propriedade [rows] de [DataTable]:
' retrieve the contents of the impots table
Dim lignesImpots As DataRowCollection = contenu.Rows
- Cada elemento da coleção [taxRows] é um objeto [DataRow] que representa uma linha na tabela. Esta linha tem colunas acessíveis através do objeto [DataRow] por meio da sua propriedade [Item]. [DataRow].[Item(i)] é a coluna número i do [DataRow]. Ao percorrer a coleção de linhas (a coleção DataRows de taxRows) e a coleção de colunas para cada linha, podemos recuperar a tabela inteira:
' 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
- Depois de os dados da tabela [taxes] terem sido carregados nas três matrizes, basta verificar o seu conteúdo utilizando o método [checkData] da 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. Testes para a classe de acesso aos dados
Um programa de teste poderia ter o seguinte aspeto:
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
A aplicação é iniciada com um parâmetro:
- bdACCESS: nome do ficheiro ACCESS a utilizar
O cálculo do imposto é realizado utilizando um objeto do tipo [tax] criado quando a aplicação é iniciada:
' 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
A cadeia de ligação à fonte OLEDB foi construída utilizando informações obtidas com o [WebMatrix].
Uma vez inicializada, a aplicação solicita repetidamente ao utilizador que introduza as três informações necessárias para calcular o seu imposto:
- estado civil: o para casado, n para solteiro
- número de filhos
- salário anual
Todas as classes são compiladas:
dos>vbc /r:system.dll /r:system.data.dll /t:library /out:impot.dll impots.vb impotsArray.vb impotsData.vb impotsOLEDB.vb
O ficheiro [impots.mdb] é colocado na pasta da aplicação de teste e a aplicação é iniciada da seguinte forma:
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)
Pode executar a aplicação com um ficheiro ACCESS incorreto:
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. As vistas da aplicação web
São as mesmas da aplicação anterior: formulaire.aspx e erreurs.aspx
6.3.6. Controladores da aplicação [global.asax, main.aspx]
Apenas o controlador [global.asax] precisa de ser modificado. Este é responsável por criar o objeto [impot] quando a aplicação é iniciada. O construtor deste objeto tem um único parâmetro: o objeto [impotsData] responsável por recuperar os dados. Este parâmetro altera-se, portanto, uma vez que estamos a mudar de fonte de dados. O controlador [global.asax.vb] passa a ter o seguinte aspeto:
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
A fonte de dados para o objeto [impot] é agora um objeto [impotOLEDB]. Este objeto recebe como parâmetro a cadeia de ligação para a fonte de dados OLEDB a ser utilizada. Esta cadeia está armazenada no ficheiro de configuração [web.config] da aplicação:
<?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>
O controlador [main.aspx] permanece inalterado.
6.3.7. Resumo das alterações
A aplicação está pronta para ser testada. Vamos enumerar as alterações feitas em relação à versão anterior:
- foi criada uma nova classe de acesso a dados
- o controlador [global.asax.vb] foi modificado num ponto: construção do objeto [import]
- Foi adicionado um ficheiro [web.config]
6.3.8. Testar a aplicação web
Todos os ficheiros acima referidos estão colocados numa pasta denominada <application-path>.

Nesta pasta, é criada uma subpasta [bin], na qual é colocado o assembly [impot.dll] — gerado a partir da compilação dos ficheiros da classe de negócios: [impots.vb, impotsData.vb, impotsArray.vb, impotsOLEDB.vb]. O comando de compilação necessário é apresentado abaixo:
dos>vbc /r:system.dll /r:system.data.dll /t:library /out:impot.dll impots.vb impotsArray.vb impotsData.vb impotsOLEDB.vb
O ficheiro [impot.dll] gerado por este comando deve ser colocado em <caminho-da-aplicação>\bin para que a aplicação web possa aceder ao mesmo. O servidor Cassini é iniciado com os parâmetros (<caminho-da-aplicação>,/impots3). Os testes produzem os mesmos resultados que na versão anterior.
6.4. Exemplo 4
6.4.1. O Problema
Propomos agora transformar a nossa aplicação numa aplicação de simulação de cálculo de impostos. Um utilizador poderá realizar cálculos de impostos sucessivos, e estes serão apresentados numa nova vista semelhante a esta:

6.4.2. A estrutura MVC da aplicação
A estrutura MVC da aplicação fica assim:

Aparece uma nova vista [simulations.aspx], da qual acabámos de apresentar uma captura de ecrã. A classe de acesso aos dados será a classe [impotsODBC] do Exemplo 2.
6.4.3. As vistas da aplicação web
A vista [erreurs.aspx] permanece inalterada. A vista [formulaire.aspx] sofre uma ligeira alteração. Na verdade, o montante do imposto já não aparece nesta vista. Encontra-se agora na vista [simulations.aspx]. Assim, ao iniciar, a página apresentada ao utilizador é a seguinte:

Além disso, a vista [form] inclui um script JavaScript que valida os dados introduzidos antes de os enviar para o servidor, conforme mostrado no exemplo seguinte:

O código de apresentação é o seguinte:
<%@ 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>
Os campos dinâmicos na página são os mesmos das versões anteriores. O campo dinâmico para o valor do imposto foi removido. O botão [Calcular] já não é um botão [submit]. É um [button] e, quando clicado, a função JavaScript [calculate()] é executada:
<INPUT type="button" value="Calculer" onclick="calculer()">
Atribuímos ao formulário o nome [frmImpots] para que possamos referenciá-lo no script [calculer]:
<form name="frmImpots" method="post" action="main.aspx?action=calcul">
A função JavaScript [calculate] utiliza expressões regulares para validar os campos nos formulários [document.frmImpots.txtEnfants] e [document.frmImpots.txtSalaire]. Se os valores introduzidos estiverem corretos, são enviados para o servidor através de [document.frmImpots.submit()].
A página de apresentação obtém os seus campos dinâmicos a partir do seguinte controlador [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
O controlador [formulaire.aspx.vb] é idêntico às versões anteriores, exceto que já não precisa de recuperar o campo [txtImpot] do contexto, uma vez que este campo foi removido da página.
A vista [simulations.aspx] apresenta-se da seguinte forma:

e corresponde ao seguinte código de apresentação:
<%@ 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>
Este código contém três campos dinâmicos:
Código HTML para uma lista de simulações na forma de linhas de tabela HTML | |
URL de um link | |
texto do link |
São gerados pelo controlador [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
O controlador da página recupera as informações colocadas pelo controlador da aplicação no contexto da página:
Objeto ArrayList contendo a lista de simulações a apresentar. Cada elemento é uma matriz de 4 cadeias de caracteres que representam as informações da simulação (casado, filhos, salário, impostos). | |
URL de um link | |
Texto do link |
6.4.4. Os controladores [global.asax, main.aspx]
Vamos rever a arquitetura MVC da nossa aplicação:

O controlador [main.aspx] deve lidar com três ações:
- init: corresponde ao primeiro pedido do cliente. O controlador apresenta a vista [form.aspx]
- calcul: corresponde ao pedido de cálculo do imposto. Se os dados no formulário de entrada estiverem corretos, o imposto é calculado utilizando a classe de negócio [impotsODBC]. O controlador devolve a vista [simulations.aspx] ao cliente com o resultado da simulação atual, juntamente com todas as anteriores. Se os dados no formulário de entrada estiverem incorretos, o controlador devolve a vista [erreurs.aspx] com a lista de erros e um link para regressar ao formulário.
- return: corresponde ao regresso ao formulário após um erro. O controlador apresenta a vista [form.aspx] tal como estava validada antes do erro.
Nesta nova versão, apenas a ação [calcul] foi alterada. Com efeito, se os dados forem válidos, deve conduzir à vista [simulations.aspx], enquanto anteriormente conduzia à vista [form.aspx]. O controlador [main.aspx.vb] passa a ser o seguinte:
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
Incluímos acima apenas o que é necessário para compreender as alterações encontradas exclusivamente na função [calculImpots]:
- Primeiro, a função guarda o formulário [Request.Form] na sessão para que o formulário possa ser regenerado no estado em que foi validado. Isto deve ser feito em todos os casos, uma vez que, quer a operação resulte na resposta [erreurs.aspx] ou na resposta [simulations.aspx], regressamos ao formulário através do link [Voltar ao formulário]. Para restaurar o formulário corretamente, os seus valores devem ter sido guardados na sessão previamente.
- Se os dados introduzidos estiverem corretos, a função adiciona a simulação atual (casado, filhos, salário, impostos) à lista de simulações. Esta lista encontra-se na sessão associada à chave «simulations».
- A lista de simulações é armazenada na sessão para utilização futura. É também colocada no contexto atual, porque é aí que a vista [simulations.aspx] espera encontrá-la
- A vista [simulations.aspx] é apresentada assim que as outras informações necessárias forem colocadas no contexto
6.4.5. Resumo das alterações
A aplicação está pronta para ser testada. Vamos listar as alterações feitas em relação às versões anteriores:
- Foi criada uma nova vista
- O controlador [main.aspx.vb] foi modificado num ponto: o tratamento da ação [calcul]
6.4.6. Testar a aplicação web
O leitor é convidado a realizar os testes. Aqui fica um lembrete do procedimento. Todos os ficheiros da aplicação são colocados numa pasta denominada <application-path>. Dentro desta pasta, é criada uma subpasta [bin], contendo o assembly [impot.dll] gerado a partir da compilação dos ficheiros da classe de negócio: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb]. O ficheiro [impot.dll] produzido por este comando deve ser colocado em <application-path>\bin para que a aplicação web possa aceder ao mesmo. O servidor Cassini é iniciado com os parâmetros (<application-path>,/impots4).
6.5. Conclusão
Os exemplos anteriores demonstraram, num cenário concreto, mecanismos comumente utilizados no desenvolvimento web. Utilizámos consistentemente a arquitetura MVC pelo seu valor educativo. Poderíamos ter tratado estes mesmos exemplos de forma diferente e talvez mais simples sem esta arquitetura. No entanto, ela oferece vantagens significativas assim que a aplicação se torna um pouco complexa, com várias páginas.
Poderíamos continuar os nossos exemplos de várias maneiras. Aqui estão algumas:
- O utilizador pode querer guardar as suas simulações ao longo do tempo. Poderia executar simulações no dia D e recuperá-las no dia D+3, por exemplo. Uma solução possível para este problema é a utilização de cookies. Sabemos que o token de sessão entre o servidor e um cliente é transmitido através deste mecanismo. Também poderíamos utilizar este mecanismo para transmitir as simulações entre o cliente e o servidor.
- Ao mesmo tempo que o servidor envia a página contendo os resultados da simulação, envia um cookie nos seus cabeçalhos HTTP contendo uma string que representa as simulações. Uma vez que estas se encontram num objeto [ArrayList], este objeto deve ser convertido para um [String]. O servidor definiria um prazo de validade para o cookie, por exemplo, 30 dias.
- O navegador do cliente armazena os cookies recebidos num ficheiro e reenvia-os sempre que faz um pedido ao servidor que os enviou, desde que ainda sejam válidos (tempo de vida não excedido). O servidor receberá uma cadeia de caracteres [String] para as simulações, que deve converter num objeto [ArrayList].
Os cookies são geridos por [Response.Cookies] quando enviados para o cliente e por [Request.Cookies] quando recebidos no servidor.
- O mecanismo descrito acima pode tornar-se bastante exigente em termos de recursos se houver um grande número de simulações. Além disso, é comum que um utilizador limpe periodicamente os seus cookies, eliminando-os todos, mesmo que, de outra forma, permita que o seu navegador os utilize. Assim, mais cedo ou mais tarde, os cookies de simulação serão perdidos. Podemos, portanto, querer armazená-los no servidor em vez de no cliente, por exemplo, numa base de dados. Para associar simulações a um utilizador específico, a aplicação poderia começar com uma fase de autenticação que exigisse um nome de utilizador e uma palavra-passe, os quais são, por sua vez, armazenados numa base de dados ou em qualquer outro tipo de repositório de dados.
- Também podemos querer proteger o funcionamento da nossa aplicação. Atualmente, esta parte de duas premissas:
- o utilizador passa sempre pelo controlador [main.aspx]
- e, nesse caso, utiliza sempre as ações disponíveis na página que lhe foi enviada
O que acontece, por exemplo, se o utilizador solicitar diretamente o URL [http://localhost/impots4/formulaire.aspx]? Este cenário é improvável, uma vez que o utilizador desconhece este URL. No entanto, deve ser tido em conta. Pode ser tratado pelo controlador da aplicação [global.asax], que processa todos os pedidos feitos à aplicação. Pode, assim, verificar se o recurso solicitado é de facto [main.aspx].
Um cenário mais provável é que um utilizador não utilize as ações disponíveis na página que o servidor lhe enviou. Por exemplo, o que acontece se o utilizador solicitar diretamente o URL [http://localhost/impots4/main.aspx?action=retour] sem primeiro preencher o formulário? Vamos experimentar. Obtemos a seguinte resposta:

O servidor falha. Isto é normal. Para a ação [return], o controlador espera encontrar um objeto [NameValueCollection] na sessão que represente os valores do formulário que precisa de apresentar. Não os encontra. O mecanismo do controlador fornece uma solução elegante para este problema. Para cada pedido, o controlador [main.aspx] pode verificar se a ação solicitada é, de facto, uma das ações da página anteriormente enviada ao utilizador. Podemos utilizar o seguinte mecanismo:
- Antes de enviar a sua resposta ao cliente, o controlador armazena informações que identificam esta página na sessão do cliente
- quando recebe um novo pedido do cliente, verifica se a ação solicitada pertence efetivamente à última página enviada a esse cliente
- As informações que ligam as páginas e as ações permitidas nessas páginas podem ser introduzidas no ficheiro de configuração [web.config] da aplicação.
- A experiência mostra que os controladores de aplicações partilham uma ampla base comum e que é possível construir um controlador genérico, com a sua especialização para uma determinada aplicação tratada através de um ficheiro de configuração. Esta é a abordagem adotada, por exemplo, pela ferramenta [Struts] no campo da programação web Java.



