Skip to content

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

    8382        0.0683        291.09

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:

    0                0.4809    9505.54

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:

Image

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:

limites
vergi dilimlerinin sınırları tablosu
coeffr
vergilendirilebilir gelire uygulanan katsayı tablosu
coeffn
hisse sayısına uygulanan katsayı tablosu
checked
verilerin (sınırlar, katsayı1, katsayı2) doğrulanıp doğrulanmadığını belirten boole değeri
valide
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>vbc /r:impot.dll testimpots1.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
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:

Image

Kullanıcı formu doldurur:

Image

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

Image

Girdiği verilerde hata yapabilir:

Image

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

Image

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:

Image

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:

rdouichecked
[oui] kutucuğunun işaretlenmesi gerekiyorsa "checked", aksi takdirde ""
rdnonchecked
Aynı durum [non] kutucuğu için de geçerlidir
txtEnfants
[txtEnfants] giriş alanına girilecek değer
txtSalaire
[txtSalaire] giriş alanına girilecek değer
txtImpot
[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:

erreursHTML
bir hata listesinden alınan HTML kodu
href
bir bağlantının URL'si
lien
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:

Context.Items("erreurs"
görüntülenecek hata mesajlarının listesini içeren ArrayList nesnesi
Context.Items("href"
bir bağlantının URL'si
Context.Items("lien"
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:

Image

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]

<%@ Application src="Global.asax.vb" Inherits="Global" %>

[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]

<%@ page src="main.aspx.vb" inherits="main" AutoEventWireup="false"%>

[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.

Image

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:

Image

Formu dolduruyoruz:

Image

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

Image

Ardından hatalı girdiler yapıyoruz:

Image

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

Image

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

Image

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

Image

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:

Image

[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:

Image

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.

Image

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:

  1. Microsoft SQL Server veritabanlarına erişim için SQL ve Server.NET sınıfları
  2. Db.NET Ole sınıfları, SGBD sürücüsü sunan OLE ve DB veritabanlarına erişim için
  3. 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:

Image

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:

Image

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:

Image

Birkaç bilgi girilmesi gerekiyor:

Data Source Name
ODBC veri kaynağını tanımlayacak ad. Herhangi bir Windows uygulaması bu ad üzerinden kaynağa erişebilir
Description
Veri kaynağını tanımlayan rastgele bir metin
Host Name
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.
Database Name
Bir SGBD MySQL, birden fazla veritabanını yönetebilir. Burada hangi veritabanını yönetmek istediğimizi belirtiriz: dbimpots
User
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
Password
Bu kullanıcının şifresi. Burada: mdpimpots
Port
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:

Image

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:

Image

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

Image

Girilmesi gereken bilgiler şunlardır:

Nom de la source de données
ODBC veri kaynağını tanımlayacak ad. Herhangi bir Windows uygulaması bu ad üzerinden kaynağa erişebilir
Description
Veri kaynağını açıklayan rastgele bir metin
Base de données
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:

Image

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:
                 ' Kaynaklar serbest bırakılır
                myReader.Close()
                impotsConn.Close()
  • Üç [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:

dos>vbc /r:impot.dll testimpots.vb
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:

  1. yeni bir veri erişim sınıfı oluşturuldu
  2. [global.asax.vb] denetleyicisi iki yerde değiştirildi: [impot] nesnesinin oluşturulması ve olası istisna ile ilgili mesajın uygulamaya kaydedilmesi
  3. [main.aspx.vb] denetleyicisi, önceki istisna mesajını görüntülemek üzere bir noktada değiştirildi
  4. [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.

Image

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:

Image

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:

Image

6.3.3. Veri erişim sınıfı

Uygulamamızın MVC yapısına geri dönelim:

Image

  • 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:

Image

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

Image

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

Image

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

Image

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

Image

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

Image

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
dos>vbc /r:impot.dll testimpots.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:

  1. yeni bir veri erişim sınıfı oluşturuldu
  2. [global.asax.vb] denetleyicisinde bir yerde değişiklik yapıldı: [impot] nesnesinin oluşturulması
  3. [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.

Image

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:

Image

6.4.2. Uygulamanın MVC yapısı

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

Image

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:

Image

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:

Image

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:

Image

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:

simulationsHTML
HTML kodlu bir simülasyon listesi, tablo satırları biçiminde HTML
href
bir bağlantının URL'si
lien
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:

Context.Items("simulations")
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.
Context.Items("href")
bir bağlantının URL'si
Context.Items("lien")
bağlantı metni

6.4.4. [global.asax, main.aspx] denetleyicileri

Uygulamamızın şemasını hatırlayalım: MVC

Image

[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:

  1. yeni bir görünüm oluşturuldu
  2. [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:

Image

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.