6. Örnekler
Bu bölümde, daha önce ele alınan konuları bir dizi örnekle açıklamayı amaçlıyoruz.
6.1. Örnek 1
6.1.1. Sorun
Bu uygulama, bir kullanıcının vergisini hesaplamasına olanak sağlamalıdır. Yalnızca maaşını beyan edecek bir vergi mükellefinin basitleştirilmiş durumunu ele alıyoruz (2003 yılı gelirleri için 2004 rakamları):
- çalışanın pay sayısı şu şekilde hesaplanır: nbParts = nbEnfants/2 + 1 (bekar ise), evliyse nbEnfants/2+2 olarak hesaplanır; burada nbEnfants, çocuk sayısıdır.
- En az üç çocuğu varsa, yarım pay daha ekler
- vergilendirilebilir geliri R=0,72*S olarak hesaplanır; burada S, yıllık maaşıdır
- aile katsayısı QF = R/nbParts olarak hesaplanır
- Vergisi I hesaplanır. Aşağıdaki tabloyu ele alalım:
4262 | 0 | 0 |
8382 | 0,0683 | 291,09 |
14753 | 0,1914 | 1322,92 |
23888 | 0,2826 | 2668,39 |
38868 | 0,3738 | 4846,98 |
47932 | 0,4262 | 6883,66 |
0 | 0,4809 | 9505,54 |
Her satırda 3 alan vardır. I vergisini hesaplamak için, QF<=alan1 olan ilk satırı ararız. Örneğin, QF=5000 ise şu satır bulunur
I vergisi bu durumda 0,0683*R - 291,09*nbParts'e eşittir. Eğer QF, QF<=alan1 ilişkisini hiçbir zaman karşılamıyorsa, o zaman son satırdaki katsayılar kullanılır. Burada:
bu da I=0,4809*R - 9505,54*nbParts vergi tutarını verir.
6.1.2. Uygulamanın MVC yapısı
Uygulamanın MVC yapısı şu şekilde olacaktır:

Denetleyicinin rolü, [main.aspx] sayfası tarafından üstlenilecektir. Üç olası eylem olacaktır:
- init: müşterinin ilk isteğine karşılık gelir. Denetleyici, [formulaire.aspx] görünümünü görüntüler
- calcul: vergi hesaplama talebine karşılık gelir. Giriş formundaki veriler doğruysa, vergi [impots] iş sınıfı kullanılarak hesaplanır. Denetleyici, onaylanmış haliyle [formulaire.aspx] görünümünü, hesaplanan vergiyle birlikte müşteriye geri döndürür. Giriş formundaki veriler yanlışsa, denetleyici hata listesi ve forma geri dönme bağlantısı içeren [erreurs.aspx] görünümünü döndürür.
- geri dönüş: bir hata sonrasında forma geri dönülmesini sağlar. Denetleyici, hata öncesinde onaylanmış haliyle [formulaire.aspx] görünümünü görüntüler.
[main.aspx] denetleyicisi, vergi hesaplamasıyla ilgili hiçbir bilgiye sahip değildir. Yalnızca istemci-sunucu iletişimini yönetmek ve istemci tarafından talep edilen eylemleri yürütmekle görevlidir. [calcul] eylemi için, [impot] iş sınıfını kullanacaktır.
6.1.3. İş sınıfı
Vergi sınıfı şu şekilde tanımlanacaktır:
' ithal edilen ad alanları
Imports System
' sınıf
Namespace st.istia.univangers.fr
Public Class impot
Private limites(), coeffR(), coeffN() As Decimal
' yapıcı
Public Sub New(ByRef source As impotsData)
' vergi hesaplaması için gerekli veriler
' dış bir kaynaktan gelir [source]
' bu veriler alınır – bir istisna olabilir
Dim data() As Object = source.getData
limites = CType(data(0), Decimal())
coeffR = CType(data(1), Decimal())
coeffN = CType(data(2), Decimal())
End Sub
' vergi hesaplaması
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
' pay sayısının hesaplanması
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
' vergilendirilebilir gelir ve aile katsayısı hesaplaması
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' vergi hesaplaması
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
Bir vergi nesnesi, oluşturucusuna [impotsData] türünde bir veri kaynağı sağlayarak oluşturulur. Bu sınıf, vergi hesaplaması için gerekli olan ve daha önce sunulan üç veri tablosunu elde etmeyi sağlayan [getData] adlı bir genel yönteme sahiptir. Bu yöntem, veriler alınamadığında veya verilerin hatalı olduğu tespit edildiğinde bir istisna işleyebilir. [impot] nesnesi oluşturulduktan sonra, vergi mükellefinin medeni durumu (evli veya bekar), çocuk sayısı ve yıllık maaşına göre vergisini hesaplayan "calculer" yöntemini tekrar tekrar çağırabiliriz.
6.1.4. Veriye erişim sınıfı
[impotsData] sınıfı, verilere erişim sağlayan sınıftır. Bu, soyut bir sınıftır. Olası her yeni veri kaynağı (tablolar, düz dosyalar, veritabanları, konsol vb.) için türetilmiş bir sınıf oluşturulması gerekir. Tanımı şöyledir:
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
' verilere erişim yöntemi
Public MustOverride Function getData() As Object()
' veri doğrulama yöntemi
Protected Function checkData() As Integer
' alınan verileri doğrular
' veriler mevcut olmalıdır
valide = Not limites Is Nothing AndAlso Not coeffr Is Nothing AndAlso Not coeffn Is Nothing
If Not valide Then Return 1
' aynı boyutta 3 tablo bulunmalıdır
If valide Then valide = limites.Length = coeffr.Length AndAlso limites.Length = coeffn.Length
If Not valide Then Return 2
' tablolar boş olmamalıdır
valide = limites.Length <> 0
If Not valide Then Return 3
' her tablo >=0 değerinde elemanlar içermeli ve artan sırada olmalı
valide = check(limites, limites.Length - 1) AndAlso check(coeffr, coeffr.Length) AndAlso check(coeffn, coeffn.Length)
If Not valide Then Return 4
' her şey yolunda
Return 0
End Function
' bir dizinin içeriğinin geçerliliğini kontrol eder
Protected Function check(ByRef tableau() As Decimal, ByVal n As Integer) As Boolean
' dizinin ilk n elemanı >=0 olmalı ve kesinlikle artan sırada olmalıdır
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
' doğru
Return True
End Function
End Class
End Namespace
Sınıfın aşağıdaki korumalı öznitelikleri vardır:
vergi dilimlerinin sınırları tablosu | |
vergilendirilebilir gelire uygulanan katsayı tablosu | |
hisse sayısına uygulanan katsayı tablosu | |
verilerin (sınırlar, katsayı1, katsayı2) doğrulanıp doğrulanmadığını belirten boole değeri | |
verilerin (sınırlar, coeffr, coeffn) geçerli olup olmadığını belirten boole değeri |
Sınıfın oluşturucusu yoktur. Türetilmiş sınıfların uygulaması gereken [getData] adlı soyut bir yöntemi vardır. Bu yöntemin işlevi şudur:
- limites, coeffr ve coeffn adlı üç diziye değerler atamak
- veriler alınamadığında veya geçersiz olduğu tespit edildiğinde bir istisna oluşturmak.
Sınıf, özniteliklerin (limites, coeffr, coeffn) geçerliliğini kontrol eden korumalı [checkData] ve [check] yöntemlerini sağlar. Bu sayede türetilmiş sınıfların bu yöntemleri uygulaması gerekmez. Sadece bunları kullanmaları yeterlidir.
Kullanacağımız ilk türetilmiş sınıf şu şekilde olacaktır:
Imports System.Collections
Imports System
Namespace st.istia.univangers.fr
Public Class impotsArray
Inherits impotsData
' argüman içermeyen oluşturucu
Public Sub New()
' dizilerin sabitlerle başlatılması
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
' giriş olarak üç dizi alan oluşturucu
Public Sub New(ByRef limites() As Decimal, ByRef coeffr() As Decimal, ByRef coeffn() As Decimal)
' verileri saklıyoruz
Me.limites = limites
Me.coeffr = coeffr
Me.coeffn = coeffn
checked = False
End Sub
Public Overrides Function getData() As Object()
' gerekirse veriler kontrol edilir
Dim erreur As Integer
If Not checked Then erreur = checkData() : checked = True
' geçerli değilse, bir istisna atılır
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' aksi takdirde üç dizi döndürülür
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
[impotsArray] adlı bu sınıfın iki yapıcı işlevi vardır:
- argüman almayan ve temel sınıfın özniteliklerini (limites, coeffr, coeffn) "sabit" olarak kodlanmış dizilerle başlatan bir yapıcı
- temel sınıfın özniteliklerini (limites, coeffr, coeffn) kendisine parametre olarak geçirilen dizilerle başlatan bir oluşturucu
Dış sınıfların (limites, coeffr, coeffn) dizilerini almasını sağlayacak olan [getData] yöntemi, temel sınıfın [checkData] yöntemini kullanarak bu üç dizinin geçerliliğini kontrol etmekle yetinir. Veriler geçersizse bir istisna atar.
6.1.5. İş sınıfları ve veri erişim sınıflarının test edilmesi
Bir web uygulamasına yalnızca doğruluğu onaylanmış iş sınıfları ve veri erişim sınıflarının dahil edilmesi önemlidir. Böylelikle web uygulamasının hata ayıklama aşaması, denetleyici ve görünümler kısmına odaklanabilir. Bir test programı şu şekilde olabilir:
' seçenekler
Option Strict On
Option Explicit On
' ad alanları
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
Module test
Sub Main()
' etkileşimli vergi hesaplama programı
' kullanıcı klavyeden üç veri girer: evli nbEnfants maaş
' program daha sonra ödenecek vergiyi gösterir
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"
' bir vergi nesnesinin oluşturulması
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
' sonsuz döngü
Dim marié As String
Dim nbEnfants As Integer
Dim salaire As Long
While True
' vergi hesaplaması için parametreler isteniyor
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()
' Yapılması gereken bir şey var mı?
If paramètres Is Nothing OrElse paramètres = "" Then
Exit While
End If
' giriş satırındaki argüman sayısının kontrolü
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
' parametrelerin geçerliliğinin kontrolü
If Not erreur Then
' evli
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
' maaş
Try
salaire = Integer.Parse(args(2))
If salaire < 0 Then
Throw New Exception
End If
Catch
erreur = True
End Try
End If
' parametreler doğruysa - vergi hesaplanır
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
Uygulama, kullanıcıdan vergisini hesaplamak için gerekli olan üç bilgiyi klavyeyle girmesini ister:
- medeni durumu: o evli, n bekar
- çocuk sayısı
- yıllık maaşı
Vergi hesaplaması, uygulamanın başlatılmasıyla birlikte oluşturulan [impot] türündeki bir nesne yardımıyla yapılır:
' vergi nesnesi oluşturulur
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
Veri kaynağı olarak [impotsArray] türünde bir nesne kullanılır. Bu sınıfın argüman almayan oluşturucusu kullanılır ve bu oluşturucu, "sabit" değerlere sahip üç diziyi (limites, coeffr, coeffn) sağlar. Teorik olarak, bir [impot] nesnesinin oluşturulması bir istisna oluşturabilir; çünkü nesne, oluşturulurken parametre olarak kendisine aktarılan veri kaynağından (limites, coeffr, coeffn) verileri talep eder ve bu veri alımı bir istisna tetikleyebilir. Ancak burada, veri alma yöntemi (sabit kodlama) bir istisna oluşturamaz. Yine de, okuyucunun dikkatini [impot] nesnesinin hatalı bir şekilde oluşturulabileceği olasılığına çekmek amacıyla bu istisnanın yönetimini programda bıraktık.
İşte önceki programın çalıştırılmasına ilişkin bir örnek:
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
[impot, impotsData, impotsArray] sınıflarının tümünü [impot.dll] derlemesinde birleştiriyoruz:
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
Test programını derliyoruz:
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
Testleri yapabiliriz:
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. Web uygulamasının görünümleri
Uygulamanın iki görünümü olacaktır: [formulaire.aspx] ve [erreurs.aspx]. Uygulamanın işleyişini ekran görüntüleriyle açıklayalım. [formulaire.aspx] görünümü, [main.aspx] URL'si ilk kez istendiğinde görüntülenir:

