6. Exemplos
Neste capítulo, propomos ilustrar o que foi abordado 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 o seu imposto. Consideramos o caso simplificado de um contribuinte que tem apenas o seu salário para declarar (dados de 2004 relativos aos rendimentos de 2003):
- calcula-se o número de quotas do trabalhador nbParts = nbEnfants/2 + 1 se não for casado, nbEnfants/2 + 2 se for casado, em que nbEnfants corresponde ao número de filhos.
- se tiver pelo menos três filhos, tem mais meia quota
- calcula-se o seu rendimento tributável R = 0,72 * S, em que S é o seu salário anual
- calcula-se o seu coeficiente familiar QF = R / nbParts
- calcula-se o seu imposto I. Consideremos a seguinte tabela:
4262 | 0 | 0 |
8382 | 0,0683 | 291,09 |
14753 | 0,1914 | 1322,92 |
23 888 | 0,2826 | 2668,39 |
38 868 | 0,3738 | 4846,98 |
47 932 | 0,4262 | 6883,66 |
0 | 0,4809 | 9505,54 |
Cada linha tem 3 campos. Para calcular o imposto I, procura-se a primeira linha em que QF <= campo1. Por exemplo, se QF = 5000, encontrar-se-á a linha
O imposto I é, então, igual a 0,0683*R - 291,09*nbParts. Se QF for tal que a relação QF <= campo1 nunca for verificada, então são utilizados os coeficientes da última linha. Neste caso:
o que resulta no 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 do controlador será desempenhada pela página [main.aspx]. Haverá três ações possíveis:
- init: corresponde ao primeiro pedido do cliente. O controlador apresentará a vista [formulaire.aspx]
- calcul: corresponde ao pedido de cálculo do imposto. Se os dados do formulário de introdução estiverem corretos, o imposto é calculado através da classe de negócio [impots]. O controlador devolve ao cliente a vista [formulaire.aspx] tal como tinha sido validada, acrescida do imposto calculado. Se os dados do formulário de introdução estiverem incorretos, o controlador devolverá a vista [erreurs.aspx] com a lista de erros e um link para regressar ao formulário.
- retorno: corresponde ao regresso ao formulário após um erro. O controlador apresenta a vista [formulaire.aspx] tal como foi validada antes do erro.
O controlador [main.aspx] não tem qualquer conhecimento sobre o cálculo de impostos. É simplesmente responsável por gerir o diálogo cliente-servidor e por executar as ações solicitadas pelo cliente. Para a ação [calcul], irá basear-se na classe de negócio [impot].
6.1.3. A classe de negócio
A classe de impostos será definida da seguinte forma:
' espaços de nomes importados
Imports System
' classe
Namespace st.istia.univangers.fr
Public Class impot
Private limites(), coeffR(), coeffN() As Decimal
' construtor
Public Sub New(ByRef source As impotsData)
' os dados necessários para o cálculo do imposto
' provêm de uma fonte externa [source]
' são obtidos — pode haver uma exceção
Dim data() As Object = source.getData
limites = CType(data(0), Decimal())
coeffR = CType(data(1), Decimal())
coeffN = CType(data(2), Decimal())
End Sub
' cálculo do imposto
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
' cálculo do número de quotas
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
' cálculo do rendimento tributável e do quociente familiar
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' cálculo do imposto
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
Cria-se 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 permite obter as três tabelas de dados necessárias para o cálculo do imposto e que foram apresentadas anteriormente. Este método pode gerir uma exceção caso os dados não tenham podido ser obtidos ou se se revelarem incorretos. Uma vez criado o objeto [impot], é possível chamar repetidamente o seu método «calcular», que calcula o imposto do contribuinte com base no seu estado civil (casado ou solteiro), no número de filhos e no seu salário anual.
6.1.4. A classe de acesso aos dados
A classe [impotsData] é a classe que permite aceder aos dados. Trata-se de uma classe abstrata. É necessário criar uma classe derivada para cada nova fonte de dados possível (tabelas, 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
' método de acesso aos dados
Public MustOverride Function getData() As Object()
' método de verificação dos dados
Protected Function checkData() As Integer
' verifica os dados obtidos
' é necessário dispor de dados
valide = Not limites Is Nothing AndAlso Not coeffr Is Nothing AndAlso Not coeffn Is Nothing
If Not valide Then Return 1
' é necessário ter 3 tabelas com o mesmo tamanho
If valide Then valide = limites.Length = coeffr.Length AndAlso limites.Length = coeffn.Length
If Not valide Then Return 2
' as tabelas não podem estar vazias
valide = limites.Length <> 0
If Not valide Then Return 3
' cada tabela deve conter elementos >=0 e por ordem crescente
valide = check(limites, limites.Length - 1) AndAlso check(coeffr, coeffr.Length) AndAlso check(coeffn, coeffn.Length)
If Not valide Then Return 4
' está tudo correto
Return 0
End Function
' verifica a validade do conteúdo de um tabulero
Protected Function check(ByRef tableau() As Decimal, ByVal n As Integer) As Boolean
' a matriz deve ter os seus primeiros n elementos >=0 e em ordem estritamente crescente
If tableau(0) < 0 Then Return False
For i As Integer = 1 To n - 1
If tableau(i) <= tableau(i - 1) Then Return False
Next
' está correto
Return True
End Function
End Class
End Namespace
A classe possui os seguintes atributos protegidos:
tabela dos limites das faixas de imposto | |
tabela dos coeficientes aplicados ao rendimento tributável | |
tabela dos coeficientes aplicados ao número de quotas | |
valor booleano que indica se os dados (limites, coeffr, coeffn) foram verificados | |
valor booleano que indica se os dados (limites, coeffr, coeffn) são válidos |
A classe não tem um construtor. Possui um método abstrato [getData] que as classes derivadas terão de implementar. Este método tem como função:
- atribuir valores aos três vetores limites, coeffr e coeffn
- lançar uma exceção se os dados não tiverem podido 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 de os implementarem. Estas terão apenas de os utilizar.
A primeira classe derivada que iremos utilizar será a seguinte:
Imports System.Collections
Imports System
Namespace st.istia.univangers.fr
Public Class impotsArray
Inherits impotsData
' construtor sem argumentos
Public Sub New()
' inicializações das tabelas com constantes
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
' construtor com três tabelas como argumentos
Public Sub New(ByRef limites() As Decimal, ByRef coeffr() As Decimal, ByRef coeffn() As Decimal)
' armazenamos os dados
Me.limites = limites
Me.coeffr = coeffr
Me.coeffn = coeffn
checked = False
End Sub
Public Overrides Function getData() As Object()
' verifica-se, se necessário, os dados
Dim erreur As Integer
If Not checked Then erreur = checkData() : checked = True
' se não forem válidos, lança-se uma exceção
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' caso contrário, devolvem-se as três matrizes
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 (limites, coeffr, coeffn) da classe base com tabelas codificadas de forma «estática»
- um construtor que inicializa os atributos (limites, coeffr, coeffn) da classe base com matrizes que lhe são passadas como parâmetros
O método [getData], que permitirá às classes externas obter as matrizes (limites, coeffr, coeffn), limita-se a verificar 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. Testes das classes de negócio e das classes de acesso aos dados
É importante incluir numa aplicação web apenas classes de negócio e de acesso aos dados cuja correção tenha sido certificada. Desta forma, a fase de depuração da aplicação web poderá concentrar-se na parte do controlador e das vistas. Um programa de teste poderia ser o seguinte:
' opções
Option Strict On
Option Explicit On
' espaços de nomes
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
Module test
Sub Main()
' programa interativo de cálculo de impostos
' o utilizador introduz três dados através do teclado: casado nbEnfants salário
' o programa apresenta então o imposto a pagar
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"
' criação de um objeto «imposto»
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
' loop infinito
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Long
While True
' solicitam-se os parâmetros para o cálculo do imposto
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()
' há algo a fazer?
If paramètres Is Nothing OrElse paramètres = "" Then
Exit While
End If
' verificação do número de argumentos na linha introduzida
Dim erreur As Boolean = False
Dim args As String() = paramètres.Split(Nothing)
Dim nbParamètres As Integer = args.Length
If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe)
erreur = True
End If
' verificação da validade dos parâmetros
If Not erreur Then
' casado
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
' salário
Try
salaire = Integer.Parse(args(2))
If salaire < 0 Then
Throw New Exception
End If
Catch
erreur = True
End Try
End If
' se os parâmetros estiverem corretos - calcula-se o imposto
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 pede ao utilizador que introduza no teclado as três informações necessárias para calcular o seu imposto:
- o seu estado civil: o para casado, n para solteiro
- o número de filhos
- o seu salário anual
O cálculo do imposto é efetuado com a ajuda de um objeto do tipo [impot], criado logo que a aplicação é iniciada:
' criação de um objeto de imposto
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, utiliza-se um objeto do tipo [impotsArray]. É o construtor sem argumentos desta classe que é utilizado e que fornece as três tabelas (limites, coeffr, coeffn) com valores «fixos». A criação de um objeto [impot] pode, em teoria, gerar uma exceção, uma vez que, para se criar, o objeto irá solicitar os dados (limites, coeffr, coeffn) à sua fonte de dados que lhe foi passada como parâmetro, e essa aquisição de dados pode lançar uma exceção. Acontece que, neste caso, o método de obtenção dos dados (codificação estática) não pode provocar uma exceção. No entanto, mantivemos o tratamento da exceção para chamar a atenção do leitor para a possibilidade de o objeto [impot] ser construído incorretamente.
Eis um exemplo de execução do programa anterior:
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 o conjunto de classes [impot, impotsData, impotsArray] num conjunto [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 realizar 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. As vistas da aplicação web
A aplicação terá duas vistas: [formulaire.aspx] e [erreurs.aspx]. Vamos ilustrar o funcionamento da aplicação através de capturas de ecrã. A vista [formulaire.aspx] é apresentada quando o URL [main.aspx] é solicitado pela primeira vez:

O utilizador preenche o formulário:

e utiliza o botão [Calculer] para obter a seguinte resposta:

Pode cometer erros nos dados introduzidos:

Ao utilizar o botão [Calculer], obtém-se então outra resposta, [erreurs.aspx]:

Pode utilizar o link [Retour au formulaire] acima para recuperar a vista [formulaire.aspx] tal como a validou antes do erro:

6.1.7. A vista [formulaire.aspx]
A página [formulaire.aspx] será a seguinte:
<%@ 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 desta página são os seguintes:
«checked» se a caixa [oui] tiver de ser assinalada, «» caso contrário | |
o mesmo se aplica à caixa de seleção [non] | |
valor a inserir no campo de introdução de dados [txtEnfants] | |
valor a inserir no campo de introdução [txtSalaire] | |
valor a inserir no campo de entrada [txtImpot] |
A página tem dois formulários, cada um com um botão [submit]. O botão [Calculer] é o botão [submit] do formulário seguinte:
<form method="post" action="main.aspx?action=calcul">
...
<P>
<INPUT type="submit" value="Calculer">
</P>
</form>
Vê-se que os parâmetros do formulário serão enviados para o controlador com [action=calcul]. O botão [Effacer] é o botão [submit] do formulário seguinte:
<form method="post" action="main.aspx?action=effacer">
<INPUT type="submit" value="Effacer">
</form>
Vê-se que os parâmetros do formulário serão enviados para o controlador com [action=effacer]. Neste caso, o formulário não tem quaisquer parâmetros. Apenas a ação é relevante.
Os campos de [formulaire.aspx] são calculados por [formulaire.aspx.vb]:
Imports System.Collections.Specialized
Public Class formulaire
Inherits System.Web.UI.Page
' campos da página
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
' recupera-se a consulta anterior no contexto
Dim form As NameValueCollection = Context.Items("formulaire")
' prepara-se a página a apresentar
' botões de opção
rdouichecked = ""
rdnonchecked = "checked"
If form("rdMarie").ToString = "oui" Then
rdouichecked = "checked"
rdnonchecked = ""
End If
' o restante
txtEnfants = CType(form("txtEnfants"), String)
txtSalaire = CType(form("txtSalaire"), String)
txtImpot = CType(Context.Items("txtImpot"), String)
End Sub
End Class
O cálculo dos campos de [main.aspx] é efetuado a partir de duas informações inseridas pelo controlador no contexto da página:
- Context.Items("formulário"): dicionário do tipo NameValueCollection que contém 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] é a que apresenta os eventuais erros que possam ocorrer durante o funcionamento 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 de uma lista de erros | |
URL de um link | |
texto do link |
Estes campos são calculados pela parte de controlo da página em [erreurs.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Public Class erreurs
Inherits System.Web.UI.Page
' parâmetro da página
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
' recuperam-se os elementos do contexto
Dim erreurs As ArrayList = CType(context.Items("erreurs"), ArrayList)
href = context.Items("href").ToString
lien = context.Items("lien").ToString
' gera-se o código HTML da lista
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 informações colocadas pelo controlador da aplicação no contexto da página:
objeto ArrayList contendo a lista de mensagens de erro a apresentar | |
URL de um link | |
texto do link |
Agora que sabemos o que o utilizador da aplicação vê, podemos passar à criação do controlador da mesma.
6.1.9. Os controladores [global.asax, main.aspx]
Recordemos o esquema MVC da nossa aplicação:

Cliente Lógica de aplicação
O controlador [main.aspx] tem de processar 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 do formulário de introdução estiverem corretos, o imposto é calculado através da classe de negócio [impots]. O controlador devolve ao cliente a vista [formulaire.aspx] tal como tinha sido validada, acrescentando ainda o imposto calculado. Se os dados do formulário de introdução estiverem incorretos, o controlador devolve a vista [erreurs.aspx] com a lista de erros e um link para regressar ao formulário.
- retorno: corresponde ao regresso ao formulário após um erro. O controlador apresenta a vista [formulaire.aspx] tal como foi validada antes do erro.
Sabe-se, além disso, que qualquer pedido à aplicação passa pelo controlador [global.asax], caso este exista. Temos, assim, na entrada da aplicação, uma cadeia de dois controladores:
- [global.asax] que, devido à arquitetura do ASP.NET, recebe todas as solicitações dirigidas à aplicação
- [main.aspx] que, por decisão do programador, também recebe todas as solicitações dirigidas à aplicação
A necessidade do [main.aspx] decorre do facto de termos de gerir uma sessão. Vimos que o [global.asax] não era adequado como controlador neste caso. Aqui, poderíamos prescindir totalmente do [global.asax]. No entanto, vamos utilizá-lo para executar código no arranque da aplicação. O esquema MVC acima mostra que vamos precisar de criar um objeto [impot] para calcular o imposto. Não faz sentido criá-lo várias vezes; basta uma vez. Vamos, portanto, criá-lo no arranque da aplicação, durante o evento [Application_Start] gerido pelo controlador [global.asax]. O código deste é 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)
' cria-se um objeto «impot»
Dim objImpot As impot
Try
objImpot = New impot(New impotsArray)
' insere-se o objeto na aplicação
Application("objImpot") = objImpot
' sem erros
Application("erreur") = False
Catch ex As Exception
'ocorreu um erro, que é registado na aplicação
Application("erreur") = True
End Try
End Sub
End Class
Depois de criado, o objeto do tipo [impot] é inserido na aplicação. É aí que as diferentes consultas dos vários clientes irão buscá-lo. Como a criação do objeto [impot] pode falhar, tratamos a eventual exceção e colocamos uma chave [erreur] 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
' antes de mais nada, verifica-se se a aplicação conseguiu inicializar-se corretamente
If CType(Application("erreur"), Boolean) Then
' redireciona-se para a página de erros
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
' recuperamos a ação a realizar
Dim action As String
If Request.QueryString("action") Is Nothing Then
action = "init"
Else
action = Request.QueryString("action").ToString.ToLower
End If
' executa-se a ação
Select Case action
Case "init"
' inicializar a aplicação
initAppli()
Case "calcul"
' cálculo do imposto
calculImpot()
Case "retour"
' regresso ao formulário
retourFormulaire()
Case "effacer"
' inicialização da aplicação
initAppli()
Case Else
' ação desconhecida = inicialização
initAppli()
End Select
End Sub
Private Sub initAppli()
' exibe-se o formulário pré-preenchido
Context.Items("formulaire") = initForm()
Context.Items("txtImpot") = ""
Server.Transfer("formulaire.aspx", True)
End Sub
Private Function initForm() As NameValueCollection
' inicialização do formulário
Dim form As New NameValueCollection
form.Set("rdMarie", "non")
form.Set("txtEnfants", "")
form.Set("txtSalaire", "")
Return form
End Function
Private Sub calculImpot()
' verifica-se a validade dos dados introduzidos
Dim erreurs As ArrayList = checkData()
' se houver erros, estes são assinalados
If erreurs.Count <> 0 Then
' guardam-se os dados introduzidos
Session.Item("formulaire") = Request.Form
' prepara-se a página de erros
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
context.Items("erreurs") = erreurs
Server.Transfer("erreurs.aspx")
End If
' Aqui não há erros — calcula-se o imposto
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
Request.Form("rdMarie") = "oui", _
CType(Request.Form("txtEnfants"), Integer), _
CType(Request.Form("txtSalaire"), Long))
' exibe-se a página de resultados
context.Items("txtImpot") = impot.ToString + " euro(s)"
context.Items("formulaire") = Request.Form
Server.Transfer("formulaire.aspx", True)
End Sub
Private Sub retourFormulaire()
' exibe-se o formulário com os valores obtidos da sessão
Context.Items("formulaire") = Session.Item("formulaire")
Context.Items("txtImpot") = ""
Server.Transfer("formulaire.aspx", True)
End Sub
Private Function checkData() As ArrayList
' Inicialmente, sem erros
Dim erreurs As New ArrayList
Dim erreur As Boolean = False
' botão de opção «casado»
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
' número de filhos
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
' salário
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
' apresenta-se a lista de erros
Return erreurs
End Function
End Class
O controlador começa por verificar se a aplicação foi inicializada corretamente:
' antes de mais nada, verifica-se se a aplicação conseguiu inicializar-se corretamente
If CType(Application("erreur"), Boolean) Then
' redireciona para a página de erros
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-se corretamente (o objeto [impot] necessário para o cálculo não pôde ser criado), então exibe a página de erros com os parâmetros adequados. Neste caso, não é necessário colocar o link de retorno no formulário, uma vez que toda a aplicação se encontra indisponível. É inserida uma mensagem de erro geral no [Context.Items("erreurs")], do tipo [ArrayList].
Se o controlador verificar 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 várias vezes com este modo de funcionamento. O processamento de cada tipo de ação é delegado a uma função.
6.1.9.1. As ações init e effacer
Estas duas ações devem fazer com que o formulário de introdução de dados seja apresentado em branco. Recorde-se que este (ver vistas) tem dois parâmetros:
- Context.Items("formulário"): dicionário do tipo [NameValueCollection] contendo os valores dos campos HTML e [rdmarie,txtEnfants,txtSalaire]
- Context.Items("txtImpot"): valor do imposto
A função [initAppli] inicializa estes dois parâmetros de forma a apresentar um formulário em branco.
6.1.9.2. A ação de cálculo
Esta ação deve calcular o imposto a pagar com base nos dados introduzidos no formulário e devolver o mesmo pré-preenchido com os valores introduzidos e, além disso, com 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 [oui] ou [non]
- 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 exibe a vista [erreurs.aspx], tendo previamente inserido no contexto os valores esperados por esta:
- as mensagens de erro são colocadas num objeto [ArrayList], objeto esse que é posteriormente colocado no contexto [Context.Items("erreurs")]
- o URL do link de retorno e o texto desse link são igualmente colocados no contexto.
Antes de passar o controlo para a página [erreurs.aspx], que irá enviar a resposta ao cliente, os valores introduzidos no formulário (Request.Form) são colocados na sessão, associados à chave «formulário». Isto permitirá que uma solicitação posterior os recupere.
Podemos questionar-nos aqui se é útil verificar se os campos [rdMarie, txtEnfants, txtSalaire] estão presentes na solicitação enviada pelo cliente. Isso é desnecessário se tivermos a certeza de que o nosso cliente é um navegador que recebeu a vista [formulaire.aspx], que contém esses campos. No entanto, nunca podemos ter a certeza disso. Mais adiante, apresentaremos um exemplo em que o cliente é a aplicação [curl], já mencionada anteriormente. Interrogaremos a aplicação sem enviar os campos que ela espera e veremos como ela reagirá. Esta é uma regra já enunciada várias vezes e que aqui relembramos: uma aplicação nunca deve fazer suposições sobre o tipo de cliente que a consulta. Por motivos de segurança, deve considerar 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 que esta pudesse conter outros. Nesta aplicação, esses campos seriam ignorados. No entanto, sempre por uma questão de segurança, seria interessante registar este tipo de pedido num ficheiro de registos e enviar um alerta ao administrador da aplicação, para que este saiba que a aplicação está a receber pedidos «estranhos». Ao analisar essas solicitações no ficheiro de registos, o administrador poderia detetar um eventual ataque à aplicação e tomar as medidas necessárias para a sua proteção.
Se os dados esperados estiverem corretos, o controlador inicia o cálculo do imposto com o objeto [impot] armazenado na aplicação. Em seguida, armazena no contexto as duas informações esperadas pela vista [formulaire.aspx]:
- Context.Items («formulário»): dicionário do tipo [NameValueCollection] contendo os valores dos campos HTML, [rdmarie,txtEnfants,txtSalaire], aqui [Request.Form)] e c.a.d. os valores introduzidos anteriormente no formulário
- Context.Items("txtImpot"): valor do imposto que acabou de ser obtido
O leitor atento talvez se tenha questionado ao ler o que precede: uma vez que o objeto [impot] criado no arranque da aplicação é partilhado entre todas as consultas, não poderão ocorrer conflitos de acesso que provoquem a corrupção dos dados do objeto [impot]? Para responder a esta questão, temos de voltar ao código da classe [impot]. As consultas recorrem ao método [impot].calculerImpot para obter o imposto a pagar. É, portanto, este código que temos de examinar:
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
' cálculo do número de quotas
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
' cálculo do rendimento tributável e do quociente familiar
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' cálculo do imposto
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
Suponhamos que um thread esteja a executar o método anterior e seja interrompido. Outro thread passa então a executar o método. Quais são os riscos? Para o descobrir, adicionámos o seguinte código:
Dim impot As Long = CLng(revenu * coeffR(i) - nbParts * coeffN(i))
' aguarda-se 10 segundos
Thread.Sleep(10000)
Return impot
O thread 1, após ter calculado o valor [impot1] da variável local [impot], é interrompido. O thread 2 é então executado e calcula um novo valor, [impot2], para essa mesma variável [impot], antes de ser interrompido. O thread 1 recupera o controlo. O que encontra na variável local [impot]? Sendo esta variável local a um método, está armazenada numa estrutura da memória denominada pilha. Esta pilha faz parte do contexto do thread, que é guardado quando este é interrompido. Quando o thread 2 é iniciado, o seu contexto é estabelecido com uma nova pilha e, portanto, uma nova variável local [impot]. Quando a thread 2 for, por sua vez, interrompida, o seu contexto será também guardado. Quando a thread 1 é reiniciada, o seu contexto é restaurado, incluindo a sua pilha. Assim, 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 as solicitações. Os testes realizados com a pausa de 10 segundos acima confirmaram que as solicitações simultâneas obtinham, de facto, o resultado esperado.
6.1.9.3. A ação de retorno
Esta ação corresponde à ativação do link [Retour vers le formulaire] da vista [erreurs.aspx] para regressar à vista [formulaire.aspx], pré-preenchida com os valores introduzidos anteriormente e guardados na sessão. A função [retourFormulaire] recupera esta informação. Os dois parâmetros esperados pela vista [formulaire.aspx] são inicializados:
- Context.Items("formulário") com os valores introduzidos anteriormente e guardados na sessão
- Context.Items("txtImpot") com a cadeia vazia
6.1.10. Teste da aplicação web
Todos os ficheiros anteriores são colocados numa pasta <application-path>.

Nesta pasta, é criada uma subpasta [bin], na qual é colocado o conjunto [impot.dll], resultante da compilação dos ficheiros das classes de negócio: [impots.vb, impotsData.vb, impotsArray.vb]. Recorda-se abaixo o comando de compilação necessário:
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 <application-path>\bin para que a aplicação web tenha acesso ao mesmo. O servidor Cassini é iniciado com os parâmetros (<application-path>,/impots1). Num navegador, acedemos à URL [http://localhost/impots1/main.aspx]:

Preenchemos o formulário:

Em seguida, iniciamos o cálculo do imposto através do botão [Calculer]. Obtemos a seguinte resposta:

Em seguida, introduzimos dados errados:

Ao clicar no botão [Calculer], obtém-se a seguinte resposta:

Ao utilizar o link [Retour au formulaire], voltamos ao formulário no estado em que se encontrava quando foi validado:

Por fim, ao clicar no botão [Effacer], a página é reiniciada:

6.1.11. Utilização do cliente [curl]
É importante testar as aplicações web com outros clientes além dos navegadores. Se enviarmos a um navegador um formulário com parâmetros a serem enviados quando for validado, o navegador reenviará os valores desses parâmetros ao servidor. Outro cliente poderá não o fazer e, nesse caso, o servidor receberá um pedido em que faltarão parâmetros. O servidor deve saber o que fazer nessa situação. Outro exemplo é o das verificações de entrada de dados efetuadas do lado do cliente. Se o formulário contiver dados a verificar, essa verificação pode ser feita do lado do cliente através de scripts incluídos no documento que contém o formulário. O navegador só enviará o formulário se todos os dados verificados do lado do cliente forem válidos. Poderíamos então sentir-nos tentados, do lado do servidor, a considerar que vamos receber dados validados e a não querer realizar essa verificação uma segunda vez. Isso seria um erro. Com efeito, um cliente que não seja um navegador poderia enviar dados inválidos ao servidor e, nesse caso, a aplicação web corre o risco de apresentar um comportamento inesperado. Vamos ilustrar estes pontos utilizando o cliente [curl].
Em primeiro lugar, 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. Vamos utilizá-lo nas solicitações seguintes para manter a sessão. Solicitemos a ação [calcul] sem fornecer parâmetros:
dos>curl --cookie ASP.NET_SessionId=ivthkl45tjdjrzznevqsf255 --include --url http://localhost/impostos1/main.aspx?action=cálculo
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 constatar que a aplicação web devolveu a vista [erreurs] com três mensagens de erro relativas aos três parâmetros em falta. Vamos agora enviar parâmetros errados:
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>
Conseguimos, de facto, recuperar o imposto a pagar: 4300 euros. O que retemos desta ilustração é que não devemos deixar-nos iludir 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 permite determinar a natureza da aplicação cliente de um serviço. Por isso, não é possível saber se o cliente de uma aplicação web é um navegador ou não. Seguimos, então, duas regras:
- ao receber um pedido de um cliente, não fazemos qualquer suposição sobre o cliente e verificamos se os parâmetros esperados no pedido estão efetivamente presentes e são válidos
- elaboramos uma resposta destinada aos navegadores, ou seja, geralmente documentos HTML
Uma aplicação web pode ser concebida para servir simultaneamente diferentes clientes, por exemplo, navegadores e telemóveis. É então possível incluir em cada pedido um novo parâmetro que indique o tipo de cliente. Assim, um navegador solicitará o cálculo do imposto através de um pedido para o URL http://machine/impots/main.aspx?client=navegador&action=cálculo, enquanto o telemóvel enviará uma solicitação para a URL http://machine/impots/main.aspx?client=mobile&action=calcul. A estrutura MVC facilita a criação de uma aplicação deste tipo. Fica da seguinte forma:

O bloco [Classes métier, Classes d'accès aux données] não sofre alterações. Trata-se, de facto, de uma parte indiferente para o cliente. O bloco [Contrôleur] sofre poucas alterações, mas deve ter em conta um novo parâmetro na solicitação: o parâmetro [client], que indica com que tipo de cliente se está a lidar. O bloco [vues] deve gerar visualizações para cada tipo de cliente. Pode ser interessante ter em conta, desde a conceção da aplicação, a presença do parâmetro [client] na consulta, mesmo que o objetivo a curto ou médio prazo se limite apenas aos navegadores. Se, posteriormente, a aplicação tiver de gerir um novo tipo de cliente, bastará escrever apenas as visualizações adaptadas a esse tipo.
6.2. Exemplo 2
6.2.1. O problema
Propomos aqui abordar o mesmo problema que anteriormente, mas alterando a fonte de dados do objeto [impot] criado pela aplicação web. Na versão anterior, a fonte de dados utilizada fornecia os valores de tabelas «fixas» no código. Desta vez, a nova fonte de dados irá obtê-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 numa tabela denominada [IMPOTS] de uma base de dados MySQL denominada [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, recordemos primeiro as diferentes formas de aceder a uma base de dados com a plataforma .NET.
Existem inúmeras bases de dados para as plataformas Windows. Para aceder às mesmas, as aplicações utilizam programas denominados controladores (drivers).

No esquema acima, o controlador apresenta duas interfaces:
- a interface I1 apresentada à aplicação
- a interface I2 para a base de dados
Para evitar que uma aplicação escrita para uma base de dados B1 tenha de ser reescrita caso se migre para uma base de dados B2 diferente, foi realizado um esforço de normalização na interface I1. Se forem utilizadas bases de dados que utilizem controladores «normalizados», a base B1 será fornecida com um controlador P1, a base de dados B2 com um controlador P2, e a interface I1 destes dois controladores será idêntica. Assim, não será necessário reescrever a aplicação. Assim, será possível, por exemplo, migrar uma base de dados ACCESS para uma base de dados MySQL sem alterar a aplicação.
Existem dois tipos de controladores normalizados:
- os controladores ODBC (Open DataBase Connectivity)
- os controladores OLE e DB (Object Linking and Embedding DataBase)
Os controladores ODBC permitem o acesso a bases de dados. As fontes de dados para os controladores OLE e DB são mais variadas: bases de dados, sistemas de correio eletrónico, diretórios, etc. Não há limites. Qualquer fonte de dados pode ser alvo de um controlador OLE DB, caso um editor assim o decida. A vantagem é, evidentemente, grande: obtém-se um acesso uniforme a uma grande variedade de dados.
A plataforma .NET 1.1 é fornecida com três tipos de classes de acesso aos dados:
- as classes SQL e Server.NET, para aceder às bases de dados SQL Server da Microsoft
- as classes Ole Db.NET, para aceder às bases de dados SGBD que oferecem um controlador OLE DB
- as classes odbc.net, para aceder às bases de dados SGBD, que disponibilizam um controlador ODBC
O SGBD MySQL dispõe, há muito tempo, de um controlador ODBC. É este que utilizamos agora. No Windows, selecionamos a opção [Menu Démarrer/Panneau de configuration/Outils d'administration/Sources ODBC 32 bits]. Dependendo da versão do Windows, este caminho pode variar ligeiramente. Obtemos a seguinte aplicação, que nos permitirá criar a nossa fonte ODBC:

Vamos criar uma fonte de dados do sistema, c.a.d. Uma fonte de dados que qualquer utilizador do computador poderá aceder (utiliser.Aussi); acima, selecionamos o separador [Source de données système]. A página apresentada tem um botão [Ajouter] que utilizamos para criar uma nova fonte de dados ODBC:

O assistente solicita que se selecione o controlador ODBC a utilizar. O Windows inclui vários controladores ODBC pré-instalados. O controlador ODBC do MySQL não faz parte desse conjunto. Por isso, é necessário instalá-lo previamente. Pode encontrá-lo na Internet digitando a sequência de palavras-chave «MySQL ODBC» ou ainda «MyODBC» num motor de busca. Neste caso, instalámos o controlador [MySQL ODBC 3.51]. Selecionamo-lo e passamos para o [Terminer]:

É necessário fornecer alguns dados:
o nome que designará a fonte de dados ODBC. Qualquer aplicação do Windows poderá aceder à fonte através deste nome | |
um texto arbitrário que descreva a fonte de dados | |
o nome do computador que aloja o SGBD MySQL. Neste caso, trata-se da máquina local. Poderia ser uma máquina remota. Isto permitiria a uma aplicação Windows aceder a uma base de dados remota sem qualquer codificação específica. Este é um grande vantagem da fonte ODBC. | |
Um SGBD MySQL pode gerir várias bases de dados. Aqui especifica-se qual se pretende gerir: dbimpots | |
nome de um utilizador registado no SGBD MySQL. É com este nome que serão efetuados os acessos à fonte de dados. Neste caso: admimpots | |
a palavra-passe deste utilizador. Aqui: mdpimpots | |
porta de trabalho do SGBD MySQL. Por predefinição, é a porta 3306. Não a alterámos |
Feito isto, testamos a validade dos nossos parâmetros de ligação com o botão [Test Data Source]:

Feito isto, temos a certeza da nossa fonte de dados ODBC. Podemos agora utilizá-la. Repetimos o [OK] tantas vezes quantas forem necessárias para sair do assistente ODBC.
Se o leitor não dispuser do SGBD mySQL, pode obtê-lo gratuitamente na URL [http://www.mysql.com]. Apresentamos abaixo o procedimento para criar uma fonte ODBC com o Access. Os primeiros passos são idênticos aos descritos anteriormente. Adiciona-se uma nova fonte de dados do sistema:

O controlador selecionado será o [Microsoft Access Driver]. Executa-se o [Terminer] para passar à definição da fonte ODBC:

As informações a fornecer são as seguintes:
o nome que designará a fonte de dados ODBC. Qualquer aplicação Windows poderá aceder à fonte através deste nome | |
um texto arbitrário que descreva a fonte de dados | |
o nome completo do ficheiro ACCESS a ser utilizado |
6.2.3. Uma nova classe de acesso aos dados
Voltemos à estrutura MVC da nossa aplicação:

No esquema acima, a classe [impotsData] é responsável por recuperar os dados. Nesta instância, terá de o fazer a partir da base de dados MySQL [dbimpots]. Sabemos, desde a versão anterior desta aplicação, que a [impotsData] é uma classe abstrata que tem de ser derivada sempre que se pretenda adaptá-la a uma nova fonte de dados. Recorde-se 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
' método de acesso aos dados
Public MustOverride Function getData() As Object()
' método de verificação dos dados
Protected Function checkData() As Integer
' verifica os dados recolhidos
...
End Function
' verifica a validade do conteúdo de uma tabela
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 sem argumentos 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á buscar os dados (limites, coeffr, coeffn) a partir de uma fonte ODBC, à qual daremos o nome:
Imports System.Data.Odbc
Imports System.Data
Imports System.Collections
Imports System
Namespace st.istia.univangers.fr
Public Class impotsODBC
Inherits impotsData
' variáveis de instância
Protected DSNimpots As String
' construtor
Public Sub New(ByVal DSNimpots As String)
' regista-se a informação relativa às três variáveis
Me.DSNimpots = DSNimpots
End Sub
Public Overrides Function getdata() As Object()
' inicializa as três tabelas «limites», «coeffr» e «coeffn» a partir
' do conteúdo da tabela [impots] da base de dados ODBC DSNimpots
' limites, coeffr e coeffn são as três colunas desta tabela
' pode lançar várias exceções
Dim connectString As String = "DSN=" + DSNimpots + ";" ' chaîne de connexion à la base
Dim impotsConn As OdbcConnection = Nothing ' la connexion
Dim sqlCommand As OdbcCommand = Nothing ' la commande SQL
' a consulta SELECT
Dim selectCommand As String = "select limites,coeffr,coeffn from impots"
' tabelas para recuperar os dados
Dim aLimites As New ArrayList
Dim aCoeffR As New ArrayList
Dim aCoeffN As New ArrayList
Try
' tenta-se aceder à base de dados
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
' está a ser criado um objeto de comando
sqlCommand = New OdbcCommand(selectCommand, impotsConn)
' executa-se a consulta
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
' Análise da tabela recuperada
While myReader.Read()
' os dados da linha atual são colocados nas tabelas
aLimites.Add(myReader("limites"))
aCoeffR.Add(myReader("coeffr"))
aCoeffN.Add(myReader("coeffn"))
End While
' libertação dos recursos
myReader.Close()
impotsConn.Close()
Catch e As Exception
Throw New Exception("Erreur d'accès à la base de données (" + e.Message + ")")
End Try
' as tabelas dinâmicas são transferidas para tabelas estáticas
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
' verifica-se os dados obtidos
Dim erreur As Integer = checkData()
' se os dados não forem válidos, é lançada uma exceção
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' caso contrário, devolvem-se as três tabelas
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Vamos analisar o fabricante:
' construtor
Public Sub New(ByVal DSNimpots As String)
' regista-se as três informações
Me.DSNimpots = DSNimpots
End Sub
Recebe como parâmetro o nome da fonte ODBC, na qual se encontram os dados a adquirir. O construtor limita-se a memorizar esse 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 seu código:
- os parâmetros da ligação à fonte de dados ODBC estão definidos, mas esta não está aberta
' cadeia de ligação à base de dados
Dim connectString As String = "DSN=" + DSNimpots + ";"
' criamos um objeto de ligação à base de dados — esta ligação não está aberta
Dim impotsConn As OdbcConnection = New OdbcConnection(connectString)
- São definidos três objetos [ArrayList] para recuperar os dados da tabela [impots]:
' tabelas para recuperar os dados
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á envolto num try/catch para gerir um eventual erro de acesso. Estabelece-se a ligação à base de dados:
' tenta-se aceder à base de dados
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
- Executa-se o comando [select] na ligação aberta. Obtém-se um objeto [OdbcDataReader] que nos permitirá percorrer as linhas da tabela resultante da consulta SELECT:
' cria-se um objeto de comando
Dim sqlCommand As OdbcCommand = New OdbcCommand(selectCommand, impotsConn)
' executa-se a consulta
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
- Percorremos a tabela de resultados, linha a linha. Para tal, utilizamos o método [Read] do objeto [OdbcDataReader] obtido anteriormente. Este método faz duas coisas:
- avança uma linha na tabela. Inicialmente, estamos posicionados antes da primeira linha
- retorna o valor booleano [true] se tiver sido possível avançar, e [false] caso contrário, sendo que este último caso indica que todas as linhas foram processadas.
As colunas da linha atual do objeto [OdbcDataReader] são obtidas por OdbcDataReader. Obtém-se um objeto que representa o valor da coluna. Percorremos toda a tabela para colocar o seu conteúdo nos três objetos [ArrayList]:
' Análise da tabela recuperada
While myReader.Read()
' os dados da linha atual são colocados nas tabelas
aLimites.Add(myReader("limites"))
aCoeffR.Add(myReader("coeffr"))
aCoeffN.Add(myReader("coeffn"))
- Feito isto, libertamos os recursos associados à ligação:
- O conteúdo dos três objetos [ArrayList] é transferido para três tabelas clássicas:
' as tabelas dinâmicas são transferidas para tabelas estáticas
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
- Assim que os dados da tabela [impots] forem transferidos para as três tabelas, basta verificar o conteúdo destas utilizando o método [checkData] da classe base [impotsData]:
' verifica-se os dados obtidos
Dim erreur As Integer = checkData()
' se os dados não forem válidos, é lançada uma exceção
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' caso contrário, devolvem-se as três tabelas
Return New Object() {limites, coeffr, coeffn}
6.2.4. Testes da classe de acesso aos dados
Um programa de teste poderia ser o seguinte:
Option Explicit On
Option Strict On
' espaços de nomes
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' página de teste
Module testimpots
Sub Main(ByVal arguments() As String)
' programa interativo de cálculo de impostos
' o utilizador introduz três dados através do teclado: casado nbEnfants salário
' o programa apresenta então o imposto a pagar
Const syntaxe1 As String = "pg DSNimpots"
Const syntaxe2 As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"
' verificação dos parâmetros do programa
If arguments.Length <> 1 Then
' mensagem de erro
Console.Error.WriteLine(syntaxe1)
' fim
Environment.Exit(1)
End If
' recuperam-se os argumentos
Dim DSNimpots As String = arguments(0)
' criação de um objeto de imposto
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
' loop infinito
While True
' inicialmente sem erros
Dim erreur As Boolean = False
' solicitam-se os parâmetros para o cálculo do imposto
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()
' há algo a fazer?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' verificação do número de argumentos na linha introduzida
Dim args As String() = paramètres.Split(Nothing)
Dim nbParamètres As Integer = args.Length
If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe2)
erreur = True
End If
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Integer
If Not erreur Then
' verificação da validade dos parâmetros
' casado
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
' salário
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
' os parâmetros estão corretos — calcula-se o imposto
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 é efetuado com a ajuda de um objeto do tipo [impot], criado logo que a aplicação é iniciada:
' criação de um objeto de imposto
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
Depois de inicializada, a aplicação solicita repetidamente ao utilizador que introduza no teclado as três informações necessárias para calcular o seu imposto:
- o seu estado civil: o para casado, n para solteiro
- o número de filhos
- o seu 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 é, por sua vez, 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 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 para ODBC, passando a utilizar uma fonte do 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. As vistas da aplicação web
São as mesmas da aplicação anterior: formulaire.aspx e erreurs.aspx
6.2.6. Os controladores da aplicação [global.asax, main.aspx]
Apenas o controlador [global.asax] deve ser alterado. Este é, de facto, responsável por criar o objeto [impot] no arranque da aplicação. O construtor deste objeto tem como único parâmetro o objeto do tipo [impotsData], responsável por recuperar os dados. Este parâmetro altera-se, portanto, para cada novo tipo 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)
' cria-se um objeto de imposto
Dim objImpot As impot
Try
objImpot = New impot(New impotsODBC(ConfigurationSettings.AppSettings("DSNimpots")))
' o objeto é inserido na aplicação
Application("objImpot") = objImpot
' sem erros
Application("erreur") = False
Catch ex As Exception
'ocorreu um erro, que é registado na aplicação
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
A fonte de dados do objeto [impot] é agora um objeto [impotODBC]. Este último tem como parâmetro o nome DSN da fonte de dados ODBC a ser utilizada. Em vez de escrever este nome diretamente no código, coloca-se 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>
Sabe-se que o valor de uma chave C da secção <appSettings> do ficheiro [web.config] é obtido no código da aplicação através de [ConfigurationSettings.AppSettings(C)].
Para determinar a causa da exceção, a mensagem da mesma é registada na aplicação, de modo a que permaneça disponível para consultas. O controlador [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
' antes de mais nada, verifica-se se a aplicação conseguiu inicializar-se corretamente
If CType(Application("erreur"), Boolean) Then
' redireciona-se para a página de erros
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
' recuperamos a ação a realizar
...
6.2.7. Resumo das alterações
A aplicação está pronta para ser testada. Enumeremos as alterações introduzidas em relação à versão anterior:
- foi criada uma nova classe de acesso aos dados
- o controlador [global.asax.vb] foi alterado em dois pontos: criação do objeto [impot] e registo na aplicação da mensagem relacionada com uma eventual exceção
- o controlador [main.aspx.vb] foi alterado num ponto para apresentar a mensagem de exceção anterior
- foi adicionado um ficheiro [web.config]
O trabalho de modificação foi realizado essencialmente no 1, c.a.d, 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. É aí que reside todo o interesse desta arquitetura. Seria possível demonstrar que, com um ficheiro de configuração [web.config] adequado, se poderia ter evitado qualquer alteração no controlador da aplicação. É possível definir no [web.config] o nome da classe de acesso aos dados a instanciar dinamicamente, bem como os vários parâmetros necessários para essa instanciação. Com estas informações, o [global.asax] pode instanciar o objeto de acesso aos dados. Mudar de fonte de dados equivale, então, a:
- criar a classe de acesso a essa fonte, caso ainda não exista
- modificar o ficheiro [web.config] para permitir a criação dinâmica de uma instância dessa classe em [global.asax]
6.2.8. Teste da aplicação web
Todos os ficheiros anteriores são colocados numa pasta <application-path>.

Nesta pasta, é criada uma subpasta [bin], na qual é colocado o assembly [impot.dll] resultante da compilação dos ficheiros das classes de negócio: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb]. Recorda-se abaixo o comando de compilação necessário:
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 referido deve ser colocado em <application-path>\bin para que a aplicação web tenha acesso ao mesmo. O servidor Cassini é iniciado com os parâmetros (<application-path>,/impots2). Os testes apresentam os mesmos resultados que na versão anterior, sendo a presença da base de dados transparente para o utilizador. No entanto, para ilustrar essa presença, fazemos com que a fonte ODBC não esteja disponível, parando os ficheiros SGBD e MySQL, e acedemos à URL [http://localhost/impots2/main.aspx]. Obtemos a seguinte resposta:

6.3. Exemplo 3
6.3.1. O problema
Propomos aqui abordar o mesmo problema, alterando 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, à qual acederemos através de um controlador OLEDB. O nosso objetivo é mostrar outra forma de aceder a uma base de dados.
6.3.2. A fonte de dados OLEDB
Os dados estarão numa tabela chamada [IMPOTS], pertencente a uma base de dados ACCESS. O conteúdo desta tabela será o seguinte:

6.3.3. A classe de acesso aos dados
Voltemos à estrutura MVC da nossa aplicação:

- No esquema 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á buscar os dados (limites, coeffr, coeffn) numa fonte ODBC, à qual 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
' variáveis de instância
Protected chaineConnexion As String
' construtor
Public Sub New(ByVal chaineConnexion As String)
' regista-se a informação
Me.chaineConnexion = chaineConnexion
End Sub
Public Overrides Function getData() As Object()
' inicializa os três tabuletos «limites», «coeffr» e «coeffn» a partir
' do conteúdo da tabela [impots] da base de dados OLEDB [chaineConnexion]
' «limites», «coeffr» e «coeffn» são as três colunas desta tabela
' pode lançar várias exceções
' cria-se um objeto DataAdapter para ler os dados da fonte OLEDB
Dim adaptateur As New OleDbDataAdapter("select limites,coeffr,coeffn from impots", chaineConnexion)
' cria-se uma imagem na memória do resultado da consulta
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
' recupera-se o conteúdo da tabela «impots»
Dim lignesImpots As DataRowCollection = contenu.Rows
' dimensiona-se as tabelas de receção
Me.limites = New Decimal(lignesImpots.Count - 1) {}
Me.coeffr = New Decimal(lignesImpots.Count - 1) {}
Me.coeffn = New Decimal(lignesImpots.Count - 1) {}
' transfere-se o conteúdo da tabela «impots» para as matrizes
Dim i As Integer
Dim ligne As DataRow
Try
For i = 0 To lignesImpots.Count - 1
' linha i da tabela
ligne = lignesImpots.Item(i)
' recupera-se o conteúdo da linha
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
' verifica-se os dados obtidos
Dim erreur As Integer = checkData()
' se os dados não forem válidos, lança-se uma exceção
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' caso contrário, devolvem-se os três tabuletos
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Vamos analisar o fabricante:
' construtor
Public Sub New(ByVal chaineConnexion As String)
' regista-se as três informações
Me.chaineConnexion = chaineConnexion
End Sub
Recebe como parâmetro a cadeia de ligação da fonte OLEDB, na qual se encontram os dados a adquirir. O controlador limita-se a memorizá-la. Uma cadeia de ligação contém todos os parâmetros necessários para que o controlador OLEDB se ligue à fonte OLEDB. É, geralmente, bastante complexa. Para descobrir a cadeia de ligação das bases de dados ACCESS, pode-se recorrer à ferramenta [WebMatrix]. Inicie esta ferramenta. Ela apresenta uma janela que permite ligar-se a uma fonte de dados:
![]() ![]() | ![]() ![]() |
Através do ícone indicado pela seta acima, é possível estabelecer uma ligação a dois tipos de bases de dados da Microsoft: SQL Server e ACCESS. Vamos selecionar ACCESS:

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

Agora, vamos criar um novo ficheiro .aspx através do [Files/New File]:

Obtemos uma folha em branco na qual podemos desenhar a nossa interface web:

Arraste a tabela [impots] do separador [Data] para a folha acima. Obtemos o seguinte resultado:

Clicamos com o botão direito do rato no objeto [AccessDataSourceControl] abaixo para aceder às seguintes 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
Vemos que esta cadeia é composta por uma parte fixa e uma parte variável, que é simplesmente o nome do ficheiro ACCESS. Utilizaremos 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 os 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 para a memória o resultado de uma consulta SELECT da tabela SQL. Para tal, definimos a consulta [select] a executar e associamo-la ao objeto [DataAdapter]. O construtor deste último requer também a cadeia de ligação que deverá utilizar para se ligar à fonte OLEDB
' cria-se um objeto DataAdapter para ler os dados da fonte OLEDB
Dim adaptateur As New OleDbDataAdapter("select limites,coeffr,coeffn from impots", chaineConnexion)
- executa-se o comando [select] através do método [Fill] do objeto [DataAdapter]. O resultado do [select] é inserido num objeto [DataTable] criado para o efeito. Um objeto [DataTable] é a representação em memória de uma tabela de base de dados, c.a.d, um conjunto de linhas e colunas. Tratamos uma exceção que pode ocorrer se, por exemplo, a cadeia de ligação estiver incorreta.
' cria-se uma imagem na memória do resultado da consulta
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 [contenu], temos a tabela [impots], que é recuperada pelo [select]. Um objeto [DataTable] é uma tabela, ou seja, um conjunto de linhas. Estas são acessíveis através da propriedade [rows] de [datatable]:
' recupera-se o conteúdo da tabela «impots»
Dim lignesImpots As DataRowCollection = contenu.Rows
- cada elemento da coleção [lignesImpots] é um objeto do tipo [DataRow] que representa uma linha da tabela. Esta tabela possui colunas acessíveis através do objeto [DataRow], por meio da sua propriedade [Item]. [DataRow].[Item(i)] é a coluna n.º i da linha [DataRow]. Ao percorrer a coleção de linhas (a coleção DataRows de lignesImpots) e as coleções de colunas de cada linha, é possível obter a tabela na sua totalidade:
' dimensiona-se as tabelas de receção
Me.limites = New Decimal(lignesImpots.Count - 1) {}
Me.coeffr = New Decimal(lignesImpots.Count - 1) {}
Me.coeffn = New Decimal(lignesImpots.Count - 1) {}
' transfere-se o conteúdo da tabela «impots» para as matrizes
Dim i As Integer
Dim ligne As DataRow
Try
For i = 0 To lignesImpots.Count - 1
' linha i da tabela
ligne = lignesImpots.Item(i)
' recupera-se o conteúdo da linha
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
- Assim que os dados da tabela [impots] forem transferidos para as três tabelas, basta verificar o conteúdo destas utilizando o método [checkData] da classe base [impotsData]:
' verifica-se os dados obtidos
Dim erreur As Integer = checkData()
' se os dados não forem válidos, lança-se uma exceção
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' caso contrário, devolvem-se os três tabuletos
Return New Object() {limites, coeffr, coeffn}
6.3.4. Testes da classe de acesso aos dados
Um programa de teste poderia ser o seguinte:
Option Explicit On
Option Strict On
' espaços de nomes
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' página de teste
Module testimpots
Sub Main(ByVal arguments() As String)
' programa interativo de cálculo de impostos
' o utilizador introduz três dados através do teclado: casado nbEnfants salário
' o programa apresenta então o imposto a pagar
Const syntaxe1 As String = "pg bdACCESS"
Const syntaxe2 As String = "syntaxe : marié nbEnfants salaire" + ControlChars.Lf + "marié : o pour marié, n pour non marié" + ControlChars.Lf + "nbEnfants : nombre d'enfants" + ControlChars.Lf + "salaire : salaire annuel en F"
' verificação dos parâmetros do programa
If arguments.Length <> 1 Then
' mensagem de erro
Console.Error.WriteLine(syntaxe1)
' fim
Environment.Exit(1)
End If
' recuperam-se os argumentos
Dim chemin As String = arguments(0)
' prepara-se a cadeia de ligação
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + chemin
' criação de um objeto de imposto
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
' loop infinito
While True
' inicialmente, sem erros
Dim erreur As Boolean = False
' solicitam-se os parâmetros para o cálculo do imposto
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()
' Há algo a fazer?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' verificação do número de argumentos na linha introduzida
Dim args As String() = paramètres.Split(Nothing)
Dim nbParamètres As Integer = args.Length
If nbParamètres <> 3 Then
Console.Error.WriteLine(syntaxe2)
erreur = True
End If
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Integer
If Not erreur Then
' verificação da validade dos parâmetros
' casado
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
' salário
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
' os parâmetros estão corretos — calcula-se o imposto
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 é executada com um parâmetro:
- bdACCESS: nome do ficheiro ACCESS a ser processado
O cálculo do imposto é efetuado com a ajuda de um objeto do tipo [impot], criado logo que a aplicação é iniciada:
' recuperam-se os argumentos
Dim chemin As String = arguments(0)
' prepara-se a cadeia de ligação
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + chemin
' criação de um objeto de imposto
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 criada a partir das informações obtidas com [WebMatrix].
Depois de inicializada, a aplicação solicita repetidamente ao utilizador que introduza no teclado as três informações necessárias para calcular o seu imposto:
- o seu estado civil: o para casado, n para solteiro
- o número de filhos
- o seu 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 esta é executada 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)
É possível iniciar 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. Os controladores da aplicação [global.asax, main.aspx]
Apenas o controlador [global.asax] deve ser alterado. Este é, de facto, responsável por criar o objeto [impot] no arranque da aplicação. O construtor deste objeto tem como único parâmetro o objeto do tipo [impotsData], responsável por recuperar os dados. Este parâmetro altera-se, portanto, uma vez que se muda de fonte de dados. O controlador [global.asax.vb] passa a ter o seguinte código:
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)
' criação de um objeto de imposto
Dim objImpot As impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
' insere-se o objeto na aplicação
Application("objImpot") = objImpot
' sem erros
Application("erreur") = False
Catch ex As Exception
'ocorreu um erro, que é registado na aplicação
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
A fonte de dados do objeto [impot] é agora um objeto [impotOLEDB]. Este último tem como parâmetro a cadeia de ligação da fonte de dados OLEDB a ser utilizada. Esta encontra-se 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] não sofre alterações.
6.3.7. Resumo das alterações
A aplicação está pronta para ser testada. Enumeremos as alterações introduzidas em relação à versão anterior:
- foi criada uma nova classe de acesso aos dados
- o controlador [global.asax.vb] foi alterado num ponto: criação do objeto [impot]
- foi adicionado um ficheiro [web.config]
6.3.8. Teste da aplicação web
Todos os ficheiros acima referidos são colocados numa pasta <application-path>.

Nesta pasta, é criada uma subpasta [bin], na qual é colocado o conjunto [impot.dll] resultante da compilação dos ficheiros das classes de negócio: [impots.vb, impotsData.vb, impotsArray.vb, impotsOLEDB.vb]. Recorda-se abaixo o comando de compilação necessário:
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] produzido por este comando deve ser colocado em <application-path>\bin para que a aplicação web tenha acesso ao mesmo. O servidor Cassini é iniciado com os parâmetros (<application-path>,/impots3). Os testes apresentam 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álculos de impostos. Um utilizador poderá efetuar cálculos sucessivos de impostos, os quais lhe serão apresentados numa nova vista semelhante a esta:

