6. Exemplos
Neste capítulo, propomos ilustrar o que foi visto anteriormente por meio de uma série de exemplos.
6.1. Exemplo 1
6.1.1. O problema
Esta aplicação deve permitir que um usuário calcule seu imposto. Consideramos o caso simplificado de um contribuinte que tenha apenas seu salário para declarar (dados de 2004 referentes à renda de 2003):
- calcula-se o número de cotas do empregado nbParts = nbEnfants/2 + 1 se ele for solteiro, nbEnfants/2 + 2 se for casado, onde nbEnfants é o número de filhos.
- se ele tiver pelo menos três filhos, recebe mais meia parcela
- calcula-se sua renda tributável R = 0,72 * S, onde S é seu salário anual
- calcula-se seu coeficiente familiar QF = R/nbParts
- calcula-se seu imposto I. Consideremos a tabela a seguir:
4262 | 0 | 0 |
8382 | 0,0683 | 291,09 |
14.753 | 0,1914 | 1.322,92 |
23.888 | 0,2826 | 2.668,39 |
38.868 | 0,3738 | 4.846,98 |
47.932 | 0,4262 | 6.883,66 |
0 | 0,4809 | 9505,54 |
Cada linha possui 3 campos. Para calcular o imposto I, procura-se a primeira linha em que QF ≤ campo1. Por exemplo, se QF = 5000, será encontrada 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 serão utilizados os coeficientes da última linha. Aqui:
o que resulta no imposto I = 0,4809 * R - 9.505,54 * nbParts.
6.1.2. A estrutura MVC do aplicativo
A estrutura MVC do aplicativo será a seguinte:

A função do controlador será desempenhada pela página [main.aspx]. Haverá três ações possíveis:
- init: corresponde à primeira solicitação do cliente. O controlador exibirá a visualização [formulaire.aspx]
- calcul: corresponde à solicitação de cálculo do imposto. Se os dados do formulário de preenchimento estiverem corretos, o imposto é calculado por meio da classe de negócios [impots]. O controlador retorna ao cliente a visualização [formulaire.aspx] tal como havia sido validada, além do imposto calculado. Se os dados do formulário de preenchimento estiverem incorretos, o controlador retornará a visualização [erreurs.aspx] com a lista de erros e um link para retornar ao formulário.
- retorno: corresponde ao retorno ao formulário após um erro. O controlador exibe a visualização [formulaire.aspx] tal como foi validada antes do erro.
O controlador [main.aspx] não possui conhecimento sobre o cálculo de impostos. Ele é responsável apenas por gerenciar o diálogo cliente-servidor e executar as ações solicitadas pelo cliente. Para a ação [calcul], ele utilizará a classe de negócios [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]
' eles são recuperados — 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 cotas
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]. Essa 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. Esse método pode lidar com uma exceção caso os dados não tenham sido obtidos ou se estiverem incorretos. Uma vez criado o objeto [impot], é possível chamar repetidamente seu método **calculer**, que calcula o imposto do contribuinte com base em seu estado civil (casado ou solteiro), no número de filhos e no salário anual.
6.1.4. A classe de acesso aos dados
A classe [impotsData] é a classe que permite acessar os dados. Trata-se de uma classe abstrata. É necessário criar uma classe derivada para cada nova fonte de dados possível (tabelas, arquivos simples, bancos de dados, console, etc.). 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 haver 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 do mesmo tamanho
If valide Then valide = limites.Length = coeffr.Length AndAlso limites.Length = coeffn.Length
If Not valide Then Return 2
' as tabelas devem estar preenchidas
valide = limites.Length <> 0
If Not valide Then Return 3
' cada tabela deve conter elementos >=0 e em ordem crescente
valide = check(limites, limites.Length - 1) AndAlso check(coeffr, coeffr.Length) AndAlso check(coeffn, coeffn.Length)
If Not valide Then Return 4
' tudo está correto
Return 0
End Function
' verifica a validade do conteúdo de um array
Protected Function check(ByRef tableau() As Decimal, ByVal n As Integer) As Boolean
' a matriz deve ter 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 de faixas de impostos | |
tabela dos coeficientes aplicados à renda tributável | |
tabela dos coeficientes aplicados ao número de cotas | |
valor booleano indicando se os dados (limites, coeffr, coeffn) foram verificados | |
valor booleano indicando se os dados (limites, coeffr, coeffn) são válidos |
A classe não possui construtor. Ela possui um método abstrato [getData] que as classes derivadas deverão implementar. Esse método tem como função:
- atribuir valores às três matrizes limites, coeffr, coeffn
- lançar uma exceção caso os dados não tenham podido ser obtidos ou se forem inválidos.
A classe fornece os métodos protegidos [checkData] e [check], que verificam a validade dos atributos (limites, coeffr, coeffn). Isso dispensa as classes derivadas de implementá-los. Elas precisarão apenas utilizá-los.
A primeira classe derivada que utilizaremos será a seguinte:
Imports System.Collections
Imports System
Namespace st.istia.univangers.fr
Public Class impotsArray
Inherits impotsData
' construtor sem argumento
Public Sub New()
' inicializações de 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 entrada
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, retorna-se as três matrizes
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Essa classe, chamada [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á que as classes externas obtenham as matrizes (limites, coeffr, coeffn), limita-se a verificar a validade das três matrizes por meio do método [checkData] da classe base. Ele lança uma exceção se os dados forem inválidos.
6.1.5. Testes das classes de negócios e das classes de acesso aos dados
É importante incluir em uma aplicação web apenas classes de negócio e de acesso a dados cuja correção tenha sido certificada. Dessa forma, a fase de depuração da aplicação web poderá se concentrar na parte do controlador e das visualizações. 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 imposto
' o usuário digita três dados no teclado: casado nbEnfants salário
' o programa exibe 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 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
' loop infinito
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Long
While True
' solicita-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()
' o que fazer?
If paramètres Is Nothing OrElse paramètres = "" Then
Exit While
End If
' verificação do número de argumentos na linha inserida
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
O aplicativo solicita que o usuário digite no teclado as três informações necessárias para calcular seu imposto:
- seu estado civil: o para casado, n para solteiro
- o número de filhos
- seu salário anual
O cálculo do imposto é feito por meio de um objeto do tipo [impot], criado assim que o aplicativo é iniciado:
' 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 dessa 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, pois, para ser criado, o objeto solicitará os dados (limites, coeffr, coeffn) à sua fonte de dados, que foi passada a ele 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 (código fixo) não pode provocar uma exceção. No entanto, mantivemos o tratamento dessa exceção para chamar a atenção do leitor para a possibilidade de o objeto [impot] ser construído incorretamente.
Aqui está 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 todas as classes [impot, impotsData, impotsArray] em um assembly [impot.dll]:
dos>vbc /t:library /out:impot.dll impotsData.vb impotsArray.vb impots.vb
Compilateur Microsoft (R) Visual Basic .NET version 7.10.3052.4
dos>dir
05/04/2004 13:28 1 337 impots.vb
21/04/2004 08:23 1 311 impotsArray.vb
21/04/2004 08:26 1 634 impotsData.vb
21/04/2004 08:42 2 490 testimpots1.vb
21/04/2004 09:21 5 632 impot.dll
Compilamos o programa de teste:
dos>dir
05/04/2004 13:28 1 337 impots.vb
21/04/2004 08:23 1 311 impotsArray.vb
21/04/2004 08:26 1 634 impotsData.vb
21/04/2004 08:42 2 490 testimpots1.vb
21/04/2004 09:21 5 632 impot.dll
21/04/2004 09:23 4 608 testimpots1.exe
Podemos 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 telas do aplicativo web
O aplicativo terá duas visualizações: [formulaire.aspx] e [erreurs.aspx]. Vamos ilustrar o funcionamento do aplicativo por meio de capturas de tela. A visualização [formulaire.aspx] é exibida quando a URL [main.aspx] é acessada pela primeira vez:

O usuário preenche o formulário:

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

Ele pode cometer erros ao inserir os dados:

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

Ele pode usar o link [Retour au formulaire] acima para retornar à visualização [formulaire.aspx], tal como a havia validado antes do erro:

6.1.7. A visualização [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 de seleção [oui] deve ser marcada, "" caso contrário | |
o mesmo vale para a caixa de seleção [non] | |
valor a ser inserido no campo de entrada [txtEnfants] | |
valor a ser inserido no campo de entrada [txtSalaire] | |
valor a ser inserido no campo de entrada [txtImpot] |
A página possui dois formulários, cada um com um botão [submit]. O botão [Calculer] é o botão [submit] do formulário a seguir:
<form method="post" action="main.aspx?action=calcul">
...
<P>
<INPUT type="submit" value="Calculer">
</P>
</form>
Vemos que os parâmetros do formulário serão enviados ao controlador com [action=calcul]. O botão [Effacer] é o botão [submit] do formulário a seguir:
<form method="post" action="main.aspx?action=effacer">
<INPUT type="submit" value="Effacer">
</form>
Vemos que os parâmetros do formulário serão enviados ao controlador com [action=effacer]. Aqui, o formulário não possui nenhum parâmetro. Apenas a ação é importante.
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 ser exibida
' 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] é feito a partir de duas informações inseridas pelo controlador no contexto da página:
- Context.Items("formulário"): dicionário do tipo NameValueCollection contendo os valores dos campos HTML [rdmarie,txtEnfants,txtSalaire]
- Context.Items("txtImpot"): valor do imposto
6.1.8. A visualização [erreurs.aspx]
A visualização [erreurs.aspx] é aquela que exibe os eventuais erros que possam ocorrer durante a execução do aplicativo. 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 possui três campos dinâmicos:
código HTML de uma lista de erros | |
URL de um link | |
texto do link |
Esses campos são calculados pela parte de controle 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
' recupera-se os elementos do contexto
Dim erreurs As ArrayList = CType(context.Items("erreurs"), ArrayList)
href = context.Items("href").ToString
lien = context.Items("lien").ToString
' gerando 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 inseridas pelo controlador do aplicativo no contexto da página:
objeto ArrayList contendo a lista de mensagens de erro a serem exibidas | |
URL de um link | |
texto do link |
Agora que sabemos o que o usuário do aplicativo vê, podemos passar à criação do controlador do aplicativo.
6.1.9. Os controladores [global.asax, main.aspx]
Vamos relembrar o esquema MVC do nosso aplicativo:

Cliente Lógica de aplicação
O controlador [main.aspx] precisa processar três ações:
- init: corresponde à primeira solicitação do cliente. O controlador exibe a visualização [formulaire.aspx]
- calcul: corresponde à solicitação de cálculo do imposto. Se os dados do formulário de preenchimento estiverem corretos, o imposto é calculado por meio da classe de negócios [impots]. O controlador retorna ao cliente a visualização [formulaire.aspx] tal como havia sido validada, além do imposto calculado. Se os dados do formulário de preenchimento estiverem incorretos, o controlador retorna a visualização [erreurs.aspx] com a lista de erros e um link para retornar ao formulário.
- retorno: corresponde ao retorno ao formulário após um erro. O controlador exibe a visualização [formulaire.aspx] tal como foi validada antes do erro.
Sabe-se, além disso, que toda solicitação à aplicação passa pelo controlador [global.asax], caso ele exista. Temos, então, na entrada da aplicação, uma cadeia de dois controladores:
- [global.asax], que, devido à arquitetura do ASP.NET, recebe todas as solicitações direcionadas ao aplicativo
- [main.aspx], que, por decisão do desenvolvedor, também recebe todas as solicitações direcionadas ao aplicativo
A necessidade do [main.aspx] decorre do fato de que teremos uma sessão para gerenciar. Vimos que o [global.asax] não era adequado como controlador nesse caso. Aqui, poderíamos dispensar totalmente o [global.asax]. No entanto, vamos utilizá-lo para executar código ao iniciar o aplicativo. O esquema MVC acima mostra que precisaremos criar um objeto [impot] para calcular o imposto. Não faz sentido criá-lo várias vezes; uma vez é suficiente. Portanto, vamos criá-lo no início da aplicação, durante o evento [Application_Start] gerenciado pelo controlador [global.asax]. O código deste controlador é 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 de importação
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 é registrado no aplicativo
Application("erreur") = True
End Try
End Sub
End Class
Uma vez criado, o objeto do tipo [impot] é inserido no aplicativo. É lá que as diversas solicitações dos diferentes clientes irão buscá-lo. Como a criação do objeto [impot] pode falhar, tratamos a possível exceção e inserimos uma chave [erreur] no aplicativo para indicar se houve 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 o aplicativo conseguiu ser inicializado 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
' recuperamos a ação a ser realizada
Dim action As String
If Request.QueryString("action") Is Nothing Then
action = "init"
Else
action = Request.QueryString("action").ToString.ToLower
End If
' executamos a ação
Select Case action
Case "init"
' inicializar o aplicativo
initAppli()
Case "calcul"
' cálculo do imposto
calculImpot()
Case "retour"
' retorno ao formulário
retourFormulaire()
Case "effacer"
' inicialização do aplicativo
initAppli()
Case Else
' ação desconhecida = inicialização
initAppli()
End Select
End Sub
Private Sub initAppli()
' exibe 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 inseridos
Dim erreurs As ArrayList = checkData()
' se houver erros, eles são sinalizados
If erreurs.Count <> 0 Then
' os dados inseridos são salvos
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
' exibimos a lista de erros
Return erreurs
End Function
End Class
O controlador começa verificando se o aplicativo foi inicializado corretamente:
' antes de mais nada, verifica-se se o aplicativo conseguiu inicializar 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 detectar que o aplicativo não conseguiu ser inicializado corretamente (o objeto [impot], necessário para o cálculo, não pôde ser criado), ele exibirá a página de erros com os parâmetros adequados. Nesse caso, não é necessário inserir o link de retorno no formulário, uma vez que toda a aplicação está indisponível. Uma mensagem de erro geral é inserida em [Context.Items("erreurs")], do tipo [ArrayList].
Se o controlador verificar que o aplicativo está operacional, ele analisa a ação que lhe é solicitada por meio do parâmetro [action]. Já nos deparamos com esse modo de funcionamento várias vezes. O processamento de cada tipo de ação é delegado a uma função.
6.1.9.1. As ações init e effacer
Essas duas ações devem exibir o formulário de entrada vazio. Vale lembrar que este (ver visualizações) possui 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 esses dois parâmetros de forma a exibir um formulário vazio.
6.1.9.2. A ação de cálculo
Essa ação deve calcular o imposto a pagar com base nos dados inseridos no formulário e devolvê-lo pré-preenchido com os valores inseridos e, além disso, com o valor do imposto calculado. A função [calculImpot], responsável por essa tarefa, começa verificando 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 inseridos forem inválidos, o controlador exibe a visualização [erreurs.aspx], tendo previamente inserido no contexto os valores esperados por ela:
- as mensagens de erro são colocadas em um objeto [ArrayList], objeto esse que é, em seguida, inserido no contexto [Context.Items("erreurs")]
- a URL do link de retorno e o texto desse link também são inseridos no contexto.
Antes de passar o controle para a página [erreurs.aspx], que enviará a resposta ao cliente, os valores inseridos no formulário (Request.Form) são colocados na sessão, associados à chave “formulário”. Isso permitirá que uma solicitação posterior os recupere.
Pode-se questionar aqui se é útil verificar se os campos [rdMarie, txtEnfants, txtSalaire] estão presentes na solicitação enviada pelo cliente. Isso é desnecessário se tivermos certeza de que nosso cliente é um navegador que recebeu a visualização [formulaire.aspx], que contém esses campos. No entanto, nunca podemos ter certeza disso. Mostraremos um pouco mais adiante um exemplo em que o cliente é o aplicativo [curl], já mencionado anteriormente. Consultaremos o aplicativo sem enviar os campos que ele espera e veremos como ele reagirá. Essa é uma regra já mencionada várias vezes e que relembramos aqui: um aplicativo nunca deve fazer suposições sobre o tipo de cliente que o consulta. Por segurança, ele deve considerar que pode ser consultado por um aplicativo programado que pode enviar sequências de parâmetros inesperadas. Ele deve se comportar corretamente em todos os casos.
No nosso caso, verificamos se os campos [rdMarie, txtEnfants, txtSalaire] estavam presentes na solicitação, mas não se ela poderia conter outros. Nesta aplicação, eles seriam ignorados. No entanto, sempre por medida de segurança, seria interessante registrar esse tipo de solicitação em um arquivo de logs e enviar um alerta ao administrador da aplicação para que ele saiba que a aplicação está recebendo solicitações “estranhas”. Ao analisar essas solicitações no arquivo de logs, ele poderia detectar um possível ataque à aplicação e, então, tomar as medidas necessárias para protegê-la.
Se os dados esperados estiverem corretos, o controlador inicia o cálculo do imposto com o objeto [impot] armazenado na aplicação. Em seguida, ele armazena no contexto as duas informações esperadas pela visualização [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 inseridos anteriormente no formulário
- Context.Items("txtImpot"): valor do imposto que acaba de ser obtido
O leitor atento talvez tenha se perguntado ao ler o que foi exposto acima: uma vez que o objeto [impot], criado ao iniciar o aplicativo, é compartilhado entre todas as consultas, não poderia haver conflitos de acesso que levassem à corrupção dos dados do objeto [impot]? Para responder a essa pergunta, precisamos voltar ao código da classe [impot]. As consultas utilizam o método [impot].calculerImpot para obter o imposto a pagar. Portanto, é esse código que precisamos examinar:
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
' cálculo do número de cotas
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 da renda 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 executando o método anterior e seja interrompido. Outro thread passa então a executar o método. Quais são os riscos? Para descobrir, adicionamos o seguinte código:
Dim impot As Long = CLng(revenu * coeffR(i) - nbParts * coeffN(i))
' aguarda 10 segundos
Thread.Sleep(10000)
Return impot
A thread 1, após calcular o valor [impot1] da variável local [impot], é interrompida. 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 retoma o controle. O que ele encontra na variável local [impot]? Como essa variável é local a um método, ela é armazenada em uma estrutura da memória chamada pilha. Essa pilha faz parte do contexto da thread, que é salvo quando esta é interrompida. Quando a thread 2 é iniciada, seu contexto é configurado com uma nova pilha e, portanto, uma nova variável local [impot]. Quando a thread 2 for interrompida por sua vez, seu contexto também será salvo. Quando a thread 1 é reiniciada, seu contexto é restaurado, incluindo sua pilha. Ela então recupera sua variável local [impot] e não a da thread 2. Estamos, portanto, em uma 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 fato, o resultado esperado.
6.1.9.3. A ação de retorno
Essa ação corresponde à ativação do link [Retour vers le formulaire] da visualização [erreurs.aspx] para retornar à visualização [formulaire.aspx], pré-preenchida com os valores inseridos anteriormente e salvos na sessão. A função [retourFormulaire] recupera essas informações. Os dois parâmetros esperados pela visualização [formulaire.aspx] são inicializados:
- Context.Items("formulário") com os valores inseridos anteriormente e salvos na sessão
- Context.Items("txtImpot") com a string vazia
6.1.10. Teste do aplicativo web
Todos os arquivos anteriores estão localizados na pasta <application-path>.

Nessa pasta, é criada uma subpasta [bin], na qual é colocado o pacote [impot.dll], resultante da compilação dos arquivos das classes de negócios: [impots.vb, impotsData.vb, impotsArray.vb]. A seguir, relembramos 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 arquivo [impot.dll] acima deve ser colocado em <application-path>\bin para que o aplicativo web tenha acesso a ele. O servidor Cassini é iniciado com os parâmetros (<application-path>,/impots1). Em um navegador, acessamos a URL [http://localhost/impots1/main.aspx]:

Preenchemos o formulário:

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

Em seguida, inserimos dados incorretos:

Ao clicar no botão [Calculer], obtemos a seguinte resposta:

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

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

6.1.11. Uso 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 postados quando ele for validado, o navegador reenviará os valores desses parâmetros ao servidor. Outro cliente pode não fazer isso e, nesse caso, o servidor receberia uma solicitação na qual faltariam parâmetros. Ele deve saber o que fazer nessa situação. Outro exemplo são as verificações de entrada de dados feitas no lado do cliente. Se o formulário contiver dados a serem verificados, essa verificação pode ser feita no lado do cliente por meio de scripts incluídos no documento que contém o formulário. O navegador só enviará o formulário se todos os dados verificados no lado do cliente forem válidos. Pode-se então ficar tentado, no lado do servidor, a considerar que receberá dados já validados e não querer realizar essa verificação uma segunda vez. Isso seria um erro. De fato, um cliente que não seja um navegador poderia enviar dados inválidos ao servidor e, nesse caso, o aplicativo web correria o risco de apresentar um comportamento inesperado. Vamos ilustrar esses pontos usando o cliente [curl].
Primeiramente, 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 nos enviou 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. Vamos solicitar a ação [calcul] sem fornecer parâmetros:
dos>curl --cookie ASP.NET_SessionId=ivthkl45tjdjrzznevqsf255 --include --url http://localhost/impots1/main.aspx?action=calcul
HTTP/1.1 200 OK
Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0
Date: Thu, 01 Apr 2004 15:22:42 GMT
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 380
Connection: Close
<HTML>
<HEAD>
<title>Impôt</title>
</HEAD>
<body>
<P>Les erreurs suivantes se sont produites :</P>
<HR>
<ul>
<li> Vous n'avez pas indiqué votre statut marital</li>
<li> Le nombre d'enfants est incorrect</li>
<li> Le salaire annuel est incorrect</li>
</ul>
<a href="main.aspx?action=retour">
Retour au formulaire
</a>
</body>
</HTML>
Podemos observar que o aplicativo web retornou a visualização [erreurs] com três mensagens de erro para os três parâmetros ausentes. Vamos agora enviar parâmetros incorretos:
dos>curl --cookie ASP.NET_SessionId=ivthkl45tjdjrzznevqsf255 --include --data rdMarie=xx --data txtEnfants=xx --data txtSalaire=xx --url http://localhost/impots1/main.aspx?action=calcul
HTTP/1.1 200 OK
Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0
Date: Thu, 01 Apr 2004 15:25:50 GMT
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 380
Connection: Close
<HTML>
<HEAD>
<title>Impôt</title>
</HEAD>
<body>
<P>Les erreurs suivantes se sont produites :</P>
<HR>
<ul>
<li> Vous n'avez pas indiqué votre statut marital</li>
<li> Le nombre d'enfants est incorrect</li>
<li> Le salaire annuel est incorrect</li>
</ul>
<a href="main.aspx?action=retour">
Retour au formulaire
</a>
</body>
</HTML>
Os três erros foram detectados 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 recuperar o imposto a pagar: 4.300 euros. O que aprendemos com este exemplo é que não devemos nos deixar enganar pelo fato de estarmos desenvolvendo um aplicativo web destinado a clientes que são navegadores. Um aplicativo web é um serviço TCP/IP, e esse protocolo de rede não permite determinar a natureza do aplicativo cliente de um serviço. Portanto, não é possível saber se o cliente de um aplicativo web é um navegador ou não. Assim, seguimos duas regras:
- ao receber uma solicitação de um cliente, não fazemos nenhuma suposição sobre o cliente e verificamos se os parâmetros esperados na solicitação estão presentes e são válidos
- elaboramos uma resposta destinada a navegadores, portanto, em geral, documentos HTML
Uma aplicação web pode ser desenvolvida para atender simultaneamente a diferentes clientes, por exemplo, navegadores e celulares. Assim, é possível incluir em cada solicitação um novo parâmetro indicando o tipo de cliente. Dessa forma, um navegador solicitará o cálculo do imposto por meio de uma solicitação na URL http://machine/impots/main.aspx?client=navegador&action=cálculo, enquanto o celular fará 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 desse tipo. Ela fica da seguinte forma:

O bloco [Classes métier, Classes d'accès aux données] não sofre alterações. Trata-se, de fato, de uma parte irrelevante para o cliente. O bloco [Contrôleur] sofre poucas alterações, mas deve levar em conta um novo parâmetro na solicitação: o parâmetro [client], que indica com que tipo de cliente se está lidando. O bloco [vues] deve gerar visualizações para cada tipo de cliente. Pode ser interessante levar em conta, já na fase de concepção do aplicativo, a presença do parâmetro [client] na consulta, mesmo que o objetivo a curto ou médio prazo seja apenas atender aos navegadores. Se, posteriormente, a aplicação precisar gerenciar um novo tipo de cliente, bastará escrever apenas as visualizações adaptadas a ele.
6.2. Exemplo 2
6.2.1. O problema
Propomos aqui abordar o mesmo problema que anteriormente, mas modificando a fonte de dados do objeto [impot] criado pelo aplicativo web. Na versão anterior, a fonte de dados utilizada fornecia os valores de tabelas definidas “em código”. Desta vez, a nova fonte de dados os obterá de uma fonte de dados ODBC associada a um banco de dados MySQL.
6.2.2. A fonte de dados ODBC
Os dados estarão em uma tabela chamada [IMPOTS], pertencente a um banco de dados MySQL chamado [dbimpots]. O conteúdo dessa tabela será o seguinte:

O proprietário do banco de dados é o usuário [admimpots], com a senha [mdpimpots]. Associamos uma fonte de dados ODBC a esse banco de dados. Antes de fazer isso, vamos primeiro relembrar as diferentes formas de acessar um banco de dados com a plataforma .NET.
Existem inúmeras bases de dados para as plataformas Windows. Para acessá-las, os aplicativos utilizam programas chamados drivers.

No esquema acima, o driver apresenta duas interfaces:
- a interface I1, apresentada ao aplicativo
- a interface I2 voltada para o banco de dados
Para evitar que um aplicativo escrito para um banco de dados B1 precise ser reescrito caso haja migração para um banco de dados B2 diferente, foi realizado um trabalho de padronização na interface I1. Se forem utilizadas bases de dados que utilizem drivers “padronizados”, a base B1 será fornecida com um driver P1, o banco de dados B2 com um driver P2, e a interface I1 desses dois drivers será idêntica. Portanto, não será necessário reescrever o aplicativo. Assim, será possível, por exemplo, migrar um banco de dados ACCESS para um banco de dados MySQL sem alterar o aplicativo.
Existem dois tipos de drivers padronizados:
- os drivers ODBC (Open DataBase Connectivity)
- os drivers OLE e DB (Object Linking and Embedding DataBase)
Os drivers ODBC permitem o acesso a bancos de dados. As fontes de dados para os drivers OLE e DB são mais variadas: bancos de dados, sistemas de mensagens, diretórios, etc. Não há limite. Qualquer fonte de dados pode ser objeto de um driver Ole DB, caso o desenvolvedor assim o decida. A vantagem é, obviamente, 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 acessar os bancos de dados do Microsoft SQL Server
- as classes Ole Db.NET, para acessar bancos de dados SGBD que oferecem um driver OLE DB
- as classes odbc.net, para acessar os bancos de dados SGBD, que oferecem um driver ODBC
O SGBD MySQL já dispõe há muito tempo de um driver ODBC. É esse que estamos usando 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, esse caminho pode variar ligeiramente. Obtemos o seguinte aplicativo, que nos permitirá criar nossa fonte ODBC:

Vamos criar uma fonte de dados do sistema, c.a.d. Uma fonte de dados que qualquer usuário da máquina poderá acessar (utiliser.Aussi); acima, selecionamos a guia [Source de données système]. A página exibida possui um botão [Ajouter] que utilizaremos para criar uma nova fonte de dados ODBC:

O assistente solicita que selecionemos o driver ODBC a ser utilizado. O Windows vem com vários drivers ODBC pré-instalados. O driver ODBC do MySQL não faz parte desse conjunto. Portanto, é necessário instalá-lo previamente. Ele pode ser encontrado na internet digitando a sequência de palavras-chave “MySQL ODBC” ou ainda “MyODBC” em um mecanismo de busca. Neste caso, instalamos o driver [MySQL ODBC 3.51]. Selecionamos esse driver e passamos para o [Terminer]:

É necessário fornecer algumas informações:
o nome que identificará a fonte de dados ODBC. Qualquer aplicativo do Windows poderá acessar a fonte por meio desse nome | |
um texto arbitrário que descreva a fonte de dados | |
o nome do computador que hospeda o SGBD MySQL. Neste caso, trata-se da máquina local. Poderia ser uma máquina remota. Isso permitiria que um aplicativo do Windows acessasse um banco de dados remoto sem qualquer codificação específica. Esse é um grande diferencial da fonte ODBC. | |
Um SGBD MySQL pode gerenciar várias bases de dados. Aqui, especifica-se qual delas se deseja gerenciar: dbimpots | |
nome de um usuário registrado no SGBD MySQL. É com esse nome que serão feitos os acessos à fonte de dados. Neste caso: admimpots | |
a senha desse usuário. Aqui: mdpimpots | |
porta de trabalho do SGBD MySQL. Por padrão, é a porta 3306. Não a alteramos |
Feito isso, testamos a validade de nossos parâmetros de conexão com o botão [Test Data Source]:

Feito isso, temos certeza de nossa fonte de dados ODBC. Agora podemos utilizá-la. Repetimos o procedimento [OK] quantas vezes forem necessárias para sair do assistente ODBC.
Caso o leitor não disponha do SGBD mySQL, ele pode obtê-lo gratuitamente no endereço [http://www.mysql.com]. Apresentamos a seguir o procedimento para criar uma fonte de dados ODBC com o Access. As primeiras etapas são idênticas às descritas anteriormente. Adiciona-se uma nova fonte de dados do sistema:

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

As informações a serem fornecidas são as seguintes:
o nome que identificará a fonte de dados ODBC. Qualquer aplicativo do Windows poderá acessar a fonte por meio desse nome | |
um texto arbitrário que descreva a fonte de dados | |
o nome completo do arquivo ACCESS a ser utilizado |
6.2.3. Uma nova classe de acesso aos dados
Voltemos à estrutura MVC do nosso aplicativo:

No esquema acima, a classe [impotsData] é responsável por recuperar os dados. Ela deverá fazer isso aqui a partir da base de dados MySQL [dbimpots]. Sabemos, desde a versão anterior desta aplicação, que [impotsData] é uma classe abstrata que deve ser derivada sempre que se deseja adaptá-la a uma nova fonte de dados. Vamos relembrar a estrutura dessa 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 coletados
...
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 herda 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 retorna os três tabuletos (limites, coeffr, coeffn)
Criamos a classe [impotsODBC], que irá buscar os dados (limites, coeffr, coeffn) em uma fonte ODBC, à qual daremos o nome de:
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)
' observa-se as três informações
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] do banco de dados ODBC DSNimpots
' limites, coeffr e coeffn são as três colunas dessa tabela
' pode gerar diversas 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 acessar o banco de dados
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
' está-se criando um objeto de comando
sqlCommand = New OdbcCommand(selectCommand, impotsConn)
' executando a consulta
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
' Análise da tabela recuperada
While myReader.Read()
' os dados da linha atual são inseridos nas tabelas
aLimites.Add(myReader("limites"))
aCoeffR.Add(myReader("coeffr"))
aCoeffN.Add(myReader("coeffn"))
End While
' liberaçã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ç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, retorna-se as três tabelas
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Vamos dar uma olhada no fabricante:
' construtor
Public Sub New(ByVal DSNimpots As String)
' registram-se as três informações
Me.DSNimpots = DSNimpots
End Sub
Ele recebe como parâmetro o nome da fonte ODBC, na qual se encontram os dados a serem adquiridos. O construtor se limita a armazenar 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 seu código:
- os parâmetros da conexão com a fonte de dados ODBC estão definidos, mas a fonte de dados não está aberta
' cadeia de conexão com o banco de dados
Dim connectString As String = "DSN=" + DSNimpots + ";"
' criamos um objeto de conexão com o banco de dados — essa conexã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 ao banco de dados está envolto por um try/catch para lidar com um possível erro de acesso. Abrimos a conexão com o banco de dados:
' tenta-se acessar o banco de dados
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
- Executamos o comando [select] na conexão aberta. Obtemos um objeto [OdbcDataReader] que nos permitirá percorrer as linhas da tabela resultante da consulta SELECT:
' é criado um objeto de comando
Dim sqlCommand As OdbcCommand = New OdbcCommand(selectCommand, impotsConn)
' é executada a consulta
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
- Percorremos a tabela de resultados, linha por linha. Para isso, utilizamos o método [Read] do objeto [OdbcDataReader] obtido anteriormente. Esse método realiza duas ações:
- avança uma linha na tabela. Inicialmente, estamos posicionados antes da primeira linha
- retorna o valor booleano [true] se conseguiu avançar, e [false] caso contrário; este último caso indica que todas as linhas já foram processadas.
As colunas da linha atual do objeto [OdbcDataReader] são obtidas por meio de OdbcDataReader. Obtém-se um objeto que representa o valor da coluna. Percorremos toda a tabela para inserir seu conteúdo nos três objetos [ArrayList]:
' Análise da tabela recuperada
While myReader.Read()
' os dados da linha atual são inseridos nas tabelas
aLimites.Add(myReader("limites"))
aCoeffR.Add(myReader("coeffr"))
aCoeffN.Add(myReader("coeffn"))
- Feito isso, liberamos os recursos associados à conexão:
- O conteúdo dos três objetos [ArrayList] é transferido para três matrizes convencionais:
' 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 inseridos nas três tabelas, basta verificar o conteúdo delas usando 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, retorna-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 imposto
' o usuário digita três dados no teclado: casado nbEnfants salário
' o programa exibe 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
' recuperação dos 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
' solicitando 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()
' o que fazer?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' verificação do número de argumentos na linha inserida
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
O aplicativo é iniciado com um parâmetro:
- DSNimpots: nome da fonte de dados ODBC a ser utilizada
O cálculo do imposto é feito por meio de um objeto do tipo [impot], criado assim 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
Após ser inicializado, o aplicativo solicita repetidamente que o usuário digite no teclado as três informações necessárias para calcular seu imposto:
- seu estado civil: o para casado, n para solteiro
- o número de filhos
- 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 é compilado por sua vez:
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)
Alteramos 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 visualizações do aplicativo 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] precisa ser modificado. Ele é responsável por criar o objeto [impot] ao iniciar a aplicação. O construtor desse objeto tem como único parâmetro o objeto do tipo [impotsData], responsável por recuperar os dados. Portanto, esse parâmetro muda para cada novo tipo de fonte de dados. O controlador [global.asax.vb] passa a ser o seguinte:
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' cria-se um objeto de imposto
Dim objImpot As impot
Try
objImpot = New impot(New impotsODBC(ConfigurationSettings.AppSettings("DSNimpots")))
' o objeto é inserido no aplicativo
Application("objImpot") = objImpot
' sem erros
Application("erreur") = False
Catch ex As Exception
'ocorreu um erro; registra-se isso no aplicativo
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 inserir esse nome diretamente no código, ele é colocado no arquivo de configuração [web.config] do aplicativo:
<?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 seção <appSettings> do arquivo [web.config] é obtido no código do aplicativo por meio de [ConfigurationSettings.AppSettings(C)].
Para identificar a causa da exceção, registra-se a mensagem da exceção no aplicativo, de modo que ela permaneça disponível para consultas. O controlador [main.aspx.vb] incluirá essa mensagem em 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 o aplicativo conseguiu ser inicializado 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...(" + Application("message").ToString + ")")
context.Items("erreurs") = erreurs
context.Items("lien") = ""
context.Items("href") = ""
Server.Transfer("erreurs.aspx")
End If
' recuperamos a ação a ser realizada
...
6.2.7. Resumo das alterações
O aplicativo está pronto para ser testado. Vamos listar as alterações feitas em relação à versão anterior:
- foi criada uma nova classe de acesso aos dados
- o controlador [global.asax.vb] foi modificado em dois pontos: criação do objeto [impot] e registro na aplicação da mensagem relacionada a uma eventual exceção
- o controlador [main.aspx.vb] foi modificado em um ponto para exibir a mensagem de exceção anterior
- foi adicionado um arquivo [web.config]
O trabalho de modificação foi realizado essencialmente no 1, c.a.d, fora do aplicativo web. Isso foi possível graças à arquitetura MVC do aplicativo, que separa o controlador das classes de negócios. É aí que reside todo o interesse dessa arquitetura. Seria possível demonstrar que, com um arquivo de configuração [web.config] adequado, teríamos evitado qualquer modificação no controlador da aplicação. É possível definir no [web.config] o nome da classe de acesso aos dados a ser instanciada dinamicamente, bem como os diversos parâmetros necessários para essa instanciação. Com essas informações, o [global.asax] pode instanciar o objeto de acesso aos dados. Mudar a fonte de dados equivale, então, a:
- criar a classe de acesso a essa fonte, caso ela ainda não exista
- modificar o arquivo [web.config] para permitir a criação dinâmica de uma instância dessa classe em [global.asax]
6.2.8. Teste do aplicativo web
Todos os arquivos anteriores são colocados em uma pasta <application-path>.

Nessa pasta, é criada uma subpasta [bin], na qual é colocado o assembly [impot.dll], resultante da compilação dos arquivos das classes de negócio: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb]. A seguir, relembramos 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 arquivo [impot.dll] acima deve ser colocado em <application-path>\bin para que o aplicativo web tenha acesso a ele. O servidor Cassini é iniciado com os parâmetros (<application-path>,/impots2). Os testes apresentam os mesmos resultados da versão anterior, sendo que a presença do banco de dados é transparente para o usuário. Para ilustrar, no entanto, essa presença, fazemos com que a fonte ODBC não esteja disponível, parando os arquivos SGBD e MySQL, e acessamos a URL [http://localhost/impots2/main.aspx]. Recebemos 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 pelo aplicativo web. Desta vez, a nova fonte de dados será um banco de dados ACCESS, ao qual acessaremos por meio de um driver OLEDB. Nosso objetivo é mostrar outra maneira de acessar um banco de dados.
6.3.2. A fonte de dados OLEDB
Os dados estarão em uma tabela chamada [IMPOTS], pertencente ao banco de dados ACCESS. O conteúdo dessa tabela será o seguinte:

6.3.3. A classe de acesso aos dados
Voltemos à estrutura MVC do nosso aplicativo:

- No esquema acima, a classe [impotsData] é responsável por recuperar os dados. Desta vez, ela deverá fazê-lo a partir de uma fonte OLEDB.
Criamos a classe [impotsOLEDB], que irá buscar os dados (limites, coeffr, coeffn) em uma fonte ODBC, à qual daremos o nome de:
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)
' registram-se as três informações
Me.chaineConnexion = chaineConnexion
End Sub
Public Overrides Function getData() As Object()
' inicializa as três matrizes limites, coeffr e coeffn a partir
' do conteúdo da tabela [impots] do banco de dados OLEDB [chaineConnexion]
' limites, coeffr e coeffn são as três colunas dessa tabela
' pode lançar diversas 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 recepçã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, retorna-se as três tabelas
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Vamos dar uma olhada no fabricante:
' construtor
Public Sub New(ByVal chaineConnexion As String)
' registram-se as três informações
Me.chaineConnexion = chaineConnexion
End Sub
Ele recebe como parâmetro a string de conexão da fonte OLEDB, na qual se encontram os dados a serem adquiridos. O construtor se limita a armazená-la. Uma string de conexão contém todos os parâmetros necessários para que o driver OLEDB se conecte à fonte OLEDB. Geralmente, ela é bastante complexa. Para descobrir a cadeia de conexão das bases de dados ACCESS, é possível utilizar a ferramenta [WebMatrix]. Inicie essa ferramenta. Ela exibe uma janela que permite conectar-se a uma fonte de dados:
![]() ![]() | ![]() ![]() |
Por meio do ícone indicado pela seta acima, é possível criar uma conexão com dois tipos de bancos de dados da Microsoft: SQL Server e ACCESS. Vamos escolher ACCESS:

Utilizamos o botão [...] para selecionar o banco de dados ACCESS. Confirmamos o assistente. Na guia [Data], os ícones representam a conexão:

Agora, vamos criar um novo arquivo .aspx por meio do [Files/New File]:

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

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

Clique com o botão direito do mouse no objeto [AccessDataSourceControl] abaixo para acessar essas propriedades:

A cadeia de conexão OLEDB com o banco 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 essa string é formada por uma parte fixa e uma parte variável, que é simplesmente o nome do arquivo ACCESS. Utilizaremos esse fato para gerar a string de conexã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 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 isso, definimos a consulta [select] a ser executada e a associamos ao objeto [DataAdapter]. O construtor deste objeto também solicita a cadeia de conexão que deverá ser usada para se conectar à 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] por meio do método [Fill] do objeto [DataAdapter]. O resultado do [select] é inserido em um objeto [DataTable] criado para essa finalidade. Um objeto [DataTable] é a representação em memória de uma tabela de banco de dados, c.a.d, um conjunto de linhas e colunas. Tratamos uma exceção que pode ocorrer se, por exemplo, a string de conexã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], trazida pelo [select]. Um objeto [DataTable] é uma tabela, portanto, um conjunto de linhas. Essas linhas podem ser acessadas por meio 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. Essa linha possui colunas acessíveis por meio do objeto [DataRow], através de 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 completa:
' dimensiona-se as tabelas de recepçã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 inseridos nas três tabelas, basta verificar o conteúdo delas usando 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, retorna-se as três tabelas
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 imposto
' o usuário digita três dados no teclado: casado nbEnfants salário
' o programa exibe 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 conexã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 ser feito?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' verificação do número de argumentos na linha inserida
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
O aplicativo é iniciado com um parâmetro:
- bdACCESS: nome do arquivo ACCESS a ser processado
O cálculo do imposto é feito por meio de um objeto do tipo [impot], criado assim que a aplicação é iniciada:
' recuperam-se os argumentos
Dim chemin As String = arguments(0)
' preparando a string de conexã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 conexão com a fonte OLEDB foi construída a partir das informações obtidas com [WebMatrix].
Após a inicialização, o aplicativo solicita repetidamente que o usuário digite no teclado as três informações necessárias para calcular seu imposto:
- seu estado civil: o para casado, n para solteiro
- o número de filhos
- 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 arquivo [impots.mdb] é colocado na pasta do aplicativo de teste, e este é executado da seguinte maneira:
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 executar o aplicativo com um arquivo 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 visualizações do aplicativo 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] precisa ser modificado. Ele é responsável por criar o objeto [impot] ao iniciar a aplicação. O construtor desse objeto tem como único parâmetro o objeto do tipo [impotsData], responsável por recuperar os dados. Portanto, esse parâmetro muda, uma vez que estamos alterando as fontes de dados. O controlador [global.asax.vb] passa a ser o seguinte:
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' criação de um objeto de imposto
Dim objImpot As impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
' inserimos o objeto no aplicativo
Application("objImpot") = objImpot
' sem erros
Application("erreur") = False
Catch ex As Exception
'ocorreu um erro; registra-se 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 conexão da fonte de dados OLEDB a ser utilizada. Ela está definida no arquivo de configuração [web.config] do aplicativo:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="chaineConnexion"
value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\serge\devel\aspnet\poly\chap5\impots2\impots.mdb" />
</appSettings>
</configuration>
O controlador [main.aspx] permanece inalterado.
6.3.7. Resumo das alterações
O aplicativo está pronto para ser testado. Vamos listar as alterações feitas em relação à versão anterior:
- foi criada uma nova classe de acesso aos dados
- o controlador [global.asax.vb] foi alterado em um ponto: criação do objeto [impot]
- foi adicionado um arquivo [web.config]
6.3.8. Teste da aplicação web
Todos os arquivos acima estão localizados na pasta <application-path>.