Kullanıcı formu doldurur:

ve [Calculer] düğmesini kullanarak aşağıdaki yanıtı alır:

Girdiği verilerde hata yapabilir:

[Calculer] düğmesinin kullanılması ise başka bir yanıt olan [erreurs.aspx]'i verir:

Yukarıdaki [Retour au formulaire] bağlantısını kullanarak, hata oluşmadan önce onayladığı [formulaire.aspx] görünümüne geri dönebilir:

6.1.7. [formulaire.aspx] görünümü
[formulaire.aspx] sayfası şu şekilde olacaktır:
<%@ 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>
Bu sayfanın dinamik alanları şunlardır:
[oui] kutucuğunun işaretlenmesi gerekiyorsa "checked", aksi takdirde "" | |
Aynı durum [non] kutucuğu için de geçerlidir | |
[txtEnfants] giriş alanına girilecek değer | |
[txtSalaire] giriş alanına girilecek değer | |
[txtImpot] giriş alanına girilecek değer |
Sayfada iki form bulunur ve her birinde bir [submit] düğmesi vardır. [Calculer] düğmesi, aşağıdaki formdaki [submit] düğmesidir:
<form method="post" action="main.aspx?action=calcul">
...
<P>
<INPUT type="submit" value="Calculer">
</P>
</form>
Görüldüğü gibi, form parametreleri [action=calcul] ile denetleyiciye gönderilecektir. [Effacer] düğmesi, aşağıdaki formdaki [submit] düğmesidir:
<form method="post" action="main.aspx?action=effacer">
<INPUT type="submit" value="Effacer">
</form>
Görüldüğü gibi, form parametreleri [action=effacer] ile denetleyiciye gönderilecektir. Burada formun herhangi bir parametresi yoktur. Önemli olan yalnızca eylemdir.
[formulaire.aspx] alanları, [formulaire.aspx.vb] tarafından hesaplanır:
Imports System.Collections.Specialized
Public Class formulaire
Inherits System.Web.UI.Page
' sayfanın alanları
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
' bağlamdan önceki isteği alınır
Dim form As NameValueCollection = Context.Items("formulaire")
' görüntülenecek sayfa hazırlanır
' radyo düğmeleri
rdouichecked = ""
rdnonchecked = "checked"
If form("rdMarie").ToString = "oui" Then
rdouichecked = "checked"
rdnonchecked = ""
End If
' geri kalan kısım
txtEnfants = CType(form("txtEnfants"), String)
txtSalaire = CType(form("txtSalaire"), String)
txtImpot = CType(Context.Items("txtImpot"), String)
End Sub
End Class
[main.aspx] alanlarının hesaplanması, denetleyici tarafından sayfa bağlamına yerleştirilen iki bilgiye dayalı olarak yapılır:
- Context.Items("form"): NameValueCollection türünde bir sözlük olup, HTML ve [rdmarie,txtEnfants,txtSalaire] alanlarının değerlerini içerir
- Context.Items("txtImpot"): vergi tutarı
6.1.8. [erreurs.aspx] görünümü
[erreurs.aspx] görünümü, uygulamanın çalışma süresi boyunca meydana gelebilecek olası hataları gösterir. Sunum kodu şöyledir:
<%@ 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>
Sayfada üç dinamik alan bulunmaktadır:
bir hata listesinden alınan HTML kodu | |
bir bağlantının URL'si | |
bağlantı metni |
Bu alanlar, [erreurs.aspx.vb]'teki sayfanın denetleyici kısmı tarafından hesaplanır:
Imports System.Collections
Imports Microsoft.VisualBasic
Public Class erreurs
Inherits System.Web.UI.Page
' sayfa parametresi
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
' bağlamdaki öğeler alınır
Dim erreurs As ArrayList = CType(context.Items("erreurs"), ArrayList)
href = context.Items("href").ToString
lien = context.Items("lien").ToString
' HTML kodunu listeden oluşturuluyor
Dim i As Integer
For i = 0 To erreurs.Count - 1
erreursHTML += "<li> " + erreurs(i).ToString + "</li>" + ControlChars.CrLf
Next
End Sub
End Class
Sayfa denetleyicisi, uygulama denetleyicisi tarafından sayfa bağlamına yerleştirilen bilgileri alır:
görüntülenecek hata mesajlarının listesini içeren ArrayList nesnesi | |
bir bağlantının URL'si | |
bağlantının metni |
Artık uygulamanın kullanıcısına ne gösterildiğini bildiğimize göre, uygulamanın denetleyicisini yazmaya geçebiliriz.
6.1.9. [global.asax, main.aspx] denetleyicileri
Uygulamamızın şemasını hatırlayalım:

MüşteriUygulama mantığı
[main.aspx] denetleyicisi üç eylemi işlemek zorundadır:
- init: İstemcinin ilk isteğine karşılık gelir. Denetleyici, [formulaire.aspx] görünümünü görüntüler
- calcul: vergi hesaplama talebine karşılık gelir. Giriş formundaki veriler doğruysa, vergi [impots] iş sınıfı kullanılarak hesaplanır. Denetleyici, onaylanmış haliyle [formulaire.aspx] görünümünü, hesaplanan vergiyle birlikte müşteriye geri döndürür. Giriş formundaki veriler yanlışsa, denetleyici hata listesi ve forma geri dönme bağlantısı içeren [erreurs.aspx] görünümünü döndürür.
- geri dönüş: bir hata sonrasında forma geri dönülmesini sağlar. Denetleyici, hata öncesinde onaylanmış haliyle [formulaire.aspx] görünümünü görüntüler.
Ayrıca, uygulamaya gelen tüm isteklerin, varsa [global.asax] denetleyicisinden geçtiği bilinmektedir. Dolayısıyla, uygulamanın girişinde iki denetleyiciden oluşan bir zincir bulunmaktadır:
- [global.asax], mimari gereği ASP.NET aracılığıyla uygulamaya gelen tüm istekleri alır
- [main.aspx], geliştiricinin kararı gereği uygulamaya gelen tüm istekleri de alır
[main.aspx]'e duyulan ihtiyaç, yönetmemiz gereken bir oturum olacağından kaynaklanmaktadır. [global.asax]'in bu durumda denetleyici olarak uygun olmadığını görmüştük. Burada [global.asax]'ten tamamen vazgeçilebilir. Ancak, uygulamanın başlatılması sırasında kod çalıştırmak için bunu kullanacağız. Yukarıdaki MVC şeması, vergiyi hesaplamak için bir [impot] nesnesi oluşturmamız gerektiğini göstermektedir. Bunu birden fazla kez oluşturmaya gerek yoktur; bir kez oluşturmak yeterlidir. Bu nedenle, onu uygulamanın başlatılması sırasında, [global.asax] denetleyicisi tarafından yönetilen [Application_Start] olayı sırasında oluşturacağız. Bunun kodu şöyledir:
[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)
' bir impo nesnesi oluşturuluyor
Dim objImpot As impot
Try
objImpot = New impot(New impotsArray)
' nesneyi uygulamaya ekleniyor
Application("objImpot") = objImpot
' hata yok
Application("erreur") = False
Catch ex As Exception
'bir hata oluştu, bunu uygulamaya kaydediyoruz
Application("erreur") = True
End Try
End Sub
End Class
Oluşturulduktan sonra, [impot] türündeki nesne uygulamaya eklenir. Farklı müşterilerin farklı istekleri bu nesneyi buradan alacaktır. [impot] nesnesinin oluşturulması başarısız olabileceğinden, olası istisnayı yönetiriz ve [impot] nesnesinin oluşturulurken bir hata olup olmadığını belirtmek için uygulamaya bir [erreur] anahtarı yerleştiririz.
[main.aspx, main.aspx.vb] denetleyicisinin kodu şu şekilde olacaktır:
[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
' her şeyden önce, uygulamanın düzgün bir şekilde başlatılıp başlatılmadığını kontrol ediyoruz
If CType(Application("erreur"), Boolean) Then
' hata sayfasına yönlendirilir
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
' yapılması gereken eylemi alıyoruz
Dim action As String
If Request.QueryString("action") Is Nothing Then
action = "init"
Else
action = Request.QueryString("action").ToString.ToLower
End If
' eylemi gerçekleştiririz
Select Case action
Case "init"
' uygulamayı başlat
initAppli()
Case "calcul"
' vergi hesaplaması
calculImpot()
Case "retour"
' form sayfasına geri dön
retourFormulaire()
Case "effacer"
' uygulamayı başlat
initAppli()
Case Else
' bilinmeyen eylem = başlatma
initAppli()
End Select
End Sub
Private Sub initAppli()
' önceden doldurulmuş form görüntüleniyor
Context.Items("formulaire") = initForm()
Context.Items("txtImpot") = ""
Server.Transfer("formulaire.aspx", True)
End Sub
Private Function initForm() As NameValueCollection
' form başlatılıyor
Dim form As New NameValueCollection
form.Set("rdMarie", "non")
form.Set("txtEnfants", "")
form.Set("txtSalaire", "")
Return form
End Function
Private Sub calculImpot()
' girilen verilerin geçerliliği kontrol ediliyor
Dim erreurs As ArrayList = checkData()
' hata varsa, uyarı verilir
If erreurs.Count <> 0 Then
' girdiler kaydedilir
Session.Item("formulaire") = Request.Form
' hata sayfası hazırlanıyor
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
context.Items("erreurs") = erreurs
Server.Transfer("erreurs.aspx")
End If
' burada hata yok - vergi hesaplanır
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
Request.Form("rdMarie") = "oui", _
CType(Request.Form("txtEnfants"), Integer), _
CType(Request.Form("txtSalaire"), Long))
' sonuç sayfası görüntülenir
context.Items("txtImpot") = impot.ToString + " euro(s)"
context.Items("formulaire") = Request.Form
Server.Transfer("formulaire.aspx", True)
End Sub
Private Sub retourFormulaire()
' oturumdan alınan değerlerle form görüntüleniyor
Context.Items("formulaire") = Session.Item("formulaire")
Context.Items("txtImpot") = ""
Server.Transfer("formulaire.aspx", True)
End Sub
Private Function checkData() As ArrayList
' başlangıçta hata yok
Dim erreurs As New ArrayList
Dim erreur As Boolean = False
' "evli" radyo düğmesi
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
' çocuk sayısı
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
' maaş
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
' hata listesi görüntüleniyor
Return erreurs
End Function
End Class
Denetleyici, öncelikle uygulamanın doğru şekilde başlatıldığını kontrol eder:
' öncelikle, uygulamanın düzgün bir şekilde başlatılıp başlatılmadığını kontrol ediyoruz
If CType(Application("erreur"), Boolean) Then
' hata sayfasına yönlendirilir
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
Denetleyici, uygulamanın düzgün bir şekilde başlatılamadığını tespit ederse (hesaplama için gerekli olan [impot] nesnesi oluşturulamadı), uygun parametrelerle hata sayfasını görüntüler. Burada, uygulamanın tamamı kullanılamaz durumda olduğundan, forma geri dönüş bağlantısı yerleştirilmesine gerek yoktur. [Context.Items("erreurs")] nesnesine, [ArrayList] türünde genel bir hata mesajı yerleştirilir.
Denetleyici, uygulamanın çalışır durumda olduğunu tespit ederse, [action] parametresi aracılığıyla kendisinden yürütülmesi istenen eylemi analiz eder. Bu çalışma şekliyle şimdiye kadar pek çok kez karşılaştık. Her eylem türünün işlenmesi bir fonksiyona devredilir.
6.1.9.1. init ve effacer eylemleri
Bu iki eylem, boş giriş formunun görüntülenmesini sağlamalıdır. Hatırlatmak gerekirse, bu formun (bkz. görünümler) iki parametresi vardır:
- Context.Items("form"): [NameValueCollection] türünde bir sözlük olup, HTML ve [rdmarie,txtEnfants,txtSalaire] alanlarının değerlerini içerir
- Context.Items("txtImpot"): vergi değeri
[initAppli] işlevi, boş bir form görüntülemek üzere bu iki parametreyi başlatır.
6.1.9.2. Hesaplama eylemi
Bu eylem, forma girilen verilerden ödenecek vergiyi hesaplamalı ve formu, girilen değerlerle ve ayrıca hesaplanan vergi tutarıyla önceden doldurulmuş olarak geri göndermelidir. Bu işi üstlenen [calculImpot] işlevi, öncelikle form verilerinin doğru olup olmadığını kontrol eder:
- [rdMarie] alanı mevcut olmalı ve değeri [oui] veya [non] olmalıdır
- [txtEnfants] alanı mevcut olmalı ve >=0 olan bir tamsayı değerine sahip olmalıdır
- [txtSalaire] alanı mevcut olmalı ve >=0 olan bir tamsayı olmalıdır
Girilen veriler geçersiz çıkarsa, denetleyici, önceden bu görünümün beklediği değerleri bağlama ekledikten sonra [erreurs.aspx] görünümünü görüntüler:
- hata mesajları bir [ArrayList] nesnesine yerleştirilir; bu nesne daha sonra [Context.Items("erreurs")] bağlamına eklenir
- geri dönüş bağlantısının URL'si ve bu bağlantının metni de bağlama yerleştirilir.
Yanıtı müşteriye gönderecek olan [erreurs.aspx] sayfasına kontrolü devretmeden önce, formda (Request.Form) girilen değerler, "form" anahtarıyla ilişkilendirilerek oturuma yerleştirilir. Bu, daha sonraki bir istekte bu değerlerin geri alınmasını sağlayacaktır.
Burada, istemci tarafından gönderilen istekte [rdMarie, txtEnfants, txtSalaire] alanlarının mevcut olup olmadığını kontrol etmenin yararlı olup olmadığı sorusu akla gelebilir. İstemcimizin, bu alanları içeren [formulaire.aspx] görünümünü almış bir tarayıcı olduğundan eminseniz, bu gereksizdir. Ancak bundan asla emin olamayız. Biraz sonra, daha önce karşılaştığımız [curl] uygulamasının istemci olduğu bir örnek göstereceğiz. Uygulamayı, beklediği alanları göndermeden sorgulayacağız ve nasıl tepki vereceğini göreceğiz. Bu, daha önce defalarca belirtilmiş ve burada tekrar hatırlattığımız bir kuraldır: Bir uygulama, kendisine sorgu gönderen istemcinin türü hakkında asla varsayımda bulunmamalıdır. Güvenlik açısından, kendisine beklenmedik parametre dizeleri gönderebilecek programlanmış bir uygulama tarafından sorgulanabileceğini göz önünde bulundurmalıdır. Her durumda doğru şekilde davranmalıdır.
Bizim örneğimizde, [rdMarie, txtEnfants, txtSalaire] alanlarının istekte mevcut olup olmadığını kontrol ettik, ancak isteğin başka alanlar da içerebileceğini kontrol etmedik. Bu uygulamada, bu tür alanlar göz ardı edilirdi. Bununla birlikte, yine güvenlik önlemi olarak, bu tür istekleri bir günlük dosyasına kaydetmek ve uygulama yöneticisine bir uyarı göndermek faydalı olacaktır; böylece yönetici, uygulamanın "tuhaf" istekler aldığını bilebilir. Günlük dosyasında bu istekleri analiz ederek, uygulamaya yönelik olası bir saldırıyı tespit edebilir ve uygulamayı korumak için gerekli önlemleri alabilir.
Beklenen veriler doğruysa, denetleyici, uygulamada depolanan [impot] nesnesiyle vergi hesaplamasını başlatır. Ardından, [formulaire.aspx] görünümü tarafından beklenen iki bilgiyi bağlama kaydeder:
- Context.Items("form"): [NameValueCollection] türünde bir sözlük; bu sözlük, HTML, [rdmarie,txtEnfants,txtSalaire], burada [Request.Form)] ve c.a.d alanlarının değerlerini içerir. formda daha önce girilen değerler
- Context.Items("txtImpot"): az önce elde edilen vergi değeri
Dikkatli bir okuyucu, yukarıdakileri okurken şu soruyu sormuş olabilir: Uygulamanın başlatılması sırasında oluşturulan [impot] nesnesi tüm istekler arasında paylaşıldığına göre, [impot] nesnesinin verilerinin bozulmasına yol açacak erişim çakışmaları yaşanamaz mı? Bu soruyu yanıtlamak için [impot] sınıfının koduna geri dönmemiz gerekiyor. İstekler, ödenecek vergiyi elde etmek için [impot].calculerImpot yöntemini çağırıyor. Dolayısıyla incelememiz gereken kod şudur:
Public Function calculer(ByVal marié As Boolean, ByVal nbEnfants As Integer, ByVal salaire As Long) As Long
' pay sayısının hesaplanması
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
' Vergilendirilebilir gelir ve aile katsayısı hesaplanır
Dim revenu As Decimal = 0.72D * salaire
Dim QF As Decimal = revenu / nbParts
' vergi hesaplaması
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
Bir iş parçacığının önceki yöntemi yürütmekte olduğunu ve kesintiye uğradığını varsayalım. Ardından başka bir iş parçacığı bu yöntemi yürütür. Riskler nelerdir? Bunu öğrenmek için aşağıdaki kodu ekledik:
Dim impot As Long = CLng(revenu * coeffR(i) - nbParts * coeffN(i))
' 10 saniye bekleniyor
Thread.Sleep(10000)
Return impot
İş parçacığı 1, yerel değişken [impot]'in değerini [impot1] olarak hesapladıktan sonra kesilir. Ardından iş parçacığı 2 çalışmaya başlar ve aynı [impot] değişkeni için [impot2] değerini hesapladıktan sonra kesilir. İş parçacığı 1 tekrar kontrolü ele alır. Yerel [impot] değişkeninde ne bulur? Bu değişken, bir yönteme özgü yerel bir değişken olduğu için, yığın adı verilen bir bellek yapısında saklanır. Bu yığın, iş parçacığının bağlamının bir parçasıdır ve iş parçacığı kesintiye uğradığında bu bağlam yedeklenir. İş parçacığı 2 devreye girdiğinde, bağlamı yeni bir yığınla ve dolayısıyla yeni bir yerel değişken olan [impot] ile kurulur. 2 numaralı iş parçacığı da kesintiye uğradığında, bağlamı da aynı şekilde kaydedilir. 1 numaralı iş parçacığı yeniden başlatıldığında, yığını da dahil olmak üzere bağlamı geri yüklenir. Böylece, 2 numaralı iş parçacığının değişkeni yerine kendi yerel değişkeni olan [impot]'i bulur. Dolayısıyla, istekler arasında erişim çakışması olmayan bir durum söz konusudur. Yukarıda belirtilen 10 saniyelik duraklama süresiyle yapılan testler, eşzamanlı isteklerin beklenen sonucu verdiğini doğrulamıştır.
6.1.9.3. Geri dönüş eylemi
Bu eylem, [erreurs.aspx] görünümündeki [Retour vers le formulaire] bağlantısının etkinleştirilmesine karşılık gelir; böylece, daha önce girilen ve oturumda kaydedilen değerlerle önceden doldurulmuş [formulaire.aspx] görünümüne geri dönülür. [retourFormulaire] işlevi bu bilgileri alır. [formulaire.aspx] görünümünün beklediği iki parametre şu şekilde başlatılır:
- Context.Items("form") parametresi, daha önce girilen ve oturumda kaydedilen değerlerle
- Context.Items("txtImpot") boş dize ile
6.1.10. Web uygulamasının test edilmesi
Yukarıdaki tüm dosyalar <application-path> klasörüne yerleştirilir.