6.4.2. A estrutura MVC da aplicação
A estrutura MVC da aplicação passa a ser a seguinte:

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] não sofre alterações. A vista [formulaire.aspx] sofre uma ligeira alteração. Com efeito, o montante do imposto já não aparece nesta vista. Encontra-se agora na vista [simulations.aspx]. Assim, no arranque, a página apresentada ao utilizador é a seguinte:

Além disso, a vista [formulaire] inclui um script JavaScript que verifica a validade dos dados introduzidos antes de os enviar para o servidor, como mostra o 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(){
// verificação dos parâmetros antes de os enviar para o servidor
with(document.frmImpots){
//número de filhos
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
// o modelo não foi verificado
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
txtEnfants.focus();
return;
}//if
//salário
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
// o modelo não foi verificado
alert("Le salaire n'a pas été donné ou est incorrect");
txtSalaire.focus();
return;
}//se
// Está tudo bem — enviamos o formulário para o servidor
submit();
}//com
}//calcular
</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 da página são os das versões anteriores. O campo dinâmico relativo ao montante do imposto desapareceu. O botão [Calculer] já não é um botão do tipo [submit]. É do tipo [button] e, quando clicado, a função JavaScript [calculer()] é executada:
<INPUT type="button" value="Calculer" onclick="calculer()">
Atribuímos ao formulário o nome [frmImpots] para podermos referenciá-lo no script [calculer]:
<form name="frmImpots" method="post" action="main.aspx?action=calcul">
A função JavaScript [calculer] utiliza expressões regulares para verificar a validade dos campos dos formulários [document.frmImpots.txtEnfants] e [document.frmImpots.txtSalaire]. Se os valores introduzidos estiverem corretos, são enviados para o servidor pela função [document.frmImpots.submit()].
A página de apresentação obtém os seus campos dinâmicos a partir do seu controlador [formulaire.aspx.vb], da seguinte forma:
Imports System.Collections.Specialized
Public Class formulaire
Inherits System.Web.UI.Page
' campos da página
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
' recuperamos a solicitação anterior no contexto
Dim form As NameValueCollection = Context.Items("formulaire")
' preparamos a página a apresentar
' botões de opção
rdouichecked = ""
rdnonchecked = "checked"
If form("rdMarie").ToString = "oui" Then
rdouichecked = "checked"
rdnonchecked = ""
End If
' o resto
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, com a única diferença de que já não precisa de recuperar o campo [txtImpot] do contexto, uma vez que esse campo desapareceu da página.
A vista [simulations.aspx] apresenta-se visualmente 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 apresenta três campos dinâmicos:
código HTML de uma lista de simulações na forma de linhas de tabela HTML | |
URL de um link | |
texto do link |
São gerados pela parte controladora [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
'recuperam-se as simulações no contexto
Dim simulations As ArrayList = CType(context.Items("simulations"), ArrayList)
' cada simulação é um array de 4 elementos do tipo string
Dim simulation() As String
Dim i, j As Integer
For i = 0 To simulations.Count - 1
simulation = CType(simulations(i), String())
simulationsHTML += "<tr>"
For j = 0 To simulation.Length - 1
simulationsHTML += "<td>" + simulation(j) + "</td>"
Next
simulationsHTML += "</tr>" + ControlChars.CrLf
Next
' recuperam-se os restantes elementos do contexto
href = context.Items("href").ToString
lien = context.Items("lien").ToString
End Sub
End Class
O controlador da página recupera 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 é um tabuleiro com 4 cadeias de caracteres que representam as informações (casado, filhos, salário, imposto) da simulação. | |
URL de um link | |
texto do link |
6.4.4. Os controladores [global.asax, main.aspx]
Recordemos o esquema MVC da nossa aplicação:

O controlador [main.aspx] tem de processar 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 do formulário de introdução estiverem corretos, o imposto é calculado através da classe de negócio [impotsODBC]. O controlador devolve ao cliente a vista [simulations.aspx] com o resultado da simulação atual e de todas as anteriores. Se os dados do formulário de introdução estiverem incorretos, o controlador devolve a vista [erreurs.aspx] com a lista de erros e um link para regressar ao formulário.
- retorno: corresponde ao regresso ao formulário após um erro. O controlador apresenta a vista [formulaire.aspx] tal como foi validada antes do erro.
Nesta nova versão, apenas a ação [calcul] sofreu alterações. Com efeito, se os dados forem válidos, esta ação deve conduzir à vista [simulations.aspx], enquanto anteriormente conduzia à vista [formulaire.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
' antes de mais nada, verifica-se se a aplicação conseguiu inicializar-se corretamente
...
' executa-se a ação
Select Case action
Case "init"
' inicializar a aplicação
initAppli()
Case "calcul"
' cálculo do imposto
calculImpot()
Case "retour"
' regresso ao formulário
retourFormulaire()
Case "effacer"
' inicialização da aplicação
initAppli()
Case Else
' ação desconhecida = inicialização
initAppli()
End Select
End Sub
...
Private Sub calculImpot()
' guardar os dados introduzidos
Session.Item("formulaire") = Request.Form
' verifica-se a validade dos dados introduzidos
Dim erreurs As ArrayList = checkData()
' se houver erros, estes são assinalados
If erreurs.Count <> 0 Then
' prepara-se a página de erros
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
context.Items("erreurs") = erreurs
Server.Transfer("erreurs.aspx")
End If
' aqui não há erros — calcula-se o imposto
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
Request.Form("rdMarie") = "oui", _
CType(Request.Form("txtEnfants"), Integer), _
CType(Request.Form("txtSalaire"), Long))
' adiciona-se o resultado às simulações existentes
Dim simulations As ArrayList
If Not Session.Item("simulations") Is Nothing Then
simulations = CType(Session.Item("simulations"), ArrayList)
Else
simulations = New ArrayList
End If
' adição da simulação atual
Dim simulation() As String = New String() {Request.Form("rdMarie").ToString, _
Request.Form("txtEnfants").ToString, Request.Form("txtSalaire").ToString, _
impot.ToString}
simulations.Add(simulation)
' colocam-se as simulações na sessão e no contexto
context.Items("simulations") = simulations
Session.Item("simulations") = simulations
' exibe-se a página de resultados
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
Server.Transfer("simulations.aspx", True)
End Sub
...
End Class
No texto acima, mantivemos apenas o que era necessário para compreender as alterações que se encontram exclusivamente na função [calculImpots]:
- em primeiro lugar, a função guarda o formulário [Request.Form] na sessão, para poder regenerar o formulário 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], regressa-se ao formulário através do link [Retour au formulaire]. Para o restabelecer corretamente, é necessário ter guardado previamente os seus valores na sessão.
- Se os dados introduzidos estiverem corretos, a função insere a simulação atual (casado, filhos, salário, imposto) na lista de simulações. Esta lista encontra-se na sessão associada à chave «simulações».
- A lista de simulações é recolocada na sessão para utilização futura. É também colocada no contexto atual, pois é aí que a vista [simulations.aspx] a espera
- A vista [simulations.aspx] é apresentada assim que as restantes informações de que necessita forem colocadas no contexto
6.4.5. Resumo das alterações
A aplicação está pronta para ser testada. Enumeremos as alterações introduzidas em relação às versões anteriores:
- foi criada uma nova vista
- o controlador [main.aspx.vb] foi alterado num ponto: tratamento da ação [calcul]
6.4.6. Teste da aplicação web
Convidamos o leitor a realizar os testes. Recordamos o procedimento. Todos os ficheiros da aplicação estão colocados numa pasta <application-path>. Nesta pasta, é criada uma subpasta [bin], na qual se encontra o assembly [impot.dll], resultante da compilação dos ficheiros das classes de negócio: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb. O ficheiro [impot.dll] gerado por este comando deve ser colocado em <application-path>\bin para que a aplicação web tenha acesso ao mesmo. O servidor Cassini é iniciado com os parâmetros (<application-path>,/impots4).
6.5. Conclusion
Os exemplos anteriores demonstraram, num caso concreto, os mecanismos habitualmente utilizados no desenvolvimento web. Utilizámos sistematicamente a arquitetura MVC pelo seu valor pedagógico. Teríamos podido tratar estes mesmos exemplos de forma diferente e talvez mais simples sem esta arquitetura. No entanto, esta oferece grandes vantagens assim que a aplicação se torna um pouco mais complexa, com várias páginas.
Poderíamos dar continuidade aos nossos exemplos de várias formas. Eis algumas delas:
- o utilizador poderá querer guardar as suas simulações ao longo do tempo. Realizaria simulações no dia D e poderia 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 é transportado por este mecanismo. Poderíamos também utilizá-lo para transportar as simulações entre o cliente e o servidor.
- Ao mesmo tempo que o servidor envia a página com os resultados das simulações, envia nos seus cabeçalhos HTTP um cookie contendo uma cadeia de caracteres que representa as simulações. Como estas se encontram num objeto [ArrayList], é necessário realizar uma transformação desse objeto para [String]. O servidor atribuiria ao cookie um período de validade, por exemplo, 30 dias.
- O navegador do cliente armazena os cookies recebidos num ficheiro e reenvia-os sempre que faz uma solicitação a um servidor que lhos tenha enviado, caso ainda estejam válidos (período de validade não ultrapassado). Para efeitos de simulação, o servidor receberá uma cadeia de caracteres [String], que deverá transformar no objeto [ArrayList].
Os cookies são geridos por [Response.Cookies] quando são enviados para o cliente e por [Request.Cookies] quando são recebidos no servidor.
- O mecanismo anterior pode tornar-se bastante pesado se houver um número significativo de simulações. Além disso, é frequente que um utilizador limpe periodicamente os seus cookies, eliminando-os todos, mesmo que, por outro lado, autorize o seu navegador a utilizá-los. Assim, mais cedo ou mais tarde, perder-se-á o cookie das simulações. Podemos, então, querer armazená-las no servidor em vez de no cliente, numa base de dados, por exemplo. Para associar simulações a um utilizador específico, a aplicação poderia iniciar com uma fase de identificação que exigisse um nome de utilizador e uma palavra-passe, os quais estariam armazenados numa base de dados ou em qualquer outro tipo de repositório de dados.
- Também poderemos querer garantir a segurança do 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 propostas na página que lhe foi enviada
O que acontece, por exemplo, se o utilizador solicitar diretamente a URL [http://localhost/impots4/formulaire.aspx]? Este caso é pouco provável, uma vez que o utilizador desconhece a existência desta URL. No entanto, deve ser previsto. Pode ser gerido pelo controlador de aplicação [global.asax], que recebe todas as solicitações feitas à aplicação. Desta forma, pode verificar se o recurso solicitado é, de facto, [main.aspx].
Um caso mais provável é o de um utilizador não utilizar as ações presentes na página que o servidor lhe enviou. Por exemplo, o que acontece se o utilizador solicitar diretamente a URL [http://localhost/impots4/main.aspx?action=retour] sem, previamente, preencher o formulário? Vamos experimentar. Obtemos a seguinte resposta:

O servidor falhou. É normal. Para a ação [retour], o controlador espera encontrar na sessão um objeto [NameValueCollection] que represente os valores do formulário que deve apresentar. Não os encontra. O mecanismo do controlador permite dar uma solução elegante a 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. Pode utilizar-se o seguinte mecanismo:
- antes de enviar a sua resposta ao cliente, o controlador armazena na sessão deste uma informação que identifica essa página
- 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 associam as páginas às ações autorizadas 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 têm uma ampla base comum e que é possível construir um controlador genérico, sendo a sua especialização para uma determinada aplicação efetuada através de um ficheiro de configuração. Este é o caminho seguido, por exemplo, pela ferramenta [Struts] no domínio da programação web em Java.