Nessa pasta, é criada uma subpasta [bin], na qual é colocado o conjunto [impot.dll], resultante da compilação dos arquivos das classes de negócios: [impots.vb, impotsData.vb, impotsArray.vb, impotsOLEDB.vb]. A seguir, relembramos 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 arquivo [impot.dll] gerado por esse comando deve ser colocado em <application-path>\bin para que o aplicativo web tenha acesso a ele. 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 nosso aplicativo em um aplicativo de simulação de cálculos de impostos. Um usuário poderá realizar cálculos sucessivos de impostos, e estes serão apresentados a ele em uma nova visualização semelhante a esta:

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

Aparece uma nova visualização [simulations.aspx], da qual acabamos de apresentar uma captura de tela. A classe de acesso aos dados será a classe [impotsODBC] do exemplo 2.
6.4.3. As visualizações do aplicativo web
A visualização [erreurs.aspx] não sofre alterações. A visualização [formulaire.aspx] sofre uma pequena alteração. De fato, o valor do imposto não aparece mais nessa visualização. Agora, ele está na visualização [simulations.aspx]. Assim, ao iniciar, a página exibida ao usuário é a seguinte:

Além disso, a visualização [formulaire] inclui um script JavaScript que verifica a validade dos dados inseridos antes de enviá-los ao servidor, conforme mostra o exemplo a seguir:

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 enviá-los ao 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
// tudo certo — o formulário é enviado ao 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 mesmos das versões anteriores. O campo dinâmico referente ao valor do imposto foi removido. O botão [Calculer] não é mais um botão do tipo [submit]. Ele é 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 poder 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 inseridos estiverem corretos, eles são enviados ao servidor pela função [document.frmImpots.submit()].
A página de apresentação obtém seus campos dinâmicos 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 ser exibida
' 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)
End Sub
End Class
O controlador [formulaire.aspx.vb] é idêntico às versões anteriores, exceto pelo fato de que não precisa mais recuperar o campo [txtImpot] do contexto, uma vez que esse campo foi removido da página.
A visualização [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 |
Eles são gerados pelo controlador [simulations.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Public Class simulations
Inherits System.Web.UI.Page
Protected simulationsHTML As String = ""
Protected href As String
Protected lien As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
'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
' recuperamos os demais 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 inseridas pelo controlador do aplicativo no contexto da página:
objeto ArrayList contendo a lista de simulações a serem exibidas. Cada elemento é uma matriz de 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]
Vamos relembrar o esquema MVC do nosso aplicativo:

O controlador [main.aspx] precisa processar três ações:
- init: corresponde à primeira solicitação do cliente. O controlador exibe a visualização [formulaire.aspx]
- calcul: corresponde à solicitação de cálculo do imposto. Se os dados do formulário de preenchimento estiverem corretos, o imposto é calculado por meio da classe de negócios [impotsODBC]. O controlador retorna ao cliente a visualização [simulations.aspx] com o resultado da simulação atual e de todas as anteriores. Se os dados do formulário de entrada estiverem incorretos, o controlador retorna a visualização [erreurs.aspx] com a lista de erros e um link para retornar ao formulário.
- retorno: corresponde ao retorno ao formulário após um erro. O controlador exibe a visualização [formulaire.aspx] tal como foi validada antes do erro.
Nesta nova versão, apenas a ação [calcul] foi alterada. De fato, se os dados forem válidos, ela deve levar à visualização [simulations.aspx], enquanto anteriormente levava à visualização [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 o aplicativo conseguiu inicializar corretamente
...
' executa-se a ação
Select Case action
Case "init"
' inicializar o aplicativo
initAppli()
Case "calcul"
' cálculo do imposto
calculImpot()
Case "retour"
' retorno ao formulário
retourFormulaire()
Case "effacer"
' inicialização do aplicativo
initAppli()
Case Else
' ação desconhecida = inicialização
initAppli()
End Select
End Sub
...
Private Sub calculImpot()
' salvando os dados inseridos
Session.Item("formulaire") = Request.Form
' verificando a validade dos dados inseridos
Dim erreurs As ArrayList = checkData()
' se houver erros, eles são sinalizados
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)
' colocando 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
Conservamos acima apenas o que era necessário para compreender as modificações que se encontram exclusivamente na função [calculImpots]:
- em primeiro lugar, a função salva o formulário [Request.Form] na sessão, a fim de poder regenerar o formulário no estado em que foi validado. Isso deve ser feito em todos os casos, pois, independentemente de a operação resultar na resposta [erreurs.aspx] ou na resposta [simulations.aspx], retorna-se ao formulário por meio do link [Retour au formulaire]. Para restaurá-lo corretamente, é necessário ter salvo seus valores anteriormente na sessão.
- Se os dados inseridos estiverem corretos, a função insere a simulação atual (casado, filhos, salário, imposto) na lista de simulações. Essa lista está na sessão associada à chave “simulações”.
- A lista de simulações é colocada de volta na sessão para uso futuro. Ela também é inserida no contexto atual, pois é lá que a visualização [simulations.aspx] a aguarda
- A visualização [simulations.aspx] é exibida assim que as demais informações que ela aguarda forem inseridas no contexto
6.4.5. Resumo das alterações
O aplicativo está pronto para ser testado. Vamos listar as alterações feitas em relação às versões anteriores:
- foi criada uma nova visualização
- o controlador [main.aspx.vb] foi alterado em um ponto: processamento da ação [calcul]
6.4.6. Teste da aplicação web
O leitor é convidado a realizar os testes. Lembramos o procedimento. Todos os arquivos da aplicação estão localizados em uma pasta <application-path>. Nessa pasta, é criada uma subpasta [bin], na qual está armazenado o assembly [impot.dll], resultante da compilação dos arquivos das classes de negócio: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb. O arquivo [impot.dll] gerado por esse comando deve ser colocado em <application-path>\bin para que o aplicativo web tenha acesso a ele. O servidor Cassini é iniciado com os parâmetros (<application-path>,/impots4).
6.5. Conclusion
Os exemplos anteriores demonstraram, em um caso concreto, os mecanismos comumente utilizados no desenvolvimento web. Utilizamos sistematicamente a arquitetura MVC por seu valor didático. Teríamos podido tratar esses mesmos exemplos de maneira diferente e talvez mais simples sem essa arquitetura. Mas ela traz 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 diversas maneiras. Aqui estão algumas delas:
- o usuário pode querer manter suas simulações ao longo do tempo. Ele faria simulações no dia D e poderia recuperá-las no dia D+3, por exemplo. Uma solução possível para esse problema é o uso de cookies. Sabemos que o token de sessão entre o servidor e um cliente é transportado por esse mecanismo. Também poderíamos utilizá-lo para transportar as simulações entre o cliente e o servidor.
- Ao mesmo tempo em que o servidor envia a página com o resultado das simulações, ele envia em seus cabeçalhos HTTP um cookie contendo uma sequência de caracteres que representa as simulações. Como essas simulações estão em um objeto [ArrayList], é necessário realizar uma transformação desse objeto para [String]. O servidor atribuiria ao cookie um prazo de validade, por exemplo, 30 dias.
- O navegador do cliente armazena os cookies recebidos em um arquivo e os reenvia sempre que faz uma solicitação ao servidor que os enviou, caso ainda estejam válidos (período de validade não ultrapassado). Para fins de simulação, o servidor receberá uma sequência de caracteres [String], que deverá ser transformada no objeto [ArrayList].
Os cookies são gerenciados por [Response.Cookies] ao serem enviados ao cliente e por [Request.Cookies] ao serem recebidos no servidor.
- O mecanismo descrito acima pode se tornar bastante pesado se houver um número significativo de simulações. Além disso, é comum que um usuário limpe periodicamente seus cookies, excluindo-os todos, mesmo que, por outro lado, autorize seu navegador a utilizá-los. Portanto, mais cedo ou mais tarde, perderemos o cookie das simulações. Pode-se, então, querer armazená-los no servidor em vez de no cliente, em um banco de dados, por exemplo. Para vincular as simulações a um usuário específico, o aplicativo poderia iniciar com uma fase de identificação que solicitasse um login e uma senha, ambos armazenados em um banco de dados ou em qualquer outro tipo de repositório de dados.
- Também poderíamos querer proteger o funcionamento do nosso aplicativo. Atualmente, ele parte de duas premissas:
- o usuário sempre passa pelo controlador [main.aspx]
- e, nesse caso, ele sempre utiliza as ações propostas na página que lhe foi enviada
O que acontece, por exemplo, se o usuário acessar diretamente a URL [http://localhost/impots4/formulaire.aspx]? Esse caso é improvável, já que o usuário não sabe da existência dessa URL. No entanto, ele deve ser previsto. Ele pode ser gerenciado pelo controlador de aplicativo [global.asax], que monitora todas as solicitações feitas ao aplicativo. Assim, ele pode verificar se o recurso solicitado é realmente [main.aspx].
Um caso mais provável é que um usuário não utilize as ações presentes na página que o servidor lhe enviou. Por exemplo, o que acontece se o usuário solicitar diretamente a URL [http://localhost/impots4/main.aspx?action=retour] sem antes preencher o formulário? Vamos tentar. Obtemos a seguinte resposta:

O servidor travou. Isso é normal. Para a ação [retour], o controlador espera encontrar na sessão um objeto [NameValueCollection] que represente os valores do formulário que ele deve exibir. Ele não os encontra. O mecanismo do controlador permite oferecer uma solução elegante para esse problema. Para cada solicitação, o controlador [main.aspx] pode verificar se a ação solicitada é, de fato, uma das ações da página enviada anteriormente ao usuário. É possível utilizar o seguinte mecanismo:
- o controlador, antes de enviar sua resposta ao cliente, armazena na sessão deste uma informação que identifica essa página
- ao receber uma nova solicitação do cliente, ele verifica se a ação solicitada pertence de fato à última página enviada a esse cliente
- as informações que relacionam as páginas e as ações autorizadas nessas páginas podem ser inseridas no arquivo de configuração [web.config] do aplicativo.
- A experiência mostra que os controladores de aplicativos têm uma ampla base comum e que é possível construir um controlador genérico, cuja especialização para um determinado aplicativo é feita por meio de um arquivo de configuração. Esse é o caminho seguido, por exemplo, pela ferramenta [Struts] na área de programação web em Java.