Bu klasörün içinde, [bin] adlı bir alt klasör oluşturulur ve bu klasöre, iş sınıfı dosyalarının derlenmesi sonucu elde edilen [impot.dll] derleme dosyası yerleştirilir: [impots.vb, impotsData.vb, impotsArray.vb]. Gerekli derleme komutu aşağıda tekrar verilmiştir:
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
Yukarıdaki [impot.dll] dosyası, web uygulamasının bu dosyaya erişebilmesi için <application-path>\bin dizinine yerleştirilmelidir. Cassini sunucusu (<application-path>,/impots1) parametreleriyle başlatılır. Bir tarayıcı kullanarak [http://localhost/impots1/main.aspx] URL'sini çağırıyoruz:

Formu dolduruyoruz:

Ardından [Calculer] düğmesiyle vergi hesaplamasını başlatıyoruz. Aşağıdaki yanıtı alıyoruz:

Ardından hatalı girdiler yapıyoruz:

[Calculer] düğmesine basıldığında şu sonuç ortaya çıkar:

[Retour au formulaire] bağlantısını kullandığımızda, form onaylandığı andaki haline geri dönüyoruz:

Son olarak, [Effacer] düğmesi kullanıldığında sayfa sıfırlanır:

6.1.11. [curl] istemcisinin kullanımı
Web uygulamalarını tarayıcılar dışındaki diğer istemcilerle test etmek önemlidir. Bir tarayıcıya, onaylandığında gönderilecek parametreler içeren bir form gönderilirse, tarayıcı bu parametrelerin değerlerini sunucuya geri gönderir. Başka bir istemci bunu yapmayabilir ve bu durumda sunucu, bazı parametrelerin eksik olduğu bir istek alır. Sunucu, bu durumda ne yapması gerektiğini bilmelidir. Bir başka örnek ise istemci tarafında yapılan giriş doğrulamalarıdır. Formda doğrulanması gereken veriler varsa, bu doğrulama, formu içeren belgeye eklenmiş komut dosyaları sayesinde istemci tarafında yapılabilir. Tarayıcı, yalnızca istemci tarafında doğrulanmış tüm veriler geçerliyse formu gönderir. Bu durumda, sunucu tarafında, doğrulanmış veriler alacağımızı varsaymak ve bu doğrulamayı ikinci kez yapmak istememek cazip gelebilir. Ancak bu bir hata olur. Zira, tarayıcı dışındaki bir istemci sunucuya geçersiz veriler gönderebilir ve bu durumda web uygulamasının beklenmedik bir şekilde davranma riski ortaya çıkar. Bu noktaları [curl] istemcisini kullanarak açıklayacağız.
Öncelikle, [http://localhost/impots1/main.aspx] URL’sini çağırıyoruz:
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>
Sunucu bize formun HTML kodunu gönderdi. HTTP başlıklarında oturum çerezi bulunuyor. Oturumu sürdürmek için bunu sonraki isteklerde kullanacağız. Herhangi bir parametre belirtmeden [calcul] eylemini talep edelim:
dos>curl --cookie ASP.NET_SessionId=ivthkl45tjdjrzznevqsf255 --include --url http://localhost/impots1/main.aspx?action=hesaplama
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>
Gördüğümüz gibi, web uygulaması eksik olan üç parametre için üç hata mesajı içeren [erreurs] görünümünü geri gönderdi. Şimdi hatalı parametreler gönderelim:
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>
Üç hata da doğru bir şekilde tespit edildi. Şimdi geçerli parametreler gönderelim:
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>
Ödenecek vergiyi doğru bir şekilde hesapladık: 4300 avro. Bu örnekten çıkaracağımız ders şudur: Web tarayıcıları olan müşterilere yönelik bir web uygulaması yazıyor olmamız, bizi yanıltmamalıdır. Bir web uygulaması bir TCP/IP hizmetidir ve bu ağ protokolü, bir hizmetin istemci uygulamasının niteliğini belirlemeye izin vermez. Dolayısıyla, bir web uygulamasının istemcisinin bir web tarayıcısı olup olmadığını bilemeyiz. Bu nedenle iki kuralı izleriz:
- bir istemciden istek alındığında, istemci hakkında hiçbir varsayımda bulunulmaz ve istekte beklenen parametrelerin mevcut ve geçerli olup olmadığı kontrol edilir
- tarayıcılara yönelik bir yanıt hazırlanır; bu nedenle genellikle HTML belgeleri kullanılır
Bir web uygulaması, örneğin tarayıcılar ve cep telefonları gibi farklı istemcilere aynı anda hizmet verecek şekilde geliştirilebilir. Bu durumda, her isteğe istemcinin türünü belirten yeni bir parametre eklenebilir. Böylece bir tarayıcı, http://machine/impots/ adresine bir istek göndererek vergi hesaplamasını talep edermain.aspx?client=navigateur&action=calcul adresine bir istek göndererek vergi hesaplamasını talep ederken, cep telefonu ise http://machine/impots/main.aspx?client=mobile&action=calcul adresine bir istek gönderecektir. MVC yapısı, bu tür bir uygulamanın yazılmasını kolaylaştırır. Uygulama şu şekilde olur:

[Classes métier, Classes d'accès aux données] bloğu değişmez. Bu, aslında müşteri açısından önemsiz bir kısımdır. [Contrôleur] bloğu çok az değişir, ancak istekte yeni bir parametreyi hesaba katmalıdır; bu parametre, hangi tür bir müşteriyle karşı karşıya olduğunu belirten [client] parametresidir. [vues] bloğu, her müşteri türü için görünümler oluşturmalıdır. Kısa veya orta vadede hedefimiz yalnızca tarayıcılar olsa bile, uygulamanın tasarım aşamasından itibaren istekte [client] parametresinin varlığını dikkate almak faydalı olabilir. Uygulamanın ileride yeni bir istemci türünü yönetmesi gerekirse, yalnızca bu türe uygun görünümler yazılması yeterlidir.
6.2. Örnek 2
6.2.1. Sorun
Burada, önceki örnekle aynı sorunu ele alacağız, ancak web uygulaması tarafından oluşturulan [impot] nesnesinin veri kaynağını değiştireceğiz. Önceki sürümde, kullanılan veri kaynağı kodda "sabit" olarak yazılmış tablo değerlerini sağlıyordu. Bu sefer ise yeni veri kaynağı, MySQL veritabanına bağlı ODBC veri kaynağından bu değerleri alacaktır.
6.2.2. ODBC veri kaynağı
Veriler, [dbimpots] adlı bir veritabanındaki MySQL adlı veritabanında bulunan [IMPOTS] adlı bir tabloda yer alacaktır. Bu tablonun içeriği şu şekilde olacaktır:

Veritabanının sahibi, şifresi [mdpimpots] olan [admimpots] kullanıcısıdır. Bu veritabanına ODBC adlı bir veri kaynağı bağlayacağız. Bunu yapmadan önce, .NET platformu ile bir veritabanına erişmenin farklı yollarını hatırlayalım.
Windows platformları için çok sayıda veritabanı bulunmaktadır. Bu veritabanlarına erişmek için uygulamalar, sürücü (driver) adı verilen programlar aracılığıyla çalışır.

Yukarıdaki şemada, sürücünün iki arayüzü bulunmaktadır:
- uygulamaya sunulan I1 arayüzü
- veritabanına yönelik I2 arayüzü
B1 veritabanı için yazılmış bir uygulamanın, farklı bir B2 veritabanına geçiş yapıldığında yeniden yazılması gerekmemesi için, I1 arayüzünde bir standardizasyon çalışması yapılmıştır. "Standartlaştırılmış" sürücüler kullanan veritabanları kullanılıyorsa, B1 veritabanı bir P1 sürücüsüyle, B2 veritabanı ise P2 sürücüsüyle birlikte sağlanacaktır ve bu iki sürücünün I1 arayüzü aynı olacaktır. Böylece uygulamayı yeniden yazmaya gerek kalmayacaktır. Böylece, örneğin, uygulamada herhangi bir değişiklik yapmadan bir ACCESS veritabanını bir MySQL veritabanına taşıyabiliriz.
İki tür standartlaştırılmış sürücü bulunmaktadır:
- ODBC sürücüleri (Open DataBase Connectivity)
- OLE ve DB sürücüleri (Object Linking and Embedding DataBase)
ODBC sürücüleri, veritabanlarına erişim sağlar. OLE ve DB sürücüleri için veri kaynakları daha çeşitlidir: veritabanları, e-posta sistemleri, adres defterleri vb. Herhangi bir sınırlama yoktur. Bir yazılım geliştiricisi karar verirse, herhangi bir veri kaynağı bir Ole DB sürücüsünün konusu olabilir. Bunun faydası elbette çok büyüktür: çok çeşitli verilere tek tip bir erişim sağlanır.
.NET 1.1 platformu, üç tür veri erişim sınıfıyla birlikte sunulur:
- Microsoft SQL Server veritabanlarına erişim için SQL ve Server.NET sınıfları
- Db.NET Ole sınıfları, SGBD sürücüsü sunan OLE ve DB veritabanlarına erişim için
- odbc.net sınıfları, SGBD veritabanlarına erişmek için kullanılır; bu veritabanları ODBC sürücüsünü sunar
SGBD ve MySQL sınıfları, uzun süredir ODBC sürücüsüne sahiptir. Şu anda kullandığımız sürücü budur. Windows'ta [Menu Démarrer/Panneau de configuration/Outils d'administration/Sources ODBC 32 bits] seçeneğini seçiyoruz. Windows sürümüne göre bu yol biraz farklılık gösterebilir. ODBC kaynağımızı oluşturmamızı sağlayacak aşağıdaki uygulamayı elde ederiz:

Bir Sistem veri kaynağı, c.a.d oluşturacağız. Bu, bilgisayardaki tüm kullanıcıların erişebileceği bir veri kaynağı olacaktır. Yukarıdaki utiliser.Aussi sekmesini seçelim. Görüntülenen sayfada, yeni bir veri kaynağı oluşturmak için kullanacağımız [Ajouter] düğmesi bulunmaktadır:

Sihirbaz, kullanılacak ODBC sürücüsünü seçmenizi ister. Windows, önceden yüklenmiş bir dizi ODBC sürücüsü ile birlikte gelir. ODBC sürücüsü, MySQL sürücüsü pakete dahil değildir. Bu nedenle, önceden bu sürücüyü yüklemeniz gerekir. İnternette bir arama motoruna "MySQL ODBC" veya "MyODBC" anahtar kelime dizisini yazarak bu sürücüyü bulabilirsiniz. Burada, [MySQL ODBC 3.51] sürücüsünü yükledik. Bunu seçip [Terminer]'i yapıyoruz:

Birkaç bilgi girilmesi gerekiyor:
ODBC veri kaynağını tanımlayacak ad. Herhangi bir Windows uygulaması bu ad üzerinden kaynağa erişebilir | |
Veri kaynağını tanımlayan rastgele bir metin | |
SGBD MySQL'i barındıran makinenin adı. Burada söz konusu olan yerel bilgisayardır. Uzak bir bilgisayar da olabilir. Bu, bir Windows uygulamasının herhangi bir özel kodlama gerektirmeden uzak bir veritabanına erişmesini sağlar. Bu, ODBC kaynağının en önemli avantajlarından biridir. | |
Bir SGBD MySQL, birden fazla veritabanını yönetebilir. Burada hangi veritabanını yönetmek istediğimizi belirtiriz: dbimpots | |
SGBD ve MySQL içinde bildirilen bir kullanıcı adı. Veri kaynağına erişimler bu kullanıcı adı altında gerçekleştirilecektir. Burada: admimpots | |
Bu kullanıcının şifresi. Burada: mdpimpots | |
SGBD MySQL'in çalışma bağlantı noktası. Varsayılan olarak 3306 numaralı bağlantı noktasıdır. Bunu değiştirmedik |
Bu işlem tamamlandıktan sonra, [Test Data Source] düğmesiyle bağlantı ayarlarımızın geçerliliğini test ediyoruz:

Bu işlem tamamlandıktan sonra, ODBC veri kaynağımızın doğru olduğundan emin oluruz. Artık bu kaynağı kullanabiliriz. Sihirbazdan çıkmak için ODBC'i gerekli olduğu kadar çok kez tekrarlıyoruz.
Okuyucunun elinde SGBD mySQL yoksa, bunu [http://www.mysql.com] adresinden ücretsiz olarak edinebilir. Aşağıda, Access ile bir ODBC kaynağı oluşturma adımlarını sunuyoruz. İlk adımlar daha önce açıklananlarla aynıdır. Yeni bir sistem veri kaynağı eklenir:

Seçilen sürücü [Microsoft Access Driver] olacaktır. [Terminer] komutunu vererek ODBC kaynağının tanımına geçin:

Girilmesi gereken bilgiler şunlardır:
ODBC veri kaynağını tanımlayacak ad. Herhangi bir Windows uygulaması bu ad üzerinden kaynağa erişebilir | |
Veri kaynağını açıklayan rastgele bir metin | |
Kullanılacak ACCESS dosyasının tam adı |
6.2.3. Yeni bir veri erişim sınıfı
Uygulamamızdaki MVC yapısına geri dönelim:

Yukarıdaki şemada, [impotsData] sınıfı verileri almaktan sorumludur. Bunu burada MySQL ve [dbimpots] veritabanlarından yapmalıdır. Bu uygulamanın önceki sürümünden bildiğimiz üzere, [impotsData], yeni bir veri kaynağına uyarlamak istediğimizde her seferinde türetilmesi gereken soyut bir sınıftır. Bu soyut sınıfın yapısını hatırlayalım:
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
' verilere erişim yöntemi
Public MustOverride Function getData() As Object()
' veri doğrulama yöntemi
Protected Function checkData() As Integer
' toplanan verileri doğrular
...
End Function
' bir tablonun içeriğinin geçerliliğini doğrular
Protected Function check(ByRef tableau() As Decimal, ByVal n As Integer) As Boolean
...
End Function
End Class
End Namespace
[impotsData] sınıfından türetilen sınıf, iki yöntemi uygulamalıdır:
- [impotsData] sınıfının argüman almayan yapıcı yöntemi kendisine uygun değilse bir yapıcı yöntem
- üç diziyi (limites, coeffr, coeffn) döndüren [getData] yöntemi
Verileri (limites, coeffr, coeffn) ODBC adlı bir kaynaktan alacak olan [impotsODBC] sınıfını oluşturuyoruz:
Imports System.Data.Odbc
Imports System.Data
Imports System.Collections
Imports System
Namespace st.istia.univangers.fr
Public Class impotsODBC
Inherits impotsData
' örnek değişkenleri
Protected DSNimpots As String
' oluşturucu
Public Sub New(ByVal DSNimpots As String)
' üç bilgiyi not eder
Me.DSNimpots = DSNimpots
End Sub
Public Overrides Function getdata() As Object()
' tablonun içeriğinden üç tabloyu (limites, coeffr, coeffn) başlatır
' ODBC ve DSNimpots veritabanlarındaki [impots] tablosunun içeriğinden
' sınırlar, coeffr ve coeffn bu tablonun üç sütunudur
' çeşitli istisnalar oluşturabilir
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
' SELECT sorgusu
Dim selectCommand As String = "select limites,coeffr,coeffn from impots"
' verileri almak için tablolar
Dim aLimites As New ArrayList
Dim aCoeffR As New ArrayList
Dim aCoeffN As New ArrayList
Try
' veritabanına erişilmeye çalışılıyor
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
' bir komut nesnesi oluşturuluyor
sqlCommand = New OdbcCommand(selectCommand, impotsConn)
' sorgu yürütülüyor
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
' Alınan tablonun işlenmesi
While myReader.Read()
' geçerli satırın verileri tablolara aktarılıyor
aLimites.Add(myReader("limites"))
aCoeffR.Add(myReader("coeffr"))
aCoeffN.Add(myReader("coeffn"))
End While
' Kaynaklar serbest bırakılır
myReader.Close()
impotsConn.Close()
Catch e As Exception
Throw New Exception("Erreur d'accès à la base de données (" + e.Message + ")")
End Try
' Dinamik tablolar statik tablolara aktarılır
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
' Alınan veriler kontrol edilir
Dim erreur As Integer = checkData()
' Veriler geçerli değilse, bir istisna oluşturulur
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' aksi takdirde üç tablo döndürülür
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Üreticiye bir göz atalım:
' yapıcı
Public Sub New(ByVal DSNimpots As String)
' üç bilgiyi kaydediyoruz
Me.DSNimpots = DSNimpots
End Sub
Bu yöntem, alınacak verilerin bulunduğu ODBC kaynağının adını parametre olarak alır. Oluşturucu, bu adı sadece hafızaya alır. [getData] yöntemi, [impots] tablosundaki verileri okumak ve bunları üç diziye (limites, coeffr, coeffn) yerleştirmekle görevlidir. Kodunu inceleyelim:
- ODBC veri kaynağına bağlantı parametreleri tanımlanmıştır, ancak veri kaynağı açılmamıştır
' veritabanına bağlantı dizesi
Dim connectString As String = "DSN=" + DSNimpots + ";"
' veritabanına bir bağlantı nesnesi oluşturulur - bu bağlantı açık değildir
Dim impotsConn As OdbcConnection = New OdbcConnection(connectString)
- [impots] tablosundaki verileri almak için üç adet [ArrayList] nesnesi tanımlanmıştır:
' verileri almak için tablolar
Dim aLimites As New ArrayList
Dim aCoeffR As New ArrayList
Dim aCoeffN As New ArrayList
- Veritabanına erişim kodunun tamamı, olası erişim hatalarını yönetmek için bir try/catch bloğu içine alınmıştır. Veritabanına bağlantı kurulur:
' veritabanına erişilmeye çalışılıyor
impotsConn = New OdbcConnection(connectString)
impotsConn.Open()
- Açık bağlantı üzerinde [select] komutunu çalıştırıyoruz. Select sorgusunun sonuç tablosundaki satırları taramamıza olanak sağlayacak bir [OdbcDataReader] nesnesi elde ediyoruz:
' bir komut nesnesi oluşturulur
Dim sqlCommand As OdbcCommand = New OdbcCommand(selectCommand, impotsConn)
' sorgu çalıştırılıyor
Dim myReader As OdbcDataReader = sqlCommand.ExecuteReader()
- Sonuç tablosunu satır satır tarıyoruz. Bunun için, daha önce elde ettiğimiz [OdbcDataReader] nesnesinin [Read] yöntemini kullanıyoruz. Bu yöntem iki şey yapar:
- tabloda bir satır ilerler. Başlangıçta, 1. satırın öncesinde konumlanıyoruz
- ilerleyebildiyse [true] boole değerini döndürür, aksi takdirde [false] değerini döndürür; bu son durum, tüm satırların işlendiğini gösterir.
[OdbcDataReader] nesnesinin geçerli satırındaki sütunlar, OdbcDataReader ile elde edilir. Sütunun değerini temsil eden bir nesne elde edilir. Tablonun tamamını tarayarak içeriğini üç [ArrayList] nesnesine aktarırız:
' Alınan tablonun işlenmesi
While myReader.Read()
' geçerli satırın verileri tablolara aktarılır
aLimites.Add(myReader("limites"))
aCoeffR.Add(myReader("coeffr"))
aCoeffN.Add(myReader("coeffn"))
- Bu işlem tamamlandıktan sonra, bağlantıyla ilişkili kaynakları serbest bırakıyoruz:
- Üç [ArrayList] nesnesinin içeriği, üç klasik diziye aktarılır:
' Dinamik tablolar statik tablolara aktarılır
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
- [impots] tablosundaki veriler üç tabloya aktarıldıktan sonra, geriye sadece [impotsData] temel sınıfının [checkData] yöntemini kullanarak bu tabloların içeriğini kontrol etmek kalır:
' Alınan veriler kontrol edilir
Dim erreur As Integer = checkData()
' veriler geçerli değilse, bir istisna oluşturulur
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' aksi takdirde üç tablo döndürülür
Return New Object() {limites, coeffr, coeffn}
6.2.4. Veri erişim sınıfı testleri
Bir test programı şu şekilde olabilir:
Option Explicit On
Option Strict On
' ad alanları
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' test sayfası
Module testimpots
Sub Main(ByVal arguments() As String)
' etkileşimli vergi hesaplama programı
' kullanıcı klavyeden üç veri girer: evli nbEnfants maaş
' program daha sonra ödenecek vergiyi gösterir
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"
' program parametrelerinin kontrolü
If arguments.Length <> 1 Then
' hata mesajı
Console.Error.WriteLine(syntaxe1)
' son
Environment.Exit(1)
End If
' argümanlar alınır
Dim DSNimpots As String = arguments(0)
' vergi nesnesi oluşturuluyor
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
' sonsuz döngü
While True
' başlangıçta hata yok
Dim erreur As Boolean = False
' vergi hesaplaması için parametreler isteniyor
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()
' ne yapılmalı?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' giriş satırındaki argüman sayısının kontrolü
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
' parametrelerin geçerliliğinin kontrolü
' evli
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
' maaş
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
' parametreler doğru - vergi hesaplanıyor
Console.Out.WriteLine(("impôt=" & objImpot.calculer(marié = "o", nbEnfants, salaire).ToString + " euro(s)"))
End If
End While
End Sub
End Module
End Namespace
Uygulama bir parametre ile başlatılır:
- DSNimpots: kullanılacak ODBC veri kaynağının adı
Vergi hesaplaması, uygulamanın başlatılmasıyla birlikte oluşturulan [impot] türündeki bir nesne kullanılarak yapılır:
' vergi nesnesi oluşturuluyor
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
Uygulama başlatıldıktan sonra, kullanıcıdan vergisini hesaplamak için gerekli olan üç bilgiyi klavyeyle girmesini tekrar tekrar ister:
- medeni durumu: o evli, n bekar
- çocuk sayısı
- yıllık maaşı
Tüm sınıflar derlenir:
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
Test programı da sırayla derlenir:
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
Test programı ilk olarak ODBC ve MySQL veri kaynaklarıyla çalıştırılır:
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)
Kaynağı ODBC olarak değiştirip bir Access kaynağı kullanıyoruz:
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. Web uygulamasının görünümleri
Bunlar önceki uygulamaya ait görünümlerdir: formulaire.aspx ve erreurs.aspx
6.2.6. [global.asax, main.aspx] uygulama denetleyicileri
Yalnızca [global.asax] denetleyicisi değiştirilmelidir. Bu denetleyici, uygulamanın başlatılması sırasında [impot] nesnesini oluşturmaktan sorumludur. Bu nesnenin oluşturucusunun tek parametresi, verileri almaktan sorumlu [impotsData] türündeki nesnedir. Dolayısıyla bu parametre, her yeni veri kaynağı türü için değişir. [global.asax.vb] denetleyicisi şu şekilde olur:
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)
' bir vergi nesnesi oluşturuluyor
Dim objImpot As impot
Try
objImpot = New impot(New impotsODBC(ConfigurationSettings.AppSettings("DSNimpots")))
' nesne uygulamaya ekleniyor
Application("objImpot") = objImpot
' hata yok
Application("erreur") = False
Catch ex As Exception
'hata oluştu, uygulamaya not edilir
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
[impot] nesnesinin veri kaynağı artık bir [impotODBC] nesnesidir. Bu nesnenin parametresi, kullanılacak ODBC veri kaynağının adı olan DSN'tir. Bu adı koda sabit olarak yazmak yerine, uygulamanın [web.config] yapılandırma dosyasına eklenir:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="DSNimpots" value="odbc-mysql-dbimpots" />
</appSettings>
</configuration>
[web.config] dosyasının <appSettings> bölümündeki bir C anahtarının değeri, uygulama kodunda [ConfigurationSettings.AppSettings(C)] ile elde edildiği bilinmektedir.
İstisnanın nedenini belirlemek için, istisna mesajı uygulamada kaydedilir ve böylece sorgular için kullanılabilir durumda kalır. [main.aspx.vb] denetleyicisi bu mesajı hata listesine ekleyecektir:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' her şeyden önce, uygulamanın düzgün bir şekilde başlatılıp başlatılmadığını kontrol ediyoruz
If CType(Application("erreur"), Boolean) Then
' hata sayfasına yönlendirilir
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
' yapılması gereken eylemi alıyoruz
...
6.2.7. Değişikliklerin özeti
Uygulama test edilmeye hazırdır. Önceki sürüme yapılan değişiklikleri sıralayalım:
- yeni bir veri erişim sınıfı oluşturuldu
- [global.asax.vb] denetleyicisi iki yerde değiştirildi: [impot] nesnesinin oluşturulması ve olası istisna ile ilgili mesajın uygulamaya kaydedilmesi
- [main.aspx.vb] denetleyicisi, önceki istisna mesajını görüntülemek üzere bir noktada değiştirildi
- [web.config] dosyası eklenmiştir
Değişiklik çalışmaları esas olarak web uygulamasının dışında, c.a.d dosyasında gerçekleştirilmiştir. Bu, denetleyiciyi iş sınıflarından ayıran uygulamanın mimarisi sayesinde mümkün olmuştur. İşte bu mimarinin en önemli avantajı da budur. Uygun bir [web.config] yapılandırma dosyası kullanılarak, uygulama denetleyicisinde herhangi bir değişiklik yapılmasına gerek kalmayacağı gösterilebilir. [web.config] dosyasına, dinamik olarak örneklenecek veri erişim sınıfının adı ile bu örneklendirme için gerekli çeşitli parametreler eklenebilir. Bu bilgilerle [global.asax], veri erişim nesnesini oluşturabilir. Veri kaynağını değiştirmek, o zaman şu anlama gelir:
- bu kaynağa erişim sınıfı henüz yoksa onu oluşturmak
- [web.config] dosyasını, bu sınıfın bir örneğinin [global.asax] içinde dinamik olarak oluşturulmasına izin verecek şekilde değiştirmek
6.2.8. Web uygulamasının test edilmesi
Yukarıdaki tüm dosyalar <application-path> klasörüne yerleştirilir.

Bu klasörde, [bin] adlı bir alt klasör oluşturulur ve bu alt klasöre, iş sınıfı dosyalarının derlenmesiyle elde edilen [impot.dll] derlemesi yerleştirilir: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb]. Gerekli derleme komutu aşağıda tekrar verilmiştir:
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
Yukarıdaki [impot.dll] dosyası, web uygulamasının bu dosyaya erişebilmesi için <application-path>\bin dizinine yerleştirilmelidir. Cassini sunucusu (<application-path>,/impots2) parametreleriyle başlatılır. Testler, önceki sürümdekiyle aynı sonuçları vermektedir; veritabanının varlığı kullanıcı için şeffaftır. Yine de bu varlığı göstermek amacıyla, SGBD ve MySQL dosyalarını durdurarak ODBC kaynağının kullanılamaz hale gelmesini sağlıyoruz ve [http://localhost/impots2/main.aspx] URL'sini sorguluyoruz. Aşağıdaki yanıtı alıyoruz:

6.3. Örnek 3
6.3.1. Sorun
Burada, web uygulaması tarafından oluşturulan [impot] nesnesinin veri kaynağını yeniden değiştirerek aynı sorunu ele alacağız. Bu sefer, yeni veri kaynağı, OLEDB sürücüsü aracılığıyla erişilecek olan ACCESS veritabanı olacaktır. Amacımız, bir veritabanına erişmenin başka bir yolunu göstermektir.
6.3.2. OLEDB veri kaynağı
Veriler, ACCESS veritabanındaki [IMPOTS] adlı tabloda yer alacaktır. Bu tablonun içeriği şu şekilde olacaktır:

6.3.3. Veri erişim sınıfı
Uygulamamızın MVC yapısına geri dönelim:

- Yukarıdaki şemada, [impotsData] sınıfı verileri almaktan sorumludur. Bu sefer verileri OLEDB kaynağından alması gerekecektir.
Verileri (limites, coeffr, coeffn) ODBC adlı kaynaktan alacak olan [impotsOLEDB] sınıfını oluşturuyoruz:
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
' örnek değişkenleri
Protected chaineConnexion As String
' oluşturucu
Public Sub New(ByVal chaineConnexion As String)
' üç bilgiyi not ediyoruz
Me.chaineConnexion = chaineConnexion
End Sub
Public Overrides Function getData() As Object()
' tablonun içeriğinden üç tabloyu (limites, coeffr, coeffn) başlatır
' OLEDB veritabanındaki [impots] tablosunun içeriğinden [chaineConnexion]
' sınırlar, coeffr ve coeffn bu tablonun üç sütunudur
' çeşitli istisnalar oluşturabilir
' OLEDB kaynağındaki verileri okumak için DataAdapter nesnesi oluşturulur
Dim adaptateur As New OleDbDataAdapter("select limites,coeffr,coeffn from impots", chaineConnexion)
' SELECT sorgusunun sonucunun bellekteki görüntüsü oluşturulur
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
' "impots" tablosunun içeriği alınır
Dim lignesImpots As DataRowCollection = contenu.Rows
' kabul tablolarının boyutları belirlenir
Me.limites = New Decimal(lignesImpots.Count - 1) {}
Me.coeffr = New Decimal(lignesImpots.Count - 1) {}
Me.coeffn = New Decimal(lignesImpots.Count - 1) {}
' "impots" tablosunun içeriği tablolara aktarılır
Dim i As Integer
Dim ligne As DataRow
Try
For i = 0 To lignesImpots.Count - 1
' tablonun i. satırı
ligne = lignesImpots.Item(i)
' satırın içeriği alınır
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
' alınan veriler kontrol ediliyor
Dim erreur As Integer = checkData()
' Veriler geçerli değilse, bir istisna oluşturulur
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' aksi takdirde üç diziyi döndürür
Return New Object() {limites, coeffr, coeffn}
End Function
End Class
End Namespace
Üreticiye bir göz atalım:
' yapıcı
Public Sub New(ByVal chaineConnexion As String)
' üç bilgiyi kaydediyoruz
Me.chaineConnexion = chaineConnexion
End Sub
Bu sürücü, parametre olarak, alınacak verilerin bulunduğu OLEDB kaynağının bağlantı dizesini alır. Sürücü, bu dizgiyi sadece bellekte saklar. Bir bağlantı dizesi, OLEDB sürücüsünün OLEDB kaynağına bağlanmak için gerekli tüm parametreleri içerir. Genellikle oldukça karmaşıktır. ACCESS veritabanlarının bağlantı dizesini öğrenmek için [WebMatrix] aracından yararlanabilirsiniz. Bu aracı çalıştırın. Veri kaynağına bağlanmanıza olanak tanıyan bir pencere açılır:
![]() ![]() | ![]() ![]() |
Yukarıdaki okla gösterilen simge sayesinde, iki tür Microsoft veritabanına bağlantı kurulabilir: SQL Server ve ACCESS. ACCESS'i seçelim:

[...] düğmesini kullanarak ACCESS veritabanını belirledik. Sihirbazı onaylıyoruz. [Data] sekmesinde, bağlantıyı simgeleyen simgeler bulunmaktadır:

Şimdi, [Files/New File] ile yeni bir .aspx dosyası oluşturalım:

Web arayüzümüzü tasarlayabileceğimiz boş bir sayfa açılır:

[Data] sekmesinden [impots] tablosunu yukarıdaki sayfaya sürükleyelim. Şu sonucu elde ediyoruz:

Aşağıdaki [AccessDataSourceControl] nesnesine sağ tıklayarak şu özelliklere erişelim:

OLEDB veritabanına olan bağlantı dizesi, yukarıdaki [ConnectionString] özelliği tarafından belirlenir:
Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\serge\devel\aspnet\poly\chap5\impots\3\impots.mdb
Görüldüğü gibi bu dize, sabit bir kısım ve değişken bir kısımdan oluşmaktadır; değişken kısım ise basitçe ACCESS dosya adıdır. Bu bilgiyi, OLEDB veri kaynağımıza bağlantı dizesini oluşturmak için kullanacağız.
Şimdi [impotsOLEDB] sınıfımıza geri dönelim. [getData] yöntemi, [impots] tablosundaki verileri okumak ve bunları üç diziye (limites, coeffr, coeffn) yerleştirmekle görevlidir. Kodunu inceleyelim:
- [DataAdapter] nesnesini tanımlıyoruz; bu nesne, SQL select sorgusunun sonucunu belleğe aktarmamızı sağlayacak. Bunun için, çalıştırılacak [select] sorgusunu tanımlıyor ve bunu [DataAdapter] nesnesiyle ilişkilendiriyoruz. Bu nesnenin oluşturucusu, OLEDB kaynağına bağlanmak için kullanacağı bağlantı dizesini de talep eder
' OLEDB kaynağındaki verileri okumak için bir DataAdapter nesnesi oluşturulur
Dim adaptateur As New OleDbDataAdapter("select limites,coeffr,coeffn from impots", chaineConnexion)
- [DataAdapter] nesnesinin [Fill] yöntemi kullanılarak [select] komutu çalıştırılır. [select] komutunun sonucu, bu amaçla oluşturulan bir [DataTable] nesnesine aktarılır. Bir [DataTable] nesnesi, c.a.d adlı bir veritabanı tablosunun bellekteki görüntüsüdür; bir dizi satır ve sütundan oluşur. Örneğin bağlantı dizesi yanlışsa ortaya çıkabilecek bir istisnayı yönetiyoruz.
' SELECT sorgusunun sonucunun bellekteki görüntüsü oluşturulur
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
- [contenu]'te, [select] tarafından getirilen [impots] tablosu bulunmaktadır. [DataTable] nesnesi bir tablo, yani bir dizi satırdır. Bu satırlara, [datatable]'in [rows] özelliği aracılığıyla erişilebilir:
' "impots" tablosunun içeriği alınır
Dim lignesImpots As DataRowCollection = contenu.Rows
- [lignesImpots] koleksiyonundaki her bir öğe, tablonun bir satırını temsil eden [DataRow] türünde bir nesnedir. Bu nesnenin sütunlarına, [DataRow] nesnesinin [Item] özelliği aracılığıyla erişilebilir. [DataRow].[Item(i)], [DataRow] satırının i numaralı sütunudur. Satır koleksiyonunu (lignesImpots'in DataRows koleksiyonu) ve her satırın sütun koleksiyonunu tarayarak, tablonun tamamını elde edebiliriz:
' kabul tablolarının boyutları belirlenir
Me.limites = New Decimal(lignesImpots.Count - 1) {}
Me.coeffr = New Decimal(lignesImpots.Count - 1) {}
Me.coeffn = New Decimal(lignesImpots.Count - 1) {}
' "impots" tablosunun içeriği tablolara aktarılır
Dim i As Integer
Dim ligne As DataRow
Try
For i = 0 To lignesImpots.Count - 1
' tablonun i. satırı
ligne = lignesImpots.Item(i)
' satırın içeriği alınır
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
- [impots] tablosundaki veriler üç tabloya aktarıldıktan sonra, geriye sadece [impotsData] temel sınıfının [checkData] yöntemini kullanarak bu tabloların içeriğini kontrol etmek kalır:
' alınan veriler kontrol ediliyor
Dim erreur As Integer = checkData()
' Veriler geçerli değilse, bir istisna oluşturulur
If Not valide Then Throw New Exception("Les données des tranches d'impôts sont invalides (" + erreur.ToString + ")")
' aksi takdirde üç diziyi döndürür
Return New Object() {limites, coeffr, coeffn}
6.3.4. Veri erişim sınıfı testleri
Bir test programı şu şekilde olabilir:
Option Explicit On
Option Strict On
' ad alanları
Imports System
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' test sayfası
Module testimpots
Sub Main(ByVal arguments() As String)
' etkileşimli vergi hesaplama programı
' kullanıcı klavyeden üç veri girer: evli nbEnfants maaş
' program daha sonra ödenecek vergiyi gösterir
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"
' program parametrelerinin kontrolü
If arguments.Length <> 1 Then
' hata mesajı
Console.Error.WriteLine(syntaxe1)
' son
Environment.Exit(1)
End If
' argümanlar alınır
Dim chemin As String = arguments(0)
' bağlantı dizesi hazırlanıyor
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + chemin
' vergi nesnesi oluşturuluyor
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
' sonsuz döngü
While True
' başlangıçta hata yok
Dim erreur As Boolean = False
' Vergi hesaplaması için parametreler isteniyor
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()
' Yapılması gereken bir şey var mı?
If paramètres Is Nothing Or paramètres = "" Then
Exit While
End If
' girdilen satırdaki argüman sayısının doğrulanması
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
' parametrelerin geçerliliğinin kontrolü
' evli
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
' maaş
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
' parametreler doğru - vergi hesaplanıyor
Console.Out.WriteLine(("impôt=" & objImpot.calculer(marié = "o", nbEnfants, salaire).ToString + " euro(s)"))
End If
End While
End Sub
End Module
End Namespace
Uygulama bir parametre ile başlatılır:
- bdACCESS: işlenecek ACCESS dosyasının adı
Vergi hesaplaması, uygulamanın başlatılmasıyla birlikte oluşturulan [impot] türündeki bir nesne kullanılarak yapılır:
' argümanlar alınır
Dim chemin As String = arguments(0)
' bağlantı dizesi hazırlanıyor
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + chemin
' vergi nesnesi oluşturuluyor
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
OLEDB kaynak bağlantı zinciri, [WebMatrix] ile elde edilen bilgilerden oluşturulmuştur.
Uygulama başlatıldıktan sonra, kullanıcıdan vergisini hesaplamak için gerekli olan üç bilgiyi klavyeyle girmesini tekrar tekrar ister:
- medeni durumu: o evli, n bekar
- çocuk sayısı
- yıllık maaşı
Tüm sınıflar derlenir:
dos>vbc /r:system.dll /r:system.data.dll /t:library /out:impot.dll impots.vb impotsArray.vb impotsData.vb impotsOLEDB.vb
[impots.mdb] dosyası test uygulamasının klasörüne yerleştirilir ve uygulama şu şekilde başlatılır:
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)
Uygulama, hatalı bir ACCESS dosyasıyla da çalıştırılabilir:
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. Web uygulamasının görünümleri
Bunlar önceki uygulamanın görünümleridir: formulaire.aspx ve erreurs.aspx
6.3.6. [global.asax, main.aspx] uygulama denetleyicileri
Yalnızca [global.asax] denetleyicisi değiştirilmelidir. Bu denetleyici, uygulamanın başlatılması sırasında [impot] nesnesini oluşturmaktan sorumludur. Bu nesnenin oluşturucusunun tek parametresi, verileri almaktan sorumlu [impotsData] türündeki nesnedir. Dolayısıyla, veri kaynakları değiştirildiğinden bu parametre de değişir. [global.asax.vb] denetleyicisi şu şekilde olur:
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)
' bir vergi nesnesi oluşturuluyor
Dim objImpot As impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
' nesneyi uygulamaya ekleniyor
Application("objImpot") = objImpot
' hata yok
Application("erreur") = False
Catch ex As Exception
'hata oluştu, uygulamaya kaydedildi
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
[impot] nesnesinin veri kaynağı artık bir [impotOLEDB] nesnesidir. Bu nesnenin parametresi, kullanılacak OLEDB veri kaynağının bağlantı dizesidir. Bu dize, uygulamanın [web.config] yapılandırma dosyasına yerleştirilmiştir:
<?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>
[main.aspx] denetleyicisi değişmez.
6.3.7. Değişiklik özeti
Uygulama test edilmeye hazırdır. Önceki sürüme yapılan değişiklikleri sıralayalım:
- yeni bir veri erişim sınıfı oluşturuldu
- [global.asax.vb] denetleyicisinde bir yerde değişiklik yapıldı: [impot] nesnesinin oluşturulması
- [web.config] dosyası eklendi
6.3.8. Web uygulamasının test edilmesi
Yukarıdaki tüm dosyalar <application-path> klasörüne yerleştirilir.

Bu klasörde, [bin] adlı bir alt klasör oluşturulur ve bu klasöre, iş sınıfı dosyalarının derlenmesiyle elde edilen [impot.dll] derlemesi yerleştirilir: [impots.vb, impotsData.vb, impotsArray.vb, impotsOLEDB.vb]. Gerekli derleme komutu aşağıda tekrar verilmiştir:
dos>vbc /r:system.dll /r:system.data.dll /t:library /out:impot.dll impots.vb impotsArray.vb impotsData.vb impotsOLEDB.vb
Bu komutla oluşturulan [impot.dll] dosyası, web uygulamasının bu dosyaya erişebilmesi için <application-path>\bin dizinine yerleştirilmelidir. Cassini sunucusu (<application-path>,/impots3) parametreleriyle başlatılır. Testler, önceki sürümdekiyle aynı sonuçları vermektedir.
6.4. Örnek 4
6.4.1. Sorun
Şimdi uygulamamızı vergi hesaplama simülasyonu uygulamasına dönüştürmeyi planlıyoruz. Bir kullanıcı art arda vergi hesaplamaları yapabilecek ve bu hesaplamalar ona şuna benzer yeni bir görünümde sunulacaktır:

6.4.2. Uygulamanın MVC yapısı
Uygulamanın MVC yapısı şu şekilde olur:

Az önce ekran görüntüsünü verdiğimiz yeni bir görünüm olan [simulations.aspx] ortaya çıkar. Veri erişim sınıfı, Örnek 2'deki [impotsODBC] sınıfı olacaktır.
6.4.3. Web uygulamasının görünümleri
[erreurs.aspx] görünümü değişmez. [formulaire.aspx] görünümü ise biraz değişir. Artık bu görünümde vergi tutarı görünmemektedir. Bu tutar artık [simulations.aspx] görünümünde yer almaktadır. Böylece, uygulama başlatıldığında kullanıcıya sunulan sayfa şöyledir:

Ayrıca, [formulaire] görünümü, aşağıdaki örnekte gösterildiği gibi, girilen verilerin geçerliliğini kontrol eden bir JavaScript komut dosyası içerir:

Görünüm kodu şöyledir:
<%@ page src="formulaire.aspx.vb" inherits="formulaire" AutoEventWireup="false"%>
<html>
<head>
<title>Impôt</title>
<script language="javascript">
function calculer(){
// parametreler sunucuya gönderilmeden önce kontrol ediliyor
with(document.frmImpots){
//çocuk sayısı
champs=/^\s*(\d+)\s*$/.exec(txtEnfants.value);
if(champs==null){
// şablon doğrulanmadı
alert("Le nombre d'enfants n'a pas été donné ou est incorrect");
txtEnfants.focus();
return;
}//if
//maaş
champs=/^\s*(\d+)\s*$/.exec(txtSalaire.value);
if(champs==null){
// şablon doğrulanmadı
alert("Le salaire n'a pas été donné ou est incorrect");
txtSalaire.focus();
return;
}//eğer
// sorun yok - form sunucuya gönderiliyor
submit();
}//with
}//hesapla
</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>
Sayfanın dinamik alanları önceki sürümlerdekiyle aynıdır. Vergi tutarı dinamik alanı kaldırılmıştır. [Calculer] düğmesi artık [submit] türünde bir düğme değildir. Bu düğme [button] türündedir ve tıklandığında [calculer()] javascript işlevi çalıştırılır:
<INPUT type="button" value="Calculer" onclick="calculer()">
Forma, [calculer] komut dosyasında ona başvurabilmek için [frmImpots] adı verilmiştir:
<form name="frmImpots" method="post" action="main.aspx?action=calcul">
[calculer] javascript işlevi, [document.frmImpots.txtEnfants] ve [document.frmImpots.txtSalaire] formlarındaki alanların geçerliliğini kontrol etmek için düzenli ifadeler kullanır. Girilen değerler doğruysa, [document.frmImpots.submit()] tarafından sunucuya gönderilir.
Görüntüleme sayfası, dinamik alanlarını aşağıdaki [formulaire.aspx.vb] denetleyicisinden alır:
Imports System.Collections.Specialized
Public Class formulaire
Inherits System.Web.UI.Page
' sayfanın alanları
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
' bağlamdan önceki isteği alıyoruz
Dim form As NameValueCollection = Context.Items("formulaire")
' görüntülenecek sayfayı hazırlıyoruz
' radyo düğmeleri
rdouichecked = ""
rdnonchecked = "checked"
If form("rdMarie").ToString = "oui" Then
rdouichecked = "checked"
rdnonchecked = ""
End If
' geri kalan kısım
txtEnfants = CType(form("txtEnfants"), String)
txtSalaire = CType(form("txtSalaire"), String)
End Sub
End Class
[formulaire.aspx.vb] denetleyicisi, sayfadan kaldırılan [txtImpot] alanını bağlamdan almaması dışında önceki sürümlerle aynıdır.
[simulations.aspx] görünümü görsel olarak şu şekildedir:

ve aşağıdaki sunum koduna karşılık gelir:
<%@ 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>
Bu kodda üç dinamik alan bulunmaktadır:
HTML kodlu bir simülasyon listesi, tablo satırları biçiminde HTML | |
bir bağlantının URL'si | |
bağlantı metni |
Bunlar, [simulations.aspx.vb] denetleyici bileşeni tarafından oluşturulur:
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
'bağlamdan simülasyonlar alınır
Dim simulations As ArrayList = CType(context.Items("simulations"), ArrayList)
' her simülasyon, 4 string elemanından oluşan bir dizidir
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
' bağlamdaki diğer öğeleri alıyoruz
href = context.Items("href").ToString
lien = context.Items("lien").ToString
End Sub
End Class
Sayfa denetleyicisi, uygulama denetleyicisi tarafından sayfa bağlamına yerleştirilen bilgileri alır:
Görüntülenecek simülasyonların listesini içeren ArrayList nesnesi. Her bir öğe, simülasyona ait bilgileri (evli, çocuk sayısı, maaş, vergi) temsil eden 4 karakter dizisinden oluşan bir dizidir. | |
bir bağlantının URL'si | |
bağlantı metni |
6.4.4. [global.asax, main.aspx] denetleyicileri
Uygulamamızın şemasını hatırlayalım: MVC

[main.aspx] denetleyicisi üç eylemi işlemek zorundadır:
- init: istemcinin ilk isteğine karşılık gelir. Denetleyici, [formulaire.aspx] görünümünü görüntüler
- calcul: vergi hesaplama talebine karşılık gelir. Giriş formundaki veriler doğruysa, vergi [impotsODBC] iş sınıfı kullanılarak hesaplanır. Denetleyici, mevcut simülasyonun sonucunu ve önceki tüm simülasyonların sonuçlarını içeren [simulations.aspx] görünümünü müşteriye döndürür. Giriş formundaki veriler yanlışsa, denetleyici hata listesini ve forma geri dönmek için bir bağlantı içeren [erreurs.aspx] görünümünü döndürür.
- geri dönüş: bir hata sonrasında forma geri dönülmesini sağlar. Denetleyici, hata öncesinde onaylanmış haliyle [formulaire.aspx] görünümünü görüntüler.
Bu yeni sürümde yalnızca [calcul] eylemi değişmiştir. Nitekim, veriler geçerliyse bu eylem [simulations.aspx] görünümüne yönlendirmelidir; oysa daha önce [formulaire.aspx] görünümüne yönlendiriyordu. [main.aspx.vb] denetleyicisi şu şekilde değiştirilmiştir:
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
' her şeyden önce, uygulamanın düzgün bir şekilde başlatılıp başlatılmadığını kontrol ediyoruz
...
' eylemi yürütüyoruz
Select Case action
Case "init"
' uygulamayı başlat
initAppli()
Case "calcul"
' vergi hesaplaması
calculImpot()
Case "retour"
' form sayfasına geri dön
retourFormulaire()
Case "effacer"
' uygulamayı başlat
initAppli()
Case Else
' bilinmeyen eylem = başlatma
initAppli()
End Select
End Sub
...
Private Sub calculImpot()
' girdiler kaydediliyor
Session.Item("formulaire") = Request.Form
' girilen verilerin geçerliliği kontrol ediliyor
Dim erreurs As ArrayList = checkData()
' hata varsa, uyarı veriliyor
If erreurs.Count <> 0 Then
' hata sayfası hazırlanıyor
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
context.Items("erreurs") = erreurs
Server.Transfer("erreurs.aspx")
End If
' burada hata yok - vergi hesaplanır
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
Request.Form("rdMarie") = "oui", _
CType(Request.Form("txtEnfants"), Integer), _
CType(Request.Form("txtSalaire"), Long))
' sonuç mevcut simülasyonlara eklenir
Dim simulations As ArrayList
If Not Session.Item("simulations") Is Nothing Then
simulations = CType(Session.Item("simulations"), ArrayList)
Else
simulations = New ArrayList
End If
' mevcut simülasyon ekleniyor
Dim simulation() As String = New String() {Request.Form("rdMarie").ToString, _
Request.Form("txtEnfants").ToString, Request.Form("txtSalaire").ToString, _
impot.ToString}
simulations.Add(simulation)
' simülasyonlar oturuma ve bağlama ekleniyor
context.Items("simulations") = simulations
Session.Item("simulations") = simulations
' sonuç sayfası görüntüleniyor
context.Items("href") = "main.aspx?action=retour"
context.Items("lien") = "Retour au formulaire"
Server.Transfer("simulations.aspx", True)
End Sub
...
End Class
Yukarıda, yalnızca [calculImpots] işlevinde bulunan değişiklikleri anlamak için gerekli olan kısımları sakladık:
- öncelikle işlev, [Request.Form] formunu oturumda kaydeder; bunun amacı, formun onaylandığı haliyle yeniden oluşturulabilmesini sağlamaktır. Bu işlem her durumda yapılmalıdır; çünkü işlem [erreurs.aspx] yanıtıyla veya [simulations.aspx] yanıtıyla sonuçlansa da, [Retour au formulaire] bağlantısı aracılığıyla forma geri dönülür. Formu doğru şekilde geri yüklemek için, değerlerin önceden oturumda kaydedilmiş olması gerekir.
- Girilen veriler doğruysa, işlev mevcut simülasyonu (evli, çocuk sayısı, maaş, vergi) simülasyonlar listesine ekler. Bu liste, "simulations" anahtarıyla ilişkili oturumda bulunur.
- Simülasyon listesi, ileride kullanılmak üzere oturuma geri yerleştirilir. Ayrıca, [simulations.aspx] görünümü bu listeyi beklediği için mevcut bağlama da eklenir.
- [simulations.aspx] görünümü, beklediği diğer bilgiler bağlama eklendikten sonra görüntülenir
6.4.5. Değişikliklerin özeti
Uygulama test edilmeye hazırdır. Önceki sürümlerde yapılan değişiklikleri sıralayalım:
- yeni bir görünüm oluşturuldu
- [main.aspx.vb] denetleyicisinde bir yerde değişiklik yapıldı: [calcul] eyleminin işlenmesi
6.4.6. Web uygulamasının testi
Okuyucunun testleri yapması istenir. Yöntemi hatırlatalım. Uygulamanın tüm dosyaları <application-path> klasörüne yerleştirilir. Bu klasörde, [bin] adlı bir alt klasör oluşturulur ve bu klasöre, iş sınıfı dosyalarının derlenmesiyle elde edilen [impot.dll] derlemesi yerleştirilir: [impots.vb, impotsData.vb, impotsArray.vb, impotsODBC.vb. Bu komutla oluşturulan [impot.dll] dosyası, web uygulamasının bu dosyaya erişebilmesi için <application-path>\bin dizinine yerleştirilmelidir. Cassini sunucusu (<application-path>,/impots4) parametreleriyle başlatılır.
6.5. Conclusion
Yukarıdaki örnekler, web geliştirmede yaygın olarak kullanılan mekanizmaları somut bir vaka üzerinden göstermiştir. Eğitimsel değeri nedeniyle sistematik olarak MVC mimarisini kullandık. Bu mimari olmadan da aynı örnekleri farklı ve belki de daha basit bir şekilde ele alabilirdik. Ancak uygulama, birden fazla sayfaya sahip ve biraz karmaşık hale geldiğinde bu mimari büyük avantajlar sağlar.
Örneklerimize farklı şekillerde devam edebiliriz. İşte bunlardan birkaçı:
- Kullanıcı, simülasyonlarını zaman içinde saklamak isteyebilir. Örneğin, J gününde simülasyonlar yapabilir ve bunları J+3 gününde tekrar bulabilir. Bu soruna olası bir çözüm, çerezlerin kullanılmasıdır. Sunucu ile istemci arasındaki oturum çerezinin bu mekanizma aracılığıyla aktarıldığını biliyoruz. Simülasyonları istemci ile sunucu arasında aktarmak için de bu mekanizmayı kullanabiliriz.
- Sunucu, simülasyonların sonuç sayfasını gönderirken, başlıklarında HTTP şeklinde simülasyonları temsil eden bir karakter dizisi içeren bir çerez de gönderir. Bunlar bir [ArrayList] nesnesi içinde bulunduğundan, bu nesnenin [String]'e dönüştürülmesi gerekir. Sunucu, çereze bir geçerlilik süresi, örneğin 30 gün, atar.
- İstemci tarayıcısı, aldığı çerezleri bir dosyada saklar ve geçerlilik süreleri dolmamışsa (yani süre aşılmamışsa), bu çerezleri kendisine gönderen sunucuya her istek gönderdiğinde geri gönderir. Sunucu, simülasyonlar için [String] karakter dizisini alacak ve bunu [ArrayList] nesnesine dönüştürmesi gerekecektir.
Çerezler, istemciye gönderilirken [Response.Cookies] tarafından, sunucuya alındığında ise [Request.Cookies] tarafından yönetilir.
- Simülasyon sayısı çok fazla olduğunda yukarıdaki mekanizma oldukça ağır hale gelebilir. Ayrıca, kullanıcı tarayıcısının çerezleri kullanmasına izin verse bile, çerezlerini periyodik olarak silerek temizlemesi sık rastlanan bir durumdur. Dolayısıyla er ya da geç simülasyonlara ait çerezler kaybolacaktır. Bu durumda, çerezleri istemcide değil de sunucuda, örneğin bir veritabanında depolamak isteyebiliriz. Simülasyonları belirli bir kullanıcıyla ilişkilendirmek için, uygulama, bir veritabanında veya başka herhangi bir veri deposunda saklanan kullanıcı adı ve şifreyi gerektiren bir kimlik doğrulama aşamasıyla başlayabilir.
- Ayrıca, uygulamamızın çalışmasını güvenli hale getirmek isteyebiliriz. Uygulama şu anda iki varsayımda bulunuyor:
- kullanıcı her zaman [main.aspx] denetleyicisinden geçer
- ve bu durumda, kendisine gönderilen sayfada sunulan eylemleri her zaman kullanır
Örneğin, kullanıcı doğrudan [http://localhost/impots4/formulaire.aspx] URL'sini talep ederse ne olur? Kullanıcı bu URL'nin varlığından haberdar olmadığı için bu durum pek olası değildir. Ancak yine de bu durum için önlem alınmalıdır. Bu durum, uygulamaya yapılan tüm istekleri gören [global.asax] uygulama denetleyicisi tarafından yönetilebilir. Böylece, istenen kaynağın gerçekten [main.aspx] olup olmadığını doğrulayabilir.
Daha olası bir durum ise, kullanıcının sunucunun kendisine gönderdiği sayfada bulunan eylemleri kullanmamasıdır. Örneğin, kullanıcı formu doldurmadan doğrudan [http://localhost/impots4/main.aspx?action=retour] URL’sini talep ederse ne olur? Deneyelim. Şu yanıtı alıyoruz:

Sunucuda bir çökme meydana geldi. Bu normal bir durumdur. [retour] eylemi için denetleyici, oturumda görüntülemesi gereken form değerlerini temsil eden bir [NameValueCollection] nesnesi bulmayı bekler. Ancak bu nesneyi bulamaz. Denetleyicinin mekanizması, bu soruna zarif bir çözüm sunar. Her istek için, [main.aspx] denetleyicisi, istenen eylemin daha önce kullanıcıya gönderilen sayfanın eylemlerinden biri olup olmadığını doğrulayabilir. Aşağıdaki mekanizma kullanılabilir:
- denetleyici, yanıtını müşteriye göndermeden önce, bu sayfayı tanımlayan bir bilgiyi müşterinin oturumuna kaydeder
- müşteriden yeni bir istek aldığında, istenen eylemin gerçekten bu müşteriye gönderilen son sayfaya ait olup olmadığını doğrular
- Sayfalar ile bu sayfalarda izin verilen eylemler arasındaki ilişki bilgileri, uygulamanın [web.config] yapılandırma dosyasına girilebilir.
- Deneyimler, uygulama denetleyicilerinin geniş bir ortak temele sahip olduğunu ve genel bir denetleyici oluşturulabileceğini göstermektedir; bu denetleyicinin belirli bir uygulama için özelleştirilmesi ise bir yapılandırma dosyası aracılığıyla gerçekleştirilir. Örneğin, Java ile web programlama alanında [Struts] aracı bu yolu izlemektedir.



