Skip to content

9. ASP sunucu bileşenleri - 3

9.1. Introduction

Kullanıcı arayüzü üzerindeki çalışmalarımıza devam ederek, özellikle görüntüledikleri verilerin güncellenmesi alanında [DataList] ve [DataGrid] bileşenlerinin yeteneklerini derinleştiriyoruz

9.2. Veri bağlantılı bileşenlerin verileriyle ilişkili olayları yönetme

9.2.1. Örnek

Aşağıdaki sayfayı ele alalım:

Sayfa, bir veri listesiyle ilişkili üç bileşen içerir:

  • [DataList1] adlı [DataList] bileşeni
  • [DataGrid1] adlı [DataGrid] bileşeni
  • [Repeater] adlı bir bileşen (adı [Repeater1])

İlgili veri listesi {"sıfır", "bir", "iki", "üç"} dizisidir. Bu verilerin her birine, [Infos1] ve [Infos2] adlı iki düğmeden oluşan bir grup atanmıştır. Kullanıcı düğmelerden birine tıkladığında, tıklanan düğmenin adı bir metin olarak görüntülenir. Burada, bir düğme veya bağlantı listesinin nasıl yönetileceğini göstermeyi amaçlıyoruz.

9.2.2. Bileşenlerin yapılandırılması

[DataList1] bileşeni şu şekilde yapılandırılmıştır:


                        <asp:datalist id="DataList1" ... runat="server">
                            <SelectedItemStyle ...</SelectedItemStyle>
                            <HeaderTemplate>
                                [début]
                            </HeaderTemplate>
                            <FooterTemplate>
                                [fin]
                            </FooterTemplate>
                            <ItemStyle ...></ItemStyle>
                            <ItemTemplate>
                                <P><%# Container.DataItem %>
                                    <asp:Button runat="server" Text="Infos1" CommandName="infos1"></asp:Button>
                                    <asp:Button runat="server" Text="Infos2" CommandName="infos2"></asp:Button></P>
                            </ItemTemplate>
                            <FooterStyle ...></FooterStyle>
                            <HeaderStyle ...></HeaderStyle>
                        </asp:datalist>

[DataList]'in görünümüyle ilgili her şeyi göz ardı ederek sadece içeriğine odaklandık:

  • <HeaderTemplate> bölümü, [DataList] dosyasının üstbilgisini, <FooterTemplate> bölümü ise altbilgisini tanımlar.
  • <ItemTemplate> bölümü, ilişkili veri listesindeki her bir veri için kullanılan görüntüleme şablonudur. Burada şu öğeler bulunur:
    • bileşene bağlı veri listesindeki geçerli verinin değeri: <%# Container.DataItem %>
    • sırasıyla [Infos1] ve [Infos2] olarak adlandırılmış iki düğme. [Button] sınıfı, burada kullanılan [CommandName] özniteliğine sahiptir. Bu öznitelik, [DataList]'te bir olayı tetikleyen düğmenin hangisi olduğunu belirlememizi sağlayacaktır. Düğmelere yapılan tıklamaları yönetmek için, düğmelere değil, doğrudan [DataList]'e bağlı tek bir olay işleyicisi kullanacağız. Bu işleyici, tıklamanın [DataList]'in hangi satırında gerçekleştiğini belirten bir bilgi alacaktır. [CommandName] özniteliği, tıklamanın o satırdaki hangi düğmede gerçekleştiğini bilmemizi sağlayacaktır.

[Repeater1] bileşeni de çok benzer şekilde yapılandırılmıştır:


                        <asp:repeater id="Repeater1" runat="server">
                            <HeaderTemplate>
                                [début]<hr />
                            </HeaderTemplate>
                            <FooterTemplate>
                                <hr />
                                [fin]
                            </FooterTemplate>
                            <SeparatorTemplate>
                                <hr />
                            </SeparatorTemplate>
                            <ItemTemplate>
                                <%# Container.DataItem %>
                                <asp:Button runat="server" Text="Infos1" CommandName="infos1"></asp:Button>
                                <asp:Button runat="server" Text="Infos2" CommandName="infos2"></asp:Button></P>
                            </ItemTemplate>
                        </asp:repeater>

Bileşen tarafından görüntülenen ardışık verilerin yatay bir çubukla ayrılması için basitçe bir <SeparatorTemplate> bölümü eklendi.

Son olarak, [DataGrid1] bileşeni şu şekilde yapılandırılmıştır:


                        <asp:datagrid id="DataGrid1" ... runat="server" PageSize="2" AllowPaging="True">
                            <SelectedItemStyle ...></SelectedItemStyle>
                            <AlternatingItemStyle ...></AlternatingItemStyle>
                            <ItemStyle ...></ItemStyle>
                            <HeaderStyle ...></HeaderStyle>
                            <FooterStyle ....></FooterStyle>
                            <Columns>
                                <asp:ButtonColumn Text="Infos1" ButtonType="PushButton" CommandName="Infos1">
                              </asp:ButtonColumn>
                                <asp:ButtonColumn Text="Infos2" ButtonType="PushButton" CommandName="Infos2">
                              </asp:ButtonColumn>
                            </Columns>
                            <PagerStyle .... Mode="NumericPages"></PagerStyle>
                        </asp:datagrid>

Burada da stil bilgilerini (renkler, genişlikler vb.) atladık. [DataGrid]'in varsayılan modu olan otomatik sütun oluşturma modundayız. Bu, veri kaynağında kaç sütun varsa o kadar sütun olacağı anlamına gelir. Burada bir sütun var. <asp:ButtonColumn> etiketiyle işaretlenmiş iki sütun daha ekledik. Burada, diğer iki bileşen için tanımlananlara benzer bilgileri ve düğme türünü (burada [PushButton]) tanımlıyoruz. Varsayılan tür [LinkButton], c.a.d'tir. bir bağlantıdır. Ayrıca, veriler [AllowPaging=true] ile sayfalara bölünecek ve sayfa boyutu iki öğe [PageSize=2] olacaktır.

9.2.3. Sayfanın sunum kodu

Örnek sayfamızın sunum kodu [main.aspx] adlı bir dosyaya yerleştirilmiştir:


<%@ page codebehind="main.aspx.vb" inherits="vs.main" autoeventwireup="false" %>
<HTML>
    <HEAD>
    </HEAD>
    <body>
        <form runat="server">
            <P>Gestion d'événements de composants associés à des listes de données</P>
            <HR width="100%" SIZE="1">
            <table cellSpacing="1" cellPadding="1" bgColor="#ffcc00" border="1">
                <tr>
                    <td ...>DataList</td>
                    <td ...>DataGrid</td>
                    <td ...>Repeater</td>
                </tr>
                <tr>
                    <td ...>

                      <asp:datalist id="DataList1" ... runat="server">
                            <HeaderTemplate>
                                [début]
                            </HeaderTemplate>
                            <FooterTemplate>
                                [fin]
                            </FooterTemplate>
                            <ItemStyle ...></ItemStyle>
                            <ItemTemplate>
                                <P><%# Container.DataItem %>
                                    <asp:Button runat="server" Text="Infos1" CommandName="infos1"></asp:Button>
                                    <asp:Button runat="server" Text="Infos2" CommandName="infos2"></asp:Button></P>
                            </ItemTemplate>
                            <FooterStyle ...></FooterStyle>
                            <HeaderStyle ....></HeaderStyle>
                        </asp:datalist>

                        <P></P>
                    </td>
                    <td ...>

                       <asp:datagrid id="DataGrid1" ... runat="server" PageSize="2" AllowPaging="True">
                            <AlternatingItemStyle ...></AlternatingItemStyle>
                            <ItemStyle ...></ItemStyle>
                            <HeaderStyle ...></HeaderStyle>
                            <FooterStyle ...></FooterStyle>
                            <Columns>
                                <asp:ButtonColumn Text="Infos1" ButtonType="PushButton" CommandName="Infos1"> 
                              </asp:ButtonColumn>
                                <asp:ButtonColumn Text="Infos2" ButtonType="PushButton" CommandName="Infos2">
                               </asp:ButtonColumn>
                            </Columns>
                            <PagerStyle ... Mode="NumericPages"></PagerStyle>
                        </asp:datagrid>

                  </td>
                    <td ...>

                       <asp:repeater id="Repeater1" runat="server">
                            <HeaderTemplate>
                                [début]<hr />
                            </HeaderTemplate>
                            <FooterTemplate>
                                <hr />
                                [fin]
                            </FooterTemplate>
                            <SeparatorTemplate>
                                <hr />
                            </SeparatorTemplate>
                            <ItemTemplate>
                                <%# Container.DataItem %>
                                <asp:Button runat="server" Text="Infos1" CommandName="infos1"></asp:Button>
                                <asp:Button runat="server" Text="Infos2" CommandName="infos2"></asp:Button></P>
                            </ItemTemplate>
                        </asp:repeater>

                  </td>
                </tr>
            </table>
            <P><asp:label id="lblInfo" runat="server"></asp:label></P>
            <P></P>
        </form>
    </body>
</HTML>

Yukarıdaki kodda, biçimlendirme kodlarını (renkler, çizgiler, boyutlar vb.) çıkardık.

9.2.4. Sayfanın kontrol kodu

Uygulamanın kontrol kodu, [main.aspx.vb] dosyasına yerleştirilmiştir:

Public Class main
    Inherits System.Web.UI.Page

     ' sayfa bileşenleri
    Protected WithEvents DataList1 As System.Web.UI.WebControls.DataList
    Protected WithEvents lblInfo As System.Web.UI.WebControls.Label
    Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents Repeater1 As System.Web.UI.WebControls.Repeater

     ' veri kaynağı
    Protected textes() As String = {"zéro", "un", "deux", "trois"}

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        If Not IsPostBack Then
             'veri kaynağıyla bağlantılar
            DataList1.DataSource = textes
            DataGrid1.DataSource = textes
            Repeater1.DataSource = textes
            Page.DataBind()
        End If
    End Sub

    Private Sub DataList1_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles DataList1.ItemCommand
         ' [datalist]'in satırlarından birinde bir olay meydana geldi
        lblInfo.Text = "Vous avez cliqué sur le bouton [" + e.CommandName + "] de l'élément [" + e.Item.ItemIndex.ToString + "] du composant [DataList]"
    End Sub

    Private Sub Repeater1_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles Repeater1.ItemCommand
         ' [repeater]'in satırlarından birinde bir olay meydana geldi
        lblInfo.Text = "Vous avez cliqué sur le bouton [" + e.CommandName + "] de l'élément [" + e.Item.ItemIndex.ToString + "] du composant [Repeater]"
    End Sub

    Private Sub DataGrid1_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.ItemCommand
         ' [datagrid]'in satırlarından birinde bir olay meydana geldi
        lblInfo.Text = "Vous avez cliqué sur le bouton [" + e.CommandName + "] de l'élément [" + e.Item.ItemIndex.ToString + "] de la page [" + DataGrid1.CurrentPageIndex.ToString() + "] du composant [DataGrid]"
    End Sub

    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
         ' sayfa değişimi
        With DataGrid1
            .CurrentPageIndex = e.NewPageIndex
            .DataSource = textes
            .DataBind()
        End With
    End Sub
End Class

Açıklamalar:

  • [textes] veri kaynağı, basit bir karakter dizisi tablosudur. Bu veri kaynağı, sayfada bulunan üç bileşene bağlanacaktır
  • Bu bağlantı, ilk sorgu sırasında [Page_Load] yordamında kurulur. Sonraki sorgularda, üç bileşen de [VIEW_STATE] mekanizması aracılığıyla değerlerini alır.
  • Üç bileşenin de [ItemCommand] olayı için bir işleyicisi vardır. Bu olay, bileşenin satırlarından birinde bir düğmeye veya bağlantıya tıklandığında gerçekleşir. İşleyici iki bilgi alır:
    • kaynak: olayı tetikleyen nesnenin (düğme veya bağlantı) referansı
    • a: duruma göre [DataListCommandEventArgs], [RepeaterCommandEventArgs], [DataGridCommandEventArgs] türündeki olayla ilgili bilgi. a argümanı çeşitli bilgiler içerir. Burada bunlardan ikisi bizim için önemlidir:
      • a.Item: Olayın meydana geldiği satırı temsil eder; türü [DataListItem], [DataGridItem] veya [RepeaterItem] olabilir. Tam türü ne olursa olsun, [Item] öğesi, ait olduğu kapsayıcının [Item] satır numarasını belirten [ItemIndex] özniteliğine sahiptir. Burada bu satır numarasını görüntülüyoruz
      • a.CommandName: Olayı tetikleyen düğmenin (Button, LinkButton, ImageButton) [CommandName] özniteliğidir. Bu bilgi, bir önceki bilgiyle birleştirildiğinde, [ItemCommand] olayının kaynağının konteynerdeki hangi düğme olduğunu belirlememizi sağlar

9.3. Uygulama - abonelik listesinin yönetimi

Artık bir veri konteyneri içinde meydana gelen olayları nasıl yakalayacağımızı bildiğimize göre, bunları nasıl yönetebileceğimizi gösteren bir örnek sunacağız.

9.3.1. Giriş

Sunulan uygulama, bir e-posta listesine abonelik uygulamasını simüle etmektedir. Bu listeler, üç sütunlu bir [DataTable] nesnesi ile tanımlanmaktadır:

ad
tür
rol
id
dize
birincil anahtar
thème
dize
liste temasının adı
description
dize
liste tarafından ele alınan konuların açıklaması

Örneğimizi gereksiz yere karmaşıklaştırmamak için, yukarıdaki [DataTable] nesnesi kod yoluyla rastgele oluşturulacaktır. Gerçek bir uygulamada ise, bu nesne muhtemelen bir veri erişim sınıfının yöntemi tarafından sağlanacaktır. Liste tablosunun oluşturulması [Application_Start] prosedüründe gerçekleştirilecek ve ortaya çıkan tablo uygulamaya yerleştirilecektir. Buna [dtThèmes] tablosu adını vereceğiz. Kullanıcı, bu tablodaki bazı temalara abone olacak. Aboneliklerinin listesi yine burada, [dtAbonnements] adlı ve yapısı aşağıdaki gibi olan [DataTable] nesnesinde saklanacaktır:

ad
tür
rol
id
dize
birincil anahtar
thème
dize
liste temasının adı

Tek sayfalık uygulama şu şekildedir:

numara
ad
tür
özellikler
rol
1
dgThèmes
DataGrid
 
abone olunabilecek e-posta listeleri
2
dlAbonnements
DataList
 
Kullanıcının yukarıdaki listelere abonelikleri listesi
3
panelInfos
panel
 
kullanıcının seçtiği konuya ilişkin bilgi paneli ve bir bağlantı [Plus d'informations]
4
lblThème
Etiket
[panelInfos]'in bir parçasıdır
tema adı
5
lblDescription
Etiket
[panelInfos]'in bir parçasıdır
tema açıklaması
6
lblInfo
Etiket
 
uygulama bilgilendirme mesajı

Örneğimiz, [DataGrid] ve [DataList] bileşenlerinin kullanımını, özellikle de bu veri konteynerlerinin satırlarında meydana gelen olayların yönetimini göstermeyi amaçlamaktadır. Bu nedenle uygulamada, örneğin kullanıcının yaptığı seçimleri bir veritabanına kaydedecek bir onay düğmesi bulunmamaktadır. Yine de bu örnek gerçekçi bir senaryoyu yansıtmaktadır. Bu örnek, bir kullanıcının ürünleri (abonelikleri) sepetine eklediği bir e-ticaret uygulamasına oldukça yakındır.

9.3.2. Çalışma Şekli

Kullanıcının gördüğü ilk ekran şöyledir:

Kullanıcı, bir konu hakkında bilgi almak için [Plus d'informations] bağlantılarına tıklar. Bu bilgiler [panelInfos] sayfasında görüntülenir:

Image

Bir konuya abone olmak için [S'abonner] bağlantılarına tıklar. Seçilen konular, [dlAbonnements] bileşenine eklenir:

Image

Kullanıcı, halihazırda abone olduğu bir listeye tekrar abone olmak isteyebilir. Bu durumda bir mesajla bilgilendirilir:

Image

Son olarak, kullanıcı yukarıdaki [Retirer] düğmelerini kullanarak temalardan birinin aboneliğini iptal edebilir. Herhangi bir onay istenmez. Kullanıcı kolayca yeniden abone olabileceğinden, bu durumda onay istenmesi gerekli değildir. [thème1] aboneliğinin kaldırılmasından sonraki görünüm şöyledir:

Image

9.3.3. Veri kapsayıcılarının yapılandırılması

[DataGrid] türündeki [dgThèmes] bileşeni, [DataTable] türündeki bir kaynağa bağlıdır. Biçimlendirme, [DataGrid] bileşeninin özellikler panelindeki [Mise en forme automatique] bağlantısı kullanılarak yapılmıştır. Özelliklerinin tanımı ise aynı paneldeki [Générateur de proprités] bağlantısı kullanılarak yapılmıştır. Oluşturulan kod şöyledir (biçimlendirme kodu hariç tutulmuştur):


                        <asp:datagrid id="dgThèmes" AutoGenerateColumns="False" AllowPaging="True" PageSize="5" 
                            runat="server">
                            <ItemStyle ...></ItemStyle>
                            <HeaderStyle ...></HeaderStyle>
                            <FooterStyle ...></FooterStyle>
                            <Columns>
                                <asp:BoundColumn DataField="th&#232;me" HeaderText="Th&#232;me"></asp:BoundColumn>
                                <asp:ButtonColumn Text="Plus d'informations" CommandName="infos"></asp:ButtonColumn>
                                <asp:ButtonColumn Text="S'abonner" CommandName="abonner"></asp:ButtonColumn>
                            </Columns>
                            <PagerStyle HorizontalAlign="Center" ... Mode="NumericPages"></PagerStyle>
                        </asp:datagrid>

Aşağıdaki noktalara dikkat edilmelidir:

AutoGenerateColumns=false
<columns>...</columns> bölümünde görüntülenecek sütunları kendimiz belirliyoruz
AllowPaging=true
PageSize=5
verilerin sayfalandırılması için
<asp:BoundColumn>
[DataGrid]'in [thème] (HeaderText) sütununu tanımlar; bu sütun, veri kaynağındaki (DataField) [thème] sütunuyla ilişkilendirilecektir
<asp:ButtonColumn>
iki düğme (veya bağlantı) sütunu tanımlar. Aynı satırdaki iki bağlantıyı birbirinden ayırt etmek için [CommandName] özelliğini kullanacağız.

[DataGrid] bileşeni henüz tam olarak yapılandırılmamıştır. Bu yapılandırma, denetleyici kodunda gerçekleştirilecektir.

[DataList] türündeki [dlAbonnements] bileşeni, [DataTable] türündeki bir kaynağa bağlıdır. Biçimlendirme, [DataList] bileşeninin özellikler panelindeki [Mise en forme automatique] bağlantısı kullanılarak yapılmıştır. Özelliklerinin tanımı ise doğrudan sunum kodunda yapılmıştır. Bu kod şöyledir (biçimlendirme kodu hariç tutulmuştur):


                        <asp:DataList id="dlAbonnements" ... runat="server" >
                            <HeaderTemplate>
                                <div align="center">
                                    Vos abonnements</div>
                            </HeaderTemplate>
                            <ItemStyle ...></ItemStyle>
                            <ItemTemplate>
                                <TABLE>
                                    <TR>
                                        <TD><%#Container.DataItem("tema")%></TD>
                                        <TD>
                                            <asp:Button id="lnkRetirer" CommandName="retirer" runat="server" Text="Retirer" /></TD>
                                    </TR>
                                </TABLE>
                            </ItemTemplate>
                            <HeaderStyle ...></HeaderStyle>
                        </asp:DataList>
<HeaderTemplate>
[DataList] başlığının metnini tanımlar
<ItemTemplate>
[DataList]'in geçerli öğesini tanımlar - burada iki sütunlu ve bir satırlı bir tablo yerleştirilmiştir. İlk hücre, kullanıcının abone olmak istediği temanın adını içerecek, diğeri ise seçimini iptal etmesini sağlayan [Retirer] düğmesini içerecektir.

9.3.4. Görünüm sayfası

[main.aspx] sunum kodu şu şekildedir:


<%@ page src="main.aspx.vb" inherits="main" autoeventwireup="false" %>
<HTML>
    <HEAD>
        <title></title>
    </HEAD>
    <body>
        <P>Indiquez les thèmes auxquels vous voulez vous abonner :</P>
        <HR width="100%" SIZE="1">
        <form runat="server">
            <table>
                <tr>
                    <td vAlign="top">
                        <asp:datagrid id="dgThèmes" ... runat="server">
...
                        </asp:datagrid>
                  </td>
                    <td vAlign="top">
                        <asp:DataList id="dlAbonnements" ... runat="server" GridLines="Horizontal" ShowFooter="False">
....
                        </asp:DataList>
                   </td>
                    <td vAlign="top">
                        <asp:Panel ID="panelInfo" Runat="server" EnableViewState="False">
                            <TABLE>
                                <TR>
                                    <TD bgColor="#99cccc">
                                        <asp:Label id="lblThème" runat="server"></asp:Label></TD>
                                </TR>
                                <TR>
                                    <TD bgColor="#ffff99">
                                        <asp:Label id="lblDescription" runat="server"></asp:Label></TD>
                                </TR>
                            </TABLE>
                        </asp:Panel>
                    </td>
                </tr>
            </table>
            <P>
                <asp:Label id="lblInfo" runat="server" EnableViewState="False"></asp:Label></P>
        </form>
    </body>
</HTML>

9.3.5. Denetim dosyaları

Denetim, [global.asax] ve [main.aspx] dosyaları arasında paylaştırılmıştır. [global.asax] dosyası şöyledir:

<%@ Application src="global.asax.vb" inherits="global" %>

İlgili [global.asax.vb] dosyası aşağıdaki kodu içerir:


Imports System.Web
Imports System.Web.SessionState
Imports System.Data
Imports System

Public Class global
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' veri kaynağı başlatılıyor
        Dim thèmes As New DataTable
        ' sütunlar
        With thèmes.Columns
            .Add("id", GetType(System.Int32))
            .Add("thème", GetType(System.String))
            .Add("description", GetType(System.String))
        End With
        ' id sütunu birincil anahtar olacak
        thèmes.Constraints.Add("cléprimaire", thèmes.Columns("id"), True)
        ' satırlar
        Dim ligne As DataRow
        For i As Integer = 0 To 10
            ligne = thèmes.NewRow
            ligne.Item("id") = i.ToString
            ligne.Item("thème") = "thème" + i.ToString
            ligne.Item("description") = "description du thème " + i.ToString
            thèmes.Rows.Add(ligne)
        Next
        ' veri kaynağı uygulamaya ekleniyor
        Application("thèmes") = thèmes
    End Sub

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' oturumun başlangıcı - boş bir abonelik tablosu oluşturulur
        Dim dtAbonnements As New DataTable
        With dtAbonnements
            ' sütunlar
            .Columns.Add("id", GetType(String))
            .Columns.Add("thème", GetType(String))
            ' birincil anahtar
            .PrimaryKey = New DataColumn() {.Columns("id")}
        End With
        ' tablo oturuma eklenir
        Session.Item("abonnements") = dtAbonnements
    End Sub

End Class

Uygulama ilk isteğini aldığında yürütülen [Application_Start] prosedürü, abone olunabilecek temaların [DataTable]'sini oluşturur. Bu, kod kullanılarak rastgele oluşturulur. Daha önce karşılaştığımız tekniği hatırlayalım. Sırasıyla şunlar oluşturulur:

  • yapısı ve verisi olmayan boş bir [DataTable] nesnesi
  • tablonun yapısı, sütunları (adları ve içerdikleri veri türleri) tanımlanarak oluşturulur
  • yararlı verileri temsil eden tablo satırları

Burada bir birincil anahtar ekledik. Birincil anahtar görevi gören sütun "id"dir. Bunu ifade etmenin çeşitli yolları vardır. Burada bir kısıtlama kullandık. SQL'te kısıtlama, bir satırın verilerinin tabloya eklenebilmesi için uyması gereken bir kuraldır. Her türlü kısıtlama mümkündür. "Primary Key" kısıtlaması, uygulandığı sütunun değerlerinin benzersiz ve boş olmaması gerektiğini zorunlu kılar. Bir birincil anahtar, aslında birden fazla sütunun değerlerini içeren bir ifadeden oluşabilir. [DataTable].Constraints, belirli bir tablonun kısıtlamalarının koleksiyonudur. Bir kısıtlama eklemek için [DataTable.Constraints.Add] yöntemi kullanılır. Bu yöntemin birkaç imzası vardır. Burada [Add(Byval nom as String, Byval colonne as DataColumn, Byval cléPrimaire as Boolean)] yöntemi kullanılmıştır:

nom
kısıtlamanın adı - herhangi bir isim olabilir
colonne
birincil anahtar olacak sütun - [DataColumn] türü
cléPrimaire
[vrai] olarak ayarlanmalıdır; böylece [colonne] birincil anahtar olur. Eğer [cléPrimaire=false] ise, sadece [colonne] üzerinde benzersiz değer kısıtlaması vardır

"id" adlı sütunu [dtAbonnements] tablosunun birincil anahtarı yapmak için şu şekilde yazılır:

dtAbonnements.Constraints.Add("xxx",dtAbonnements.Columns("id"),true)

[Session_Start] prosedürü, uygulama bir müşteriden ilk isteği aldığında yürütülür. Bu prosedür, her müşteriye özgü nesneler oluşturmak için kullanılır; bu nesneler, müşterinin farklı istekleri boyunca kalıcı olmalıdır. Prosedür, müşterinin aboneliklerini içeren [DataTable] tablosunu oluşturur. Başlangıçta bu tablo boş olduğu için sadece yapısı oluşturulur. Tablo, istekler geldikçe doldurulacaktır. Burada da "id" sütunu birincil anahtar olarak kullanılır. Bu kısıtlamayı tanımlamak için farklı bir teknik kullanılmıştır:

[DataTable].PrimaryKey
birincil anahtarı oluşturan sütunların dizisidir – burada tek elemanlı bir dizi tanımlanmıştır: "id" adlı sütun

Müşterinin isteği [main.aspx] denetleyicisine ulaştığında, iki [DataTable] nesnesi uygulamada tema tablosu için, oturumda ise abonelik tablosu için hazırdır. [main.aspx.vb] denetleyicisi şöyledir:


Imports System.Data

Public Class main
    Inherits System.Web.UI.Page

    Protected WithEvents dgThèmes As System.Web.UI.WebControls.DataGrid
    Protected WithEvents lblThème As System.Web.UI.WebControls.Label
    Protected WithEvents lblDescription As System.Web.UI.WebControls.Label
    Protected WithEvents dlAbonnements As System.Web.UI.WebControls.DataList
    Protected WithEvents lblInfo As System.Web.UI.WebControls.Label
    Protected WithEvents panelInfo As System.Web.UI.WebControls.Panel

    Protected dtThèmes As DataTable
    Protected dtAbonnements As DataTable

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
    End Sub

    Private Sub liaisons()
...
    End Sub

    Private Sub dgThèmes_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles dgThèmes.PageIndexChanged
...
    End Sub

    Private Sub dgThèmes_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles dgThèmes.ItemCommand
...
    End Sub

    Private Sub infos(ByVal id As String)
...
    End Sub

    Private Sub abonner(ByVal id As String)
...
    End Sub

    Private Sub dlAbonnements_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles dlAbonnements.ItemCommand
...
    End Sub
End Class

[Page_Load] prosedürünün temel işlevi şudur:

  • uygulamada ve oturumda bulunan [dtThèmes] ve [dtAbonnements] tablolarını, sayfanın tüm yöntemlerinin kullanabilmesi için almak
  • bu iki kaynaktan gelen verileri ilgili konteynerlerle eşleştirmek. Bu işlem yalnızca ilk istekte gerçekleştirilir. Sonraki isteklerde eşleştirme her zaman yapılmaz; yapılması gerektiğinde ise bazen [Page_Load]'ten sonra gerçekleşecek bir olayı beklemek gerekebilir.

[Page_Load] kodunun içeriği şöyledir:


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' veri kaynakları alınır
        dtThèmes = CType(Application("thèmes"), DataTable)
        dtAbonnements = CType(Session("abonnements"), DataTable)
        ' veri bağlantısı
        If Not IsPostBack Then
            liaisons()
        End If
        ' bazı bilgiler gizlenmiştir
        panelInfo.Visible = False
    End Sub

    Private Sub liaisons()
        'veri kaynağını [datagrid] bileşenine bağlanıyor
        With dgThèmes
            .DataSource = dtThèmes
            .DataKeyField = "id"
        End With
        ' veri kaynağı [datalist] bileşenine bağlanıyor
        With dlAbonnements
            .DataSource = dtAbonnements
            .DataKeyField = "id"
        End With
        ' veriler bileşenlere atanıyor
        Page.DataBind()
    End Sub

[liaisons] prosedüründe, [DataList] ve [DataGrid] bileşenlerinin [DataKeyField] özelliğini kullanarak, konteyner satırlarını benzersiz bir şekilde tanımlamak için kullanılacak veri kaynağındaki sütunu tanımlıyoruz. Genellikle bu sütun, veri kaynağının birincil anahtarıdır ancak bu zorunlu değildir. Sütunda yinelenen ve boş değerler olmaması yeterlidir. [dgThèmes] konteyneri için, [dtThèmes] kaynağındaki "id" sütunu birincil anahtar olarak kullanılacak ve [dlAbonnements] konteyneri için ise [dtAbonnements] kaynağındaki "id" sütunu kullanılacaktır. Bir konteyner için birincil anahtar görevi gören sütunun, konteyner tarafından görüntülenmesi gerekmez. Burada, her iki konteyner de birincil anahtar sütununu görüntülememektedir. Bir konteynerin birincil anahtara sahip olmasının önemi, bir olayın meydana geldiği konteyner satırıyla ilişkili bilgileri veri kaynağında kolayca bulabilmeyi sağlamasıdır. Nitekim, bir olayın meydana geldiği konteyner satırından yola çıkarak, ona bağlı veri kaynağındaki karşılık gelen satır üzerinde işlem yapmak sıkça karşılaşılan bir durumdur. Birincil anahtar bu işi kolaylaştırır.

[DataGrid]'in sayfalandırması geleneksel şekilde yönetilir:


    Private Sub dgThèmes_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles dgThèmes.PageIndexChanged
        ' sayfa değişimi
        dgThèmes.CurrentPageIndex = e.NewPageIndex
        ' bağlantı
        liaisons()
    End Sub

[Plus d'informations] ve [S'abonner] bağlantıları üzerindeki işlemler, [dgThèmes_ItemCommand] prosedürü tarafından yönetilir:


    Private Sub dgThèmes_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles dgThèmes.ItemCommand
        ' [datagrid] bileşeninin bir satırında olay
        Dim commande As String = e.CommandName
        Select Case commande
            Case "infos"
                infos(dgThèmes.DataKeys(e.Item.ItemIndex))
            Case "abonner"
                abonner(dgThèmes.DataKeys(e.Item.ItemIndex))
        End Select
        ' bağlantı
        liaisons()
    End Sub

İki bağlantıyı birbirinden ayırt etmek için her ikisinin de [CommandName] özniteliğine sahip olması kullanılır. Bu özniteliğin değerine göre, olayın meydana geldiği [DataGrid] öğesiyle ilişkili "id" anahtarını her iki durumda da aktararak [infos] veya [abonner] yordamını çağırıyoruz. Bu bilgiyle donatılmış olan [info] prosedürü, kullanıcı tarafından seçilen temaya ait bilgileri görüntüler:


    Private Sub infos(ByVal id As String)
        ' anahtar kimliği ile ilgili bilgiler
        ' anahtara karşılık gelen [datatable] satırı alınır
        Dim ligne As DataRow
        ligne = dtThèmes.Rows.Find(id)
        If Not ligne Is Nothing Then
            ' bilgiyi görüntülenir
            lblThème.Text = CType(ligne("thème"), String)
            lblDescription.Text = CType(ligne("description"), String)
            panelInfo.Visible = True
        End If
    End Sub

[dtThèmes] tablosu bir birincil anahtara sahip olduğundan, [dtThèmes.Rows.Find("P")] yöntemi, P birincil anahtarına sahip satırı bulmamızı sağlar. Eğer bulunursa, bir [DataRow] nesnesi elde edilir. Burada, [id] parametresi verildiğinde, [id] birincil anahtarına sahip satırı aramamız gerekir. Satır bulunursa, bu satırdaki [thème] ve [description] bilgileri bilgi paneline eklenir ve ardından bu panel görünür hale getirilir.

[abonner(id)] prosedürü, [id] anahtar temasını abonelik listesine eklemelidir. Kod şu şekildedir:


    Private Sub abonner(ByVal id As String)
         ' anahtar kimliği temasına abonelik
         ' anahtara karşılık gelen [datatable] satırını alırız
        Dim ligne As DataRow
        ligne = dtThèmes.Rows.Find(id)
        If Not ligne Is Nothing Then
             ' zaten abone olup olmadığımız kontrol ediliyor
            Dim abonnement As DataRow
            abonnement = dtAbonnements.Rows.Find(id)
            If Not abonnement Is Nothing Then
                 ' hata bildirilir
                lblInfo.Text = "Vous êtes déjà abonné au thème [" + ligne("thème") + "]"
            Else
                 ' temayı aboneliklere eklenir
                abonnement = dtAbonnements.NewRow
                abonnement("id") = id
                abonnement("thème") = ligne("thème")
                dtAbonnements.Rows.Add(abonnement)
                 ' bağlantılar kurulur
                liaisons()
            End If
        End If
    End Sub

[dtThèmes] tema listesinde, öncelikle [id] anahtar satırı aranır. Bu satır bulunursa, temanın abonelikler listesinde zaten mevcut olup olmadığı kontrol edilir; böylece temanın iki kez kaydedilmesi önlenir. Eğer mevcutsa, bir hata mesajı görüntülenir. Aksi takdirde, [dtAbonnements] tablosuna yeni bir abonelik eklenir ve veri listesi bileşenleri ile ilgili kaynakları arasında bağlantılar kurulur.

Kullanıcı bir [Retirer] düğmesine tıkladığında, [dtAbonnements] tablosundan bir öğe silinmelidir. Bu, aşağıdaki prosedürle gerçekleştirilir:


    Private Sub dlAbonnements_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles dlAbonnements.ItemCommand
        ' bir aboneliği kaldırıyoruz
        Dim commande As String = e.CommandName
        If commande = "retirer" Then
            ' [datatable] aboneliği kaldırılıyor
            With dtAbonnements.Rows
                .Remove(.Find(dlAbonnements.DataKeys(e.Item.ItemIndex)))
            End With
            ' bağlantılar
            liaisons()
        End If
    End Sub

Öncelikle, olayın kaynağı olan öğenin [CommandName] özniteliği kontrol edilir. Aslında bu oldukça gereksizdir, çünkü [Retirer] düğmesi, [DataList] bileşeninde bir olay oluşturabilen tek denetimdir. Dolayısıyla herhangi bir belirsizlik yoktur. Bir [DataTable] nesnesinden bir satırı silmek için, parametre olarak geçirilen [DataRow] türündeki satırı tablodan kaldıran [DataList.Remove(DataRow)] yöntemi kullanılır. Bu satır, aranan satırın birincil anahtarı geçirilen [DataList.Find] yöntemi tarafından bulunur. Satır silindikten sonra, veriler bileşenlere bağlanır

9.4. Sayfalandırılmış bir [DataList]'i yönetme

Kullanıcının abonelik listesini temsil eden [DataList] bileşenini sayfalandırmak için önceki örneği tekrar ele alıyoruz. [DataGrid] bileşeninin aksine, [DataList] bileşeni sayfalandırma için herhangi bir kolaylık sunmamaktadır. Bunun uygulanmasının karmaşık olduğunu göreceğiz; bu da [DataGrid] bileşenindeki otomatik sayfalandırmanın değerini daha iyi anlamamızı sağlayacaktır.

9.4.1. Çalışma Prensibi

Tek fark, [DataList] bileşenindeki sayfalandırmadır. Sayfalar iki abonelik içerecektir. Kullanıcının beş aboneliği varsa, üç sayfa olacaktır. İlki şu şekilde olacaktır:

Image

İkinci sayfaya [Suivant] bağlantısı üzerinden ulaşılır:

Image

Üçüncü sayfa:

Image

[Précédent] ve [Suivant] bağlantılarının, sırasıyla mevcut sayfanın öncesinde ve sonrasında bir sayfa olması durumunda görünür olduğu dikkatinizi çekecektir.

9.4.2. Görselleştirme kodu

[Précédent] ve [Suivant] bağlantıları, [DataList]'e bir <FooterTemplate> etiketi eklenerek elde edilir:


                        <asp:datalist id="dlAbonnements" runat="server" ...>
....
                            <FooterTemplate>
                                <asp:LinkButton id="lnkPrecedent" runat="server" CommandName="precedent">Précédent</asp:LinkButton>
                                <asp:LinkButton id="lnkSuivant" runat="server" CommandName="suivant">Suivant</asp:LinkButton>
                            </FooterTemplate>
....
                        </asp:datalist>

9.4.3. Denetim kodu

İlgili dosya [global.asax.vb] şu şekilde değişmektedir:

...

Public Class global
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
...
    End Sub

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
         ' oturumun başlangıcı - boş bir abonelik tablosu oluşturuluyor
        Dim dtAbonnements As New DataTable
        With dtAbonnements
             ' sütunlar
            .Columns.Add("id", GetType(String))
            .Columns.Add("thème", GetType(String))
             ' birincil anahtar
            .PrimaryKey = New DataColumn() {.Columns("id")}
        End With
         ' tablo oturuma eklenir
        Session.Item("abonnements") = dtAbonnements
         ' geçerli sayfa 0'dır
        Session.Item("pAC") = 0
         ' bu sayfadaki abonelik sayısı 0'dır
        Session.Item("nbAC") = 0
    End Sub

[dtAbonnements] abonelik tablosunun yanı sıra, oturuma iki bilgi daha eklenir:

pAC
[Integer] türü - bu, son istek sırasında görüntülenen geçerli sayfanın numarasıdır
nbAC
[Integer] türü - önceki geçerli sayfada görüntülenen satır sayısı

Bir oturumun başlangıcında, mevcut sayfanın numarası ve bu sayfadaki satır sayısı sıfırdır.

[main.aspx.vb] denetleyicisi şu şekilde değişir:

....

Public Class main
    Inherits System.Web.UI.Page

....

     ' uygulama verileri
    Protected dtThèmes As DataTable
    Protected dtAbonnements As DataTable
    Protected dtPA As DataTable    ' la page d'abonnements affichée
    Protected Const nbAP As Integer = 2    ' nbre abonnements par page
    Protected pAC As Integer    ' page abonnement courant
    Protected nbAC As Integer    ' nombre d'abonnements dans page courante

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
    End Sub

    Private Sub terminer()
...
    End Sub

    Private Sub dgThèmes_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles dgThèmes.PageIndexChanged
...
    End Sub

    Private Sub dgThèmes_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles dgThèmes.ItemCommand
...
    End Sub

    Private Sub infos(ByVal id As String)
...
    End Sub

    Private Sub abonner(ByVal id As String)
...
    End Sub

    Private Sub dlAbonnements_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles dlAbonnements.ItemCommand
...
    End Sub

    Private Sub changePAC()
...
    End Sub

    Private Sub setLiens(ByVal ctl As Control, ByVal blPrec As Boolean, ByVal blSuivant As Boolean)
...
    End Sub
End Class

Burada aboneliklerin sayfalandırılmasıyla ilgili yeni veriler tanımlanmaktadır:


    Protected dtPA As DataTable    ' la page d'abonnements affichée
    Protected Const nbAP As Integer = 2    ' nbre abonnements par page
    Protected pAC As Integer    ' page abonnement courant
    Protected nbAC As Integer    ' nombre d'abonnements dans page courante

Bu bilgilerin bir kısmı oturumda depolanır ve [Page_Load] prosedüründeki her istekte geri çağrılır:


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' veri kaynakları alınmaktadır
        dtThèmes = CType(Application("thèmes"), DataTable)
        dtAbonnements = CType(Session("abonnements"), DataTable)
        ' ve aboneliklerin görüntülenme bilgileri
        pAC = CType(Session("pAC"), Integer)
        nbAC = CType(Session("nbAC"), Integer)
        ' veri bağlantısı
        If Not IsPostBack Then
            ' boş bir abonelik listesinin görüntülenmesi
            terminer()
        End If
        ' bazı bilgiler gizlenir
        panelInfo.Visible = False
    End Sub

[pAC] ve [nbAC] numaralı bilgiler, önceki istekte görüntülenen abonelik sayfası hakkındaki bilgilerdir:

pAC
Bu, önceki istek sırasında görüntülenen mevcut sayfanın numarasıdır
nbAC
bu mevcut sayfada görüntülenen satır sayısı

[terminer] yöntemi, önceki uygulamadaki [liaisons] yönteminde olduğu gibi bileşenleri veri kaynaklarıyla ilişkilendirir. Buradaki yenilik, [DataList] yönteminin, görüntülenecek abonelik sayfası olan [dtPA] tablosuyla bağlanmasıdır:


    Private Sub terminer()
        ' veri kaynağını [datagrid] bileşenine bağlama
        With dgThèmes
            .DataSource = dtThèmes
            .DataKeyField = "id"
        End With
        ' geçerli sayfa pAC dikkate alınarak abonelikler sayfası [datalist] bileşenine bağlanır
        changePAC()
        ' p sayfasını görüntüler
        With dlAbonnements
            .DataSource = dtPA
            .DataKeyField = "id"
        End With
        ' veriler bileşenlere atanır
        Page.DataBind()
        ' [datalist]'in [précédent] ve [suivant] bağlantılarının yönetimi
        Dim blprec As Boolean = pAC <> 0
        Dim blsuivant As Boolean = pAC <> (dtAbonnements.Rows.Count - 1) \ nbAP
        Dim nbLiensTrouvés As Integer = 0
        setLiens(dlAbonnements, blprec, blsuivant, nbLiensTrouvés)
        ' geçerli sayfa bilgileri oturuma kaydedilir
        Session("pAC") = pAC
        Session("nbAC") = dtPA.Rows.Count
    End Sub

Aşağıdaki hususlara dikkat edilmelidir:

  • [dtPA] kaynağı, görüntülenecek geçerli sayfa numarası [pAC]'e bağlıdır. [pAC] değişkeni, sınıfın genel bir değişkenidir ve bu geçerli sayfa numarasını değiştirmekle görevli yöntemler tarafından kullanılır. [changePAC] yöntemi, [dlAbonnements] bileşenine bağlanacak olan [dtPA] tablosunu oluşturma işini gerçekleştirir.
  • [setLiens] yöntemi, görüntülenen geçerli sayfa [pAC]'in öncesinde ve sonrasında bir sayfa olup olmadığına bağlı olarak [Précédent] ve [Suivant] bağlantılarını gösterme veya gizleme görevini üstlenir. Dört parametresi vardır:
    • [dlAbonnements]: Bu, iki bağlantıyı bulmak için kontrol ağacını inceleyeceğimiz [DataList] kontrolüdür. Aslında, bu iki bağlantı [DataList]'in altbilgisi gibi belirli bir konumda bulunsa da, bunlara doğrudan başvurmanın basit bir yolu yok gibi görünüyor. Her halükarda, burada böyle bir yol bulunamadı.
    • [blPrecedent]: [Precedent] bağlantısının [visible] özelliğine atanacak boole değeri – geçerli sayfa 0'dan farklıysa "true" olur
    • [blSuivant]: [Suivant] bağlantısının [visible] özelliğine atanacak boole değeri - geçerli sayfa abonelik listesinin son sayfası değilse true olur
    • [nbLiensTrouvés]: Bulunan bağlantı sayısını sayan bir çıktı parametresidir. Bu sayı ikiye eşit olduğunda yöntem sona erer.
  • [pAC] ve [nbAC] bilgileri, bir sonraki istek için oturumda saklanır.

[changePAC] yöntemi, [dlAbonnements] bileşenine bağlanacak olan [dtPA] tablosunu oluşturur. Bunu, görüntülenecek geçerli sayfanın [pAC] numarasına göre yapar. [dtPA] tablosu, [dtAbonnements] abonelik tablosunun belirli satırlarını görüntülemelidir. Bu tablonun oturumda saklandığını ve istekler doğrultusunda güncellendiğini (artırıldığını veya azaltıldığını) hatırlatmak isteriz. İlk olarak, [dtPA] tablosunun görüntülemesi gereken [dtAbonnements] tablosundaki satır numaraları aralığını [premier,dernier] olarak belirleyelim:


    Private Sub changePAC()
        ' pAC sayfasını aboneliklerin geçerli sayfası yapar
        ' [datalist] sayfalarının yönetimi
        Dim nbAbonnements = dtAbonnements.Rows.Count
        Dim dernièrePage = (nbAbonnements - 1) \ nbAP
        ' ilk ve son abonelik
        If pAC < 0 Then pAC = 0
        If pAC > dernièrePage Then pAC = dernièrePage
        Dim premier As Integer = pAC * nbAP
        Dim dernier As Integer = (pAC + 1) * nbAP - 1
        If dernier > nbAbonnements - 1 Then dernier = nbAbonnements - 1

Bu işlem tamamlandıktan sonra, [dtPA] tablosunu oluşturabiliriz. Öncelikle [id,thème] tablosunun yapısını tanımlıyoruz, ardından daha önce hesaplanan [premier,dernier] aralığı içinde yer alan [dtAbonnements] tablosundaki satırları bu tabloya kopyalayarak dolduruyoruz.


         ' dtpa veri tablosunun oluşturulması
        dtPA = New DataTable
        With dtPA
            ' sütunlar
            .Columns.Add("id", GetType(String))
            .Columns.Add("thème", GetType(String))
            ' birincil anahtar
            .PrimaryKey = New DataColumn() {.Columns("id")}
        End With
        Dim abonnement As DataRow
        For i As Integer = premier To dernier
            abonnement = dtPA.NewRow
            With abonnement
                .Item("id") = dtAbonnements.Rows(i).Item("id")
                .Item("thème") = dtAbonnements.Rows(i).Item("thème")
            End With
            dtPA.Rows.Add(abonnement)
        Next
    End Sub

[changePAC] yönteminin sonunda, [dtPA] tablosu oluşturulmuştur ve bu tablo, [DataList] bileşenine bağlanabilir. Bu işlem, [terminer] yönteminde gerçekleştirilir. Aynı yöntemde, [DataList]'in [Précédent] ve [Suivant] bağlantılarının durumunu belirlemek için [setLiens] yordamı kullanılır. Bu yordamın kodu şöyledir:


    Private Sub setLiens(ByVal ctl As Control, ByVal blPrec As Boolean, ByVal blSuivant As Boolean, ByRef nbLiensTrouvés As Integer)
        ' [précédent] ve [suivant] bağlantıları aranıyor
        ' [datalist] kontrol ağacında
        ' Tüm bağlantılar bulundu mu?
        If nbLiensTrouvés = 2 Then Exit Sub
        ' alt denetimlerin incelenmesi
        Dim c As Control
        For Each c In ctl.Controls
            ' önce derinlemesine çalışıyoruz - bağlantılar ağacın en altında
            setLiens(c, blPrec, blSuivant, nbLiensTrouvés)
            ' [Précédent] bağlantısı mı?
            If c.ID = "lnkPrecedent" Then
                CType(c, LinkButton).Visible = blPrec
                nbLiensTrouvés += 1
            End If
            ' [Suivant] bağlantısı mı?
            If c.ID = "lnkSuivant" Then
                CType(c, LinkButton).Visible = blSuivant
                nbLiensTrouvés += 1
            End If
        Next
    End Sub

İşlem özyinelemelidir. Öncelikle, [dlAbonnements] bileşeninin alt denetimleri arasında, iki sayfalandırma bağlantısının kimlikleri (ID özniteliği) olan [lnkPrecedent] ve [lnkSuivant] adlı bileşenleri arar. Bu bağlantılar, kontrol ağacının en altında bulundukları için, arama önce ağacın en altından başlar. Bir bağlantı bulunur bulunmaz, [nbLiensTrouvés] sayacı artırılır ve bağlantının [visible] özelliği, yordama parametre olarak geçirilen bir değerle doldurulur. İki bağlantı da bulunur bulunmaz, kontrol ağacı artık taranmaz ve özyinelemeli yordam sona erer.

[changePAC] yönteminin, [dlAbonnements] bileşeni için [dtPA] veri kaynağını ayarlarken, görüntülenecek geçerli sayfanın [pAC] numarasıyla çalıştığını belirtmiştik. Bu numarayı değiştiren birkaç prosedür vardır:


    Private Sub abonner(ByVal id As String)
        ' anahtar kimliği temasına abonelik
..
            ' tema aboneliklere ekleniyor
..
             ' geçerli sayfa numarasının güncellenmesi - artık son sayfadır
            pAC = (dtAbonnements.Rows.Count - 1) \ nbAP
             ' veri bağlantıları
            terminer()
        End If
    End Sub

Bir abonelik eklendikten sonra, bu abonelik abonelik listesinin en sonuna eklenir. Bu nedenle, kullanıcının yapılan eklemeyi görebilmesi için sayfa, aboneliklerin son sayfasına yönlendirilir.


    Private Sub dlAbonnements_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles dlAbonnements.ItemCommand
        ' bir abonelik kaldırılıyor
        Dim commande As String = e.CommandName
        Select Case commande
            Case "retirer"
                ' [datatable] aboneliği kaldırılıyor
                With dtAbonnements.Rows
                    .Remove(.Find(dlAbonnements.DataKeys(e.Item.ItemIndex)))
                End With
                ' geçerli sayfa değiştirilmeli mi?
                nbAC -= 1
                If nbAC = 0 Then pAC -= 1
            Case "precedent"
                ' geçerli sayfa değiştiriliyor
                pAC -= 1
            Case "suivant"
                ' geçerli sayfa değiştir
                pAC += 1
        End Select
        ' veri bağlantıları
        terminer()
    End Sub
  • [nbAC], bir aboneliğin iptal edilmesinden önce geçerli sayfada görüntülenen satır sayısıdır. Sayfanın yeni satır sayısı 0'a eşitse, geçerli sayfa numarası [pAC] bir birim azaltılır.
  • [Precedent] bağlantısına tıklandığında, geçerli sayfa numarası [pAC] bir birim azaltılır.
  • [Suivant] bağlantısına tıklandığında, geçerli sayfa numarası [pAC] bir birim artırılır.

Diğer işlemler önceki haliyle aynı kalır.

9.4.4. Sonuç

Bu örnekle, [DataList] bileşenine sayfa numarası atayabileceğimizi gösterdik. Bu sayfa numaralandırma işlemi hassas bir işlemdir ve mümkün olduğunda [DataGrid] bileşeninin otomatik sayfa numaralandırma özelliğine güvenmek daha iyidir. Bu örnek ayrıca, [DataList] bileşeninin altbilgisinde bulunan bileşenlere nasıl erişebileceğimizi de göstermiştir.

9.5. Ürün tabanına erişim sınıfı

Daha önce kullandığımız ACCESS ve [produits] veritabanlarına tekrar odaklanıyoruz. Bu veritabanında, yapısı aşağıdaki gibi olan [liste] adlı tek bir tablo bulunduğunu hatırlayalım:

[liste] tablosuna erişim sağlayacak, bu tabloyu okuma ve güncelleme imkanı sunan bir sınıf oluşturacağız. Ayrıca, tabloyu güncellemek için önceki sınıfı kullanacak bir konsol istemcisi de geliştireceğiz. İkinci aşamada ise aynı işi yapmak üzere bir web istemcisi geliştireceğiz.

9.5.1. ExceptionProduits sınıfı

[Exception] sınıfının, parametre olarak bir hata mesajı kabul eden bir oluşturucusu vardır. Burada, tek bir hata mesajı yerine bir hata mesajları listesini kabul eden bir oluşturucuya sahip bir istisna sınıfına ihtiyacımız var. Bu, aşağıdaki [ExceptionProduits] sınıfı olacaktır:


    Public Class ExceptionProduits
        Inherits Exception

        ' istisna ile ilgili hata mesajları
        Private _erreurs As ArrayList

        ' oluşturucu
        Public Sub New(ByVal erreurs As ArrayList)
            Me._erreurs = erreurs
        End Sub

        ' özellik
        Public ReadOnly Property erreurs() As ArrayList
            Get
                Return _erreurs
            End Get
        End Property
    End Class

9.5.2. [sProduit] yapısı

[produit] yapısı, [id, nom, prix] ürününü temsil edecektir:


     ' yapı sProduit
    Public Structure sProduit
         ' alanlar
        Private _id As Integer
        Private _nom As String
        Private _prix As Double

         ' özellik kimliği
        Public Property id() As Integer
            Get
                Return _id
            End Get
            Set(ByVal Value As Integer)
                _id = Value
            End Set
        End Property

        ' ad özelliği
        Public Property nom() As String
            Get
                Return _nom
            End Get
            Set(ByVal Value As String)
                If IsNothing(Value) OrElse Value.Trim = String.Empty Then Throw New Exception
                _nom = Value
            End Set
        End Property

        ' fiyat özelliği
        Public Property prix() As Double
            Get
                Return _prix
            End Get
            Set(ByVal Value As Double)
                If IsNothing(Value) OrElse Value < 0 Then Throw New Exception
                _prix = Value
            End Set
        End Property
    End Structure

Yapı, yalnızca [nom] ve [prix] alanları için geçerli verileri kabul eder.

9.5.3. Ürünler sınıfı

[Produits] sınıfı, ürün veritabanındaki [liste] tablosunu güncellememizi sağlayacak sınıftır. Yapısı şu şekildedir:

    Public Class produits
         ' örnek verileri

        Public Sub New(ByVal chaineConnexionOLEDB As String)
....
        End Sub

        Public Function getProduits() As DataTable
....
        End Function

        Public Sub ajouterProduit(ByVal produit As sProduit)
...
        End Sub

        Public Sub modifierProduit(ByVal produit As sProduit)
...
        End Sub

        Public Sub supprimerProduit(ByVal id As Integer)
...
        End Sub

    End Class

Örnek verileri

Sınıfın çeşitli yöntemleri tarafından paylaşılan veriler şunlardır:


        Private connexion As OleDbConnection
        Private Const selectText As String = "select id,nom,prix from liste"
        Private Const insertText As String = "insert into liste(nom,prix) values(?,?)"
        Private Const updateText As String = "update liste set nom=?,prix=? where id=?"
        Private Const deleteText As String = "delete from liste where id=?"
        Private selectCommand As New OleDbCommand
        Dim insertCommand As New OleDbCommand
        Dim updateCommand As New OleDbCommand
        Dim deleteCommand As New OleDbCommand
        Dim adaptateur As New OleDbDataAdapter
connexion
veritabanına bağlantı - SQL komutunun yürütülmesi için açılacak ve hemen ardından kapatılacaktır
selectText
SQL [select] sorgusu, tüm tabloyu alır [liste]
insertText
[liste] tablosuna bir satır (ad, fiyat) eklenmesini sağlayan sorgu. [id] alanının belirtilmediğine dikkat edilmelidir. Aslında, bu alan SGBD tarafından otomatik olarak artırıldığından, onu belirtmemize gerek yoktur.
updateText
[id] anahtarına sahip [liste] tablosundaki satırın (ad, fiyat) alanlarının güncellenmesini sağlayan sorgu
deleteText
[liste] tablosunda [id] anahtarına sahip satırı silmeye izin veren sorgu
selectCommand
[OleDbCommand] nesnesi, [selectText] sorgusunu [connexion] bağlantısı üzerinden yürütür
updateCommand
[OleDbCommand] nesnesi, [connexion] bağlantısı üzerinde [updateText] sorgusunu yürütüyor
insertCommand
[OleDbCommand] nesnesi, [connexion] bağlantısı üzerinde [insertText] sorgusunu yürütüyor
deleteCommand
[OleDbCommand] nesnesi, [connexion] bağlantısı üzerinde [deleteText] sorgusunu yürütüyor
adaptateur
[selectCommand]'in yürütülmesinin sonucunu bir [DataSet] nesnesinde almak için kullanılan nesne

Oluşturucu

Oluşturucu, kullanılacak veritabanını belirten bağlantı dizesi olan tek bir [chaineConnexionOLEDB] parametresi alır. Bu parametreye dayanarak, tablonun sorgulanması ve güncellenmesi için dört komut ile adaptör hazırlanır. Bu sadece bir hazırlık aşamasıdır ve herhangi bir bağlantı kurulmaz.


        Public Sub New(ByVal chaineConnexionOLEDB As String)
            ' bağlantı hazırlanıyor
            connexion = New OleDbConnection(chaineConnexionOLEDB)
            ' sorgu komutları hazırlanıyor
            Dim commandes() As OleDbCommand = {selectCommand, insertCommand, updateCommand, deleteCommand}
            Dim textes() As String = {selectText, insertText, updateText, deleteText}
            For i As Integer = 0 To commandes.Length - 1
                With commandes(i)
                    .CommandText = textes(i)
                    .Connection = connexion
                End With
            Next
            ' veri erişim adaptörü hazırlanıyor
            adaptateur.SelectCommand = selectCommand
        End Sub

getProduits yöntemi

Bu yöntem, [Liste] tablosunun içeriğini bir [DataTable] nesnesine aktarmayı sağlar. Kod şu şekildedir:


        Public Function getProduits() As DataTable
            ' [liste] tablosu [dataset]'e aktarılıyor
            Dim contenu As New DataSet
            ' OLEDB kaynağındaki verileri okumak için bir DataAdapter nesnesi oluşturuluyor
            Try
                With adaptateur
                    .FillSchema(contenu, SchemaType.Source)
                    .Fill(contenu)
                End With
            Catch e As Exception
                ' pb
                Dim erreursCommande As New ArrayList
                erreursCommande.Add(String.Format("Erreur d'accès à la base de données : {0}", e.Message))
                Throw New ExceptionProduits(erreursCommande)
            End Try
            ' sonuç verilir
            Return contenu.Tables(0)
        End Function

İşlem, aşağıdaki iki komutla gerçekleştirilir:


                With adaptateur
                    .FillSchema(contenu, SchemaType.Source)
                    .Fill(contenu)
                End With

[FillSchema] yöntemi, [DataSet] ve contenu'in yapısını (sütunlar, kısıtlamalar, ilişkiler), [adaptateur.Connexion] tarafından referans verilen veritabanının yapısına göre belirler. Bu sayede [liste] tablosunun yapısını ve özellikle birincil anahtarını elde edebiliriz. Ardından gelen [Fill] işlemi, [Dataset] ve contenu tablolarını [liste] tablosundaki satırlarla doldurur. Sadece bu işlemle verileri ve yapıyı elde edebilirdik, ancak birincil anahtarı elde edemezdik. Oysa bu anahtar, [liste] tablosunu bellekte güncellemek için bize gerekli olacaktır. Burada, diğer yöntemlerde olduğu gibi, tek bir hata yerine bir hata listesi (ArrayList) elde etmek amacıyla [ExceptionProduits] sınıfını kullanarak olası hataları yönetiyoruz. [getProduits] yöntemi, [liste] tablosunu bir [DataTable] nesnesi biçiminde döndürür.

ajouterProduits yöntemi

Bu yöntem, [liste] tablosuna bir satır (id, isim, fiyat) eklemeye yarar. Bu bilgiler, [id, nom, prix] alanlarından oluşan bir [sProduit] yapısı biçiminde verilir. Yöntemin kodu şöyledir:


        Public Sub ajouterProduit(ByVal produit As sProduit)
            ' bir ürün eklenir: [nom,prix]
            ' ekleme parametrelerini hazırlıyoruz
            With insertCommand.Parameters
                .Clear()
                .Add(New OleDbParameter("nom", produit.nom))
                .Add(New OleDbParameter("prix", produit.prix))
            End With
            ' ekleme yapılıyor
            Try
                ' bağlantı açılıyor
                connexion.Open()
                ' komutun yürütülmesi
                insertCommand.ExecuteNonQuery()
            Catch ex As Exception
                ' sorun
                Dim erreursCommande As New ArrayList
                erreursCommande.Add(String.Format("Erreur lors de l'ajout : {0}", ex.Message))
                Throw New ExceptionProduits(erreursCommande)
            Finally
                ' bağlantı kapatılıyor
                connexion.Close()
            End Try
        End Sub

[produit] yapısının alanları, [insertCommand] komutunun parametrelerine aktarılır. Bu komutun mevcut yapılandırmasını hatırlayalım (üreticiye bakınız):


 Private Const insertText As String = "insert into liste(nom,prix) values(?,?)"
 insertCommand.Connexion=connexion

SQL ve [insert] komutlarının metinleri, "?" şeklinde gösterilen formal parametreler içerir; bunların gerçek parametrelerle değiştirilmesi gerekir. Bu işlem, [OleDbCommand] sınıfına ait [Parameters] koleksiyonu ile gerçekleştirilir. Bu koleksiyon, ? biçimli parametrelerin yerine geçmesi gereken gerçek parametreleri tanımlayan [OleDbParameter] türündeki öğeler içerir. Bu parametreler isimlendirilmediğinden, hangi fiili parametrenin hangi biçimsel parametreye karşılık geldiğini belirlemek için fiili parametrelerin indeksi kullanılır. Burada, [Parameters] koleksiyonundaki i numaralı fiili parametre, i numaralı ? biçimsel parametresinin yerini alacaktır. [OleDbParameter] türünde bir etkin parametre oluşturmak için burada, etkin parametrenin adını ve değerini tanımlayan [OleDbParameter (Byval nom as String, Byval valeur as Object)] oluşturucusu kullanılır. Adı herhangi bir şey olabilir. Ayrıca burada bu isim kullanılmayacaktır. SQL komutundaki iki parametre, [insert], [produit] yapısındaki [nom, prix] alanlarının değerlerini alır. Bu işlem tamamlandıktan sonra, ekleme işlemi [insertCommand.ExecuteNonQuery] komutuyla gerçekleştirilir.

modifierProduits yöntemi

Bu yöntem, [liste] tablosundaki satırı değiştirmeye olanak tanır. Yöntem için gerekli bilgiler, [sProduit] yapısı içindeki [id, nom, prix] alanları aracılığıyla sağlanır.


        Public Sub modifierProduit(ByVal produit As sProduit)
            ' bir ürün değiştiriliyor [id,nom,prix]
            ' güncelleme ayarları hazırlanıyor
            With updateCommand.Parameters
                .Clear()
                .Add(New OleDbParameter("nom", produit.nom))
                .Add(New OleDbParameter("prix", produit.prix))
                .Add(New OleDbParameter("id", produit.id))
            End With
            ' değişiklik yapılıyor
            Try
                ' bağlantı açılıyor
                connexion.Open()
                ' komut çalıştırılıyor
                Dim nbLignes As Integer = updateCommand.ExecuteNonQuery()
                If nbLignes = 0 Then Throw New Exception(String.Format("Le produit de clé [{0}] n'existe pas dans la table des données", produit.id))
            Catch ex As Exception
                ' sorun
                Dim erreursCommande As New ArrayList
                erreursCommande.Add(String.Format("Erreur lors de la modification : {0}", ex.Message))
                Throw New ExceptionProduits(erreursCommande)
            Finally
                ' bağlantı kapatılıyor
                connexion.Close()
            End Try
        End Sub

Kod, [ajouterProduits] yöntemindeki kodla neredeyse aynıdır; tek fark, söz konusu [OleDbCommand] komutunun [insertCommand] yerine [updateCommand] olmasıdır.

supprimerProduits yöntemi

Bu yöntem, parametre olarak geçirilen [id] anahtarına sahip satırı [liste] tablosundan silmeye yarar. Kod şu şekildedir:


        Public Sub supprimerProduit(ByVal id As Integer)
            ' anahtar ürününü siliyor [id]
            ' silme ayarları hazırlanıyor
            With deleteCommand.Parameters
                .Clear()
                .Add(New OleDbParameter("id", id))
            End With
            ' silme işlemi gerçekleştiriliyor
            Try
                ' bağlantı açılıyor
                connexion.Open()
                ' komut çalıştırılıyor
                Dim nbLignes As Integer = deleteCommand.ExecuteNonQuery()
                If nbLignes = 0 Then Throw New Exception(String.Format("Le produit de clé [{0}] n'existe pas dans la table des données", id))
            Catch ex As Exception
                ' sorun
                Dim erreursCommande As New ArrayList
                erreursCommande.Add(String.Format("Erreur lors de la suppression : {0}", ex.Message))
                Throw New ExceptionProduits(erreursCommande)
            Finally
                ' bağlantı kapatılıyor
                connexion.Close()
            End Try
        End Sub

Burada da önceki yöntemlerde olduğu gibi aynı yaklaşım izlenir.

9.5.4. [produits] sınıfının testleri

[testproduits.vb] adlı konsol türü bir test programı şu şekilde olabilir:

Option Explicit On 
Option Strict On

' ad alanları
Imports System
Imports System.Data
Imports Microsoft.VisualBasic
Imports System.Collections

Namespace st.istia.univangers.fr

     ' test sayfası
    Module testproduits
        Dim contenu As DataTable

        Sub Main(ByVal arguments() As String)
             ' ürün tablosunun içeriğini görüntüler
             ' tablo, pg'nin dosya adını aldığı ACCESS veritabanında yer alır
            Const syntaxe1 As String = "pg bdACCESS"

             ' program parametrelerinin doğrulanması
            If arguments.Length <> 1 Then
                 ' hata mesajı
                Console.Error.WriteLine(syntaxe1)
                 ' son
                Environment.Exit(1)
            End If
             ' bağlantı dizesi hazırlanıyor
            Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0)
             ' ürün nesnesi oluşturuluyor
            Dim objProduits As produits = New produits(chaineConnexion)
             ' tüm ürünler görüntüleniyor
            afficheProduits(objProduits)
             ' bir ürün ekleniyor
            Dim produit As New sProduit
            With produit
                .nom = "xxx"
                .prix = 1
            End With
            Try
                objProduits.ajouterProduit(produit)
            Catch ex As ExceptionProduits
                afficheErreurs(ex.erreurs)
            End Try
             ' tüm ürünler görüntüleniyor
            afficheProduits(objProduits)
             ' eklenen ürünün kimliğini alınıyor
            produit.id = CType(contenu.Rows(contenu.Rows.Count - 1)("id"), Integer)
             ' eklenen ürünü değiştir
            produit.prix = 200
            Try
                objProduits.modifierProduit(produit)
            Catch ex As ExceptionProduits
                afficheErreurs(ex.erreurs)
            End Try
             ' tüm ürünleri görüntüleme
            afficheProduits(objProduits)
             ' eklenen ürünü siliyor
            Try
                objProduits.supprimerProduit(produit.id)
            Catch ex As ExceptionProduits
                afficheErreurs(ex.erreurs)
            End Try
             ' tüm ürünleri görüntüler
            afficheProduits(objProduits)
        End Sub

        Sub afficheProduits(ByRef objProduits As produits)
             ' ürün tablosunu bir datatable'a alınır
            Try
                contenu = objProduits.getProduits()
            Catch ex As ExceptionProduits
                afficheErreurs(ex.erreurs)
                Environment.Exit(2)
            End Try
            Dim lignes As DataRowCollection = contenu.Rows
            For i As Integer = 0 To lignes.Count - 1
                 ' tablonun i. satırı
                Console.Out.WriteLine(lignes(i).Item("id").ToString + "," + lignes(i).Item("nom").ToString + _
                "," + lignes(i).Item("prix").ToString)
            Next
             ' konsol akışını durdurur
            Console.WriteLine("...")
            Console.ReadLine()
        End Sub

        Sub afficheErreurs(ByRef erreurs As ArrayList)
             ' hataları konsolda görüntüler
            If erreurs.Count <> 0 Then
                Console.WriteLine("Les erreurs suivantes se sont produites :")
                For i As Integer = 0 To erreurs.Count - 1
                    Console.WriteLine(String.Format("-- {0}", CType(erreurs(i), String)))
                Next
            End If
             ' konsol akışını durdurur
            Console.WriteLine("...")
            Console.ReadLine()
        End Sub
    End Module
End Namespace

İki kaynak dosyayı derliyoruz:

dos>vbc /t:library /r:system.dll /r:system.data.dll /r:system.xml.dll produits.vb

dos >vbc /r:produits.dll /r:system.dll /r:system.data.dll /r:system.xml.dll testproduits.vb

dos>dir
07/04/2004  08:40             7 168 produits.dll
04/04/2004  16:38           118 784 produits.mdb
07/04/2004  08:31             6 209 produits.vb
07/04/2004  08:40             5 120 testproduits.exe
03/04/2004  19:02             3 312 testproduits.vb

ardından test ediyoruz:

dos>testproduits produits.mdb
1,produit1,10
2,produit2,20
3,produit3,30
...

1,produit1,10
2,produit2,20
3,produit3,30
8,xxx,1
...

1,produit1,10
2,produit2,20
3,produit3,30
8,xxx,200
...

1,produit1,10
2,produit2,20
3,produit3,30
...

Okuyucunun yukarıdaki ekran çıktısını test programının koduyla karşılaştırması rica olunur.

9.6. Önbellekteki ürün tablosunu güncelleyen web uygulaması

9.6.1. Giriş

Şimdi, ürün tablosunu güncelleyen (ekleme, silme, değiştirme) bir web uygulaması yazıyoruz. Güncellenen tablo, [DataTable] nesnesinde bellekte tutulacak ve tüm kullanıcılar tarafından paylaşılacaktır. İki noktaya dikkat çekmek istiyoruz:

  • [DataTable] nesnesinin yönetimi
  • bir tablonun birden fazla kullanıcı tarafından eşzamanlı olarak güncellenmesinden kaynaklanan sorunlar.

Uygulamanın MVC mimarisi şu şekilde olacaktır:

9.6.2. Çalışma şekli ve görünümler

Uygulamanın ana sayfası aşağıdaki gibidir:

Image

 

[formulaire] adlı bu görünüm, kullanıcının ürünler üzerinde bir filtreleme koşulu belirlemesine ve sayfa başına görmek istediği ürün sayısını ayarlamasına olanak tanır.

No.
ad
tür
rol
1
lnkFiltre
LinkButton
filtreleme koşulunu belirlemek için kullanılan [Formulaire] görünümünü görüntüler
2
lnkMisaJour
LinkButton
ürün tablosunu görüntülemek ve güncellemek (değiştirme ve silme) için kullanılan [Produits] görünümünü görüntüler
3
lnkAjout
LinkButton
ürün eklemek için kullanılan [Ajout] görünümünü görüntüler
4
panel
vueFormulaire
[Formulaire] görünümü
5
txtFiltre
TextBox
filtreleme koşulu
6
txtPages
TextBox
sayfa başına ürün sayısı
7
rfvLignes
RequiredFieldValidator
[txtPages]'te bir değer olup olmadığını kontrol eder
8
rvLignes
RangeValidator
txtPages'in [3,10] aralığında olup olmadığını kontrol eder
9
btnExécuter
 
(5) numaralı koşulla filtrelenmiş [produits] görünümünü görüntüleyen [submit] düğmesi
10
lblInfo1
Etiket
Hata durumunda görüntülenen bilgilendirme metni

Örneğin, [formulaire] görünümü aşağıdaki şekilde doldurulmuşsa:

Image

şu sonuç elde edilir:

no
ad
tür
rol
1
vueProduits
panel
 
2
rdCroissant
rdDécroissant
RadioButton
kullanıcı, [nom], [prix] sütunlarından birinin başlığına tıkladığında istediği sıralama düzenini belirleyebilir. Her iki düğme de [rdTri] grubunun bir parçasıdır.
3
DataGrid1
DataGrid
ürün tablosunun filtrelenmiş bir görünümünün görüntüleme tablosu. Filtre, [formulaire] görünümü tarafından belirlenen filtredir. Ayrıca .AllowPaging=true, .AllowSorting=true
4
DataGrid2
DataGrid
ürün tablosunun tamamını gösterir - güncellemeleri takip etmenizi sağlar
5
LblInfo2
Etiket
özellikle hata durumlarında bilgi metni
6
DataGrid3
DataGrid
ürün tablosunda silinen ürünleri gösterecektir
7
DataGrid4
DataGrid
ürün tablosunda değiştirilen ürünleri gösterecektir
8
DataGrid5
DataGrid
ürün tablosunda eklenen ürünleri gösterecektir

Bu sayfada beş veri konteyneri bulunmaktadır. Hepsi, farklı bir [DataView] görünümü aracılığıyla aynı [dtProduits] tablosunu görüntüler. Bir görünüm, görünümün kaynak tablosundaki satırların bir alt kümesini temsil eder. Bu alt küme, [DataView] sınıfının [RowFilter] ve [RowStateFilter] özelliklerinden oluşturulur:

  • [RowFilter], satırlara bir filtre uygulanmasına olanak tanır; örneğin yukarıdaki [prix>30] gibi. Bu tür bir filtreleme, [DataGrid1] tarafından kullanılacaktır.
  • [RowStateFilter], tablodaki satırın durumuna göre bir filtre belirlemeye olanak tanır. Bu filtre, tablonun görünümü oluşturulduğunda satırın sahip olduğu orijinal duruma göre satırın durumunu gösterir. Burada [dtProduits] tablosu bir veritabanından alınmıştır. Başlangıçta tüm satırlarının durumu, tablonun orijinal satırlarıyla ilgili olduğunu belirtmek üzere [Original] olarak ayarlanır. Bu durum daha sonra değişebilir ve aşağıda bazıları verilen farklı değerler alabilir:
    • [Added]: satır eklenmiştir – orijinal tablonun bir parçası değildi
    • [Deleted]: satır silinmiştir – hâlâ tabloda yer almaktadır ancak "silinecek" olarak "işaretlenmiştir"
    • [Modified]: satır değiştirildi

[RowStateFilter], tabloda belirli bir duruma sahip satırları görüntülemeye olanak tanır:

  • (devamı)
    • [DataViewRowState.Added]: Yalnızca eklenen satırlar görüntülenir. Bu satırlar, güncel değerleriyle birlikte görüntülenir.
    • [DataViewRowState.ModifiedOriginal]: Yalnızca değiştirilen satırlar görüntülenir. Bu satırlar, orijinal değerleriyle birlikte görüntülenir.
    • [DataViewRowState.ModifiedCurrent]: Yalnızca değiştirilen satırlar görüntülenir. Bu satırlar, güncel değerleriyle birlikte görüntülenir.
    • [DataViewRowState.Deleted]: Yalnızca silinen satırlar görüntülenir. Bu satırlar, orijinal değerleriyle birlikte görüntülenir.
    • [DataViewRowState.CurrentRows]: Silinmemiş satırlar görüntülenir. Bu satırlar, güncel değerleriyle birlikte görüntülenir.

Böylece [RowStateFilter] filtresinin değerleri şu şekilde olacaktır:

DataGrid1
hatların durumuna ilişkin filtre yok
 
DataGrid2
DataViewRowState.CurrentRows
ürün tablosunun mevcut durumunu gösterir
DataGrid3
DataViewRowState.Deleted
ürün tablosundan silinen satırları gösterir
DataGrid4
DataViewRowState.ModifiedOriginal
ürün tablosundaki değiştirilen satırları başlangıç değerleriyle birlikte görüntüler
DataGrid5
DataViewRowState.Added
başlangıçtaki ürün tablosuna eklenen satırları görüntüler

[DataGrid1-5] konteynerleri, [dtProduits] tablosundaki güncellemeleri takip etmemizi sağlayacaktır. [DataGrid1] bileşeni, bir ürünün değiştirilmesine ve silinmesine olanak tanır. Bileşenin yapılandırmasının bu güncellemeyi nasıl sağladığını göreceğiz. Ayrıca sayfalandırılmış ve sıralanmıştır. Bu iki nokta, önceki bir örnekte zaten incelenmiştir.

[Ajout] bağlantısı, ürün ekleme formuna erişim sağlar:

no
ad
tür
rol
1
vueAjout
panel
 
2
txtNom
TextBox
ürün adı
3
rfvNom
RequiredFieldValidator
[txtNom]'te bir değer olup olmadığını kontrol eder
4
txtPrix
TextBox
ürün fiyatı
5
rfvPrix
RequiredFieldValidator
[txtPrix]'te bir değer olup olmadığını kontrol eder
6
cvPrix
CompareValidator
fiyatın >= 0 olup olmadığını kontrol eder
7
btnAjouter
Düğme
[submit] ürün ekleme düğmesi
8
lblInfo3
Etiket
Ekleme işleminin sonucuna ilişkin bilgi metni

Uygulama başlatıldığında [produits] veritabanı kullanılamayabilir. Bu durumda, kullanıcıya [erreurs] görünümü sunulur:

no
ad
tür
rol
1
vueErreurs
panel
 
2
rptErreurs
Tekrarlayıcı
hata listesi

9.6.3. Veri konteynerlerinin yapılandırılması

Beş adet [DataGrid] konteyneri, [WebMatrix] altında yapılandırılmıştır. Bu konteynerlerin biçimlendirilmesi (renkler ve kenarlıklar), özellikler panelindeki [Configuration automatique] bağlantısı kullanılarak gerçekleştirilmiştir. [DataGrid1] konteyneri, aynı paneldeki [Générateur de propriétés] bağlantısı kullanılarak yapılandırılmıştır. Sıralama izni verilmiştir ([Général] sekmesi):

Image

Diğer dört konteynerin aksine sütunlar otomatik olarak oluşturulmaz. Sütunlar, sihirbaz yardımıyla manuel olarak tanımlanmıştır:

Image

Öncelikle, [Colonne connexe] türünde [nom] ve [prix] adlı iki sütun oluşturuldu. Bu sütunlar, konteynerlerin görüntüleyeceği veri kaynağındaki [nom] ve [prix] alanlarıyla sırasıyla ilişkilendirildi. Örneğin, [nom] sütununun yapılandırması şöyledir:

Image

Sıralama ifadesi, kullanıcıkullanıcı, [DataGrid]'teki [nom] alanıyla ilişkili [nom] başlık sütun adını tıkladığında çalıştırılacak olan [select] komutunun [order by] cümlesinin arkasına yerleştirilmesi gereken ifade. Burada [nom] değerini belirledik, böylece sıralama koşulu [order by nom] olacaktır. Kullanıcının seçtiği sıralama düzenine göre bunu [order by nom asc] veya [order by nom desc] olacak şekilde değiştireceğimizi göreceğiz.

Ayrıca, iki sütun halinde düğmeler oluşturduk:

Image

[Modifier, Mettre à jour, Annuler] sütunu bir ürünü düzenlememize, [Supprimer] sütunu ise ürünü silmemize olanak tanıyacaktır. Bu sütunların her biri yapılandırılabilir. [Modifier, Mettre à jour, Annuler] sütunu aşağıdaki yapılandırmayı sunar:

Image

Düğmelerin metinlerini değiştirebileceğimizi görüyoruz. Düğmeler söz konusu olduğunda, bağlantılar ve düğmeler arasında seçim yapabiliriz (yukarıdaki açılır liste). Burada bağlantılar seçilmiştir. [Supprimer] sütununun yapılandırması da benzerdir. Bu yapılandırma sihirbazının yanı sıra, [DataGrid]'in özellikler penceresini doğrudan kullanarak, [DataGrid]'in satırlarının veri kaynağının hangi alanına göre indekslendiğini belirten [DataKeyField] özelliğini doldurduk. Burada, ürün tablosunun birincil anahtarı kullanılmaktadır:

Image

Sonuç olarak, bu yapılandırma aşağıdaki sunum kodunu oluşturur:


<asp:DataGrid id="DataGrid1" runat="server" AllowSorting="True" PageSize="4" AllowPaging="True" AutoGenerateColumns="False" DataKeyField="id">
<SelectedItemStyle ...></SelectedItemStyle>
<HeaderStyle ...></HeaderStyle>
<FooterStyle ...></FooterStyle>
<Columns>
         <asp:BoundColumn DataField="nom" SortExpression="nom" HeaderText="nom"></asp:BoundColumn>
          <asp:BoundColumn DataField="prix" SortExpression="prix" HeaderText="prix"></asp:BoundColumn>
           <asp:EditCommandColumn ButtonType="LinkButton" UpdateText="Mettre &#224; jour" CancelText="Annuler" EditText="Modifier"></asp:EditCommandColumn>
          <asp:ButtonColumn Text="Supprimer" CommandName="Delete"></asp:ButtonColumn>
  </Columns>
<PagerStyle NextPageText="Suivant" PrevPageText="Pr&#233;c&#233;dent" ...></PagerStyle>
</asp:DataGrid>

Her zamanki gibi, belirli bir deneyim kazandıktan sonra, yukarıdaki kodun tamamını veya bir kısmını doğrudan yazabilirsiniz.

Diğer [DataGrid] konteynerleri, ilişkili veri kaynağındaki sütunlardan [DataGrid] sütunlarının otomatik olarak oluşturulmasıyla elde edilen varsayılan yapılandırmaya sahiptir.

[Repeater] konteyneri, bir hata listesini görüntülemek için kullanılır. Yapılandırması doğrudan sunum kodunda yapılır:


                            <asp:Repeater id="rptErreurs" runat="server" EnableViewState="False">
                                <HeaderTemplate>
                                    Les erreurs suivantes se sont produites :
                                    <ul>
                                </HeaderTemplate>
                                <ItemTemplate>
                                    <li>
                                        <%# Container.DataItem %>
                                    </li>
                                </ItemTemplate>
                                <FooterTemplate>
                                    </ul>
                                </FooterTemplate>
                            </asp:Repeater>

Bileşenin her satırı, veri listesindeki karşılık gelen değeri gösterir: [Container.DataItem], c.a.d. Bu değer, [ArrayList] türünde olacak ve bir hata listesini temsil edecektir.

9.6.4. Uygulamanın sunum kodu

Bu kod, [main.aspx] dosyasına yerleştirilir:


<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
    <HEAD>
    </HEAD>
    <body>
        <form id="Form1" runat="server">
            <P>
                <table>
                    <tr>
                        <td><FONT size="6">Options :</FONT></td>
                        <td>
                          <asp:linkbutton id="lnkFiltre" runat="server" CausesValidation="False">
                                  Filtrage
                          </asp:linkbutton>
                    </td>
                        <td>
                        <asp:linkbutton id="lnkMisajour" runat="server" CausesValidation="False">
                            Mise à jour
                           </asp:linkbutton>
                    </td>
                        <td>
                          <asp:linkbutton id="lnkAjout" runat="server" CausesValidation="False">
                               Ajout
                           </asp:linkbutton>
                       </td>
                    </tr>
                </table>
            </P>
            <HR width="100%" SIZE="1">
            <table>
                <tr>
                    <td>

                  <asp:panel id="vueFormulaire" runat="server">
                            <P>Condition de filtrage sur&nbsp;la table LISTE.&nbsp;Exemple : prix&lt;100 and 
                                prix&gt;50</P>
                            <P>
                                <asp:TextBox id="txtFiltre" runat="server" Columns="60"></asp:TextBox></P>
                            <P>Nombre de lignes par page :
                                <asp:TextBox id="txtPages" runat="server" Columns="3">5</asp:TextBox>
                                <asp:RequiredFieldValidator id="rfvLignes" runat="server" Display="Dynamic"
                            ControlToValidate="txtPages" ErrorMessage="Indiquez le nombre de lignes par page"
                                    EnableClientScript="False">
                     </asp:RequiredFieldValidator></P>
                            <P>
                                <asp:RangeValidator id="rvLignes" runat="server" Display="Dynamic"
                        ControlToValidate="txtPages" ErrorMessage="Vous devez indiquer un nombre entre 3 et 10"
                                    EnableClientScript="False" MaximumValue="10" MinimumValue="3" Type="Integer"
                             EnableViewState="False">
                             </asp:RangeValidator></P>
                            <P>
                                <asp:Label id="lblinfo1" runat="server"></asp:Label></P>
                            <P>
                                <asp:Button id="btnExécuter" runat="server" CausesValidation="False"
                              EnableViewState="False" Text="Exécuter">
                             </asp:Button></P>
                        </asp:panel>

                      <asp:panel id="vueProduits" runat="server">
                            <TABLE>
                                <TR>
                                    <TD align="center" bgColor="#ff9966">
                                        <P>Tri
                                            <asp:RadioButton id="rdCroissant" runat="server" Text="croissant"
                                     GroupName="rdTri" Checked="True">
                                 </asp:RadioButton>
                                            <asp:RadioButton id="rdDécroissant" runat="server" Text="décroissant"
                                                 GroupName="rdTri">
                                    </asp:RadioButton></P>
                                    </TD>
                                    <TD align="center" bgColor="#ff9966">Tous les produits
                                    </TD>
                                    <TD align="center" bgColor="#ff9966">Suppressions
                                    </TD>
                                    <TD align="center" bgColor="#ff9966">Modifications
                                    </TD>
                                    <TD align="center" bgColor="#ff9966">Ajouts
                                    </TD>
                                <TR>
                                    <TD vAlign="top">
                                        <P>
                                            <asp:DataGrid id="DataGrid1" runat="server" AllowSorting="True" PageSize="4"
                                                 AllowPaging="True" ....     AutoGenerateColumns="False" DataKeyField="id">
                                                <ItemStyle ...></ItemStyle>
                                                <HeaderStyle ...></HeaderStyle>
                                                <FooterStyle ...></FooterStyle>
                                                <Columns>
                                                    <asp:BoundColumn DataField="nom" SortExpression="nom" HeaderText="nom">
                                               </asp:BoundColumn>
                                                    <asp:BoundColumn DataField="prix" SortExpression="prix" HeaderText="prix">
                                          </asp:BoundColumn>
                                                    <asp:EditCommandColumn ButtonType="LinkButton" UpdateText="Mettre &#224; jour"
                                                         CancelText="Annuler" EditText="Modifier">
                                       </asp:EditCommandColumn>
                                                    <asp:ButtonColumn Text="Supprimer" CommandName="Delete">
                                     </asp:ButtonColumn>
                                                </Columns>
                                                <PagerStyle NextPageText="Suivant" PrevPageText="Pr&#233;c&#233;dent" ....>
                                           </PagerStyle>
                                            </asp:DataGrid>
                                       </P>
                                    </TD>
                                    <TD vAlign="top">
                                        <asp:DataGrid id="DataGrid2" runat="server" ...>
                                            <AlternatingItemStyle ...></AlternatingItemStyle>
                                            <ItemStyle ...></ItemStyle>
                                            <HeaderStyle ....></HeaderStyle>
                                            <FooterStyle ...></FooterStyle>
                                            <PagerStyle ... Mode="NumericPages"></PagerStyle>
                                        </asp:DataGrid>
                            </TD>
                                    <TD vAlign="top">
                                        <asp:DataGrid id="DataGrid3" runat="server" ...>
                                            <ItemStyle ...></ItemStyle>
                                            <HeaderStyle ...></HeaderStyle>
                                            <FooterStyle ...></FooterStyle>
                                            <PagerStyle ...></PagerStyle>
                                        </asp:DataGrid>
                                   </TD>
                                    <TD vAlign="top">
                                        <asp:DataGrid id="DataGrid4" runat="server" ...>
                                            <ItemStyle ....></ItemStyle>
                                            <HeaderStyle ....></HeaderStyle>
                                            <FooterStyle ...></FooterStyle>
                                            <PagerStyle .... Mode="NumericPages"></PagerStyle>
                                        </asp:DataGrid>
                       </TD>
                                    <TD vAlign="top">
                                        <asp:DataGrid id="DataGrid5" runat="server....>
                                            <AlternatingItemStyle ...></AlternatingItemStyle>
                                            <HeaderStyle ..></HeaderStyle>
                                            <FooterStyle ...></FooterStyle>
                                            <PagerStyle ....></PagerStyle>
                                        </asp:DataGrid>
                         </TD>
                                </TR>
                            </TABLE>
                            <P></P>
                            <P>
                                <asp:Label id="lblInfo2" runat="server"></asp:Label></P>
                    </asp:panel>

                   <asp:panel id="vueErreurs" runat="server">
                            <asp:Repeater id="rptErreurs" runat="server" EnableViewState="False">
                                <HeaderTemplate>
                                    Les erreurs suivantes se sont produites :
                                    <ul>
                                </HeaderTemplate>
                                <ItemTemplate>
                                    <li>
                                        <%# Container.DataItem %>
                                    </li>
                                </ItemTemplate>
                                <FooterTemplate>
                                    </ul>
                                </FooterTemplate>
                            </asp:Repeater>
                        </asp:panel>

                  <asp:panel id="vueAjout" EnableViewState="False" Runat="server">
                            <P>Ajout d'un produit</P>
                            <P>
                                <TABLE ... border="1">
                                    <TR>
                                        <TD>nom</TD>
                                        <TD>
                                            <asp:TextBox id="txtNom" runat="server" Columns="30"></asp:TextBox>
                                            <asp:RequiredFieldValidator id="rfvNom" runat="server" ControlToValidate="txtNom"
                                                     ErrorMessage="Vous devez indiquer un nom">
                               </asp:RequiredFieldValidator>
                                      </TD>
                                    </TR>
                                    <TR>
                                        <TD>prix</TD>
                                        <TD>
                                            <asp:TextBox id="txtPrix" runat="server" Columns="10"></asp:TextBox>
                                            <asp:RequiredFieldValidator id="rfvPrix" runat="server" ControlToValidate="txtPrix"
                                                 ErrorMessage="Vous devez indiquer un prix">
                                           </asp:RequiredFieldValidator>
                                            <asp:CompareValidator id="cvPrix" runat="server" ControlToValidate="txtPrix"
                                                     ErrorMessage="CompareValidator" Type="Double" Operator="GreaterThanEqual"
                                                     ValueToCompare="0">
                             </asp:CompareValidator>
                                   </TD>
                                    </TR>
                                </TABLE>
                            </P>
                            <P>
                                <asp:Button id="btnAjouter" runat="server" CausesValidation="False"
                                     Text="Ajouter">
                               </asp:Button></P>
                            <P>
                                <asp:Label id="lblInfo3" runat="server"></asp:Label></P>
                        </asp:panel>

                </td>
                </tr>
            </table>
        </form>
    </body>
</HTML>

Aşağıdaki birkaç noktaya dikkat edelim:

  • Sayfa, uygulamanın dört görünümünü oluşturacak dört [vueFormulaire, vueProduits, vueAjout, vueErreurs] konteynerinden (panel) oluşmaktadır.
  • [submit] türündeki düğmeler veya bağlantılar, [CausesValidation=false] özelliğine sahiptir. [causesValidation=true] özelliği, sayfadaki tüm geçerlilik denetimlerinin yürütülmesini sağlar. Ancak burada, tüm geçerlilik denetimlerinin aynı anda yapılması gerekmez. Örneğin bir ekleme yapıldığında, sayfa başına satır sayısı ile ilgili denetimlerin çalıştırılmasını istemeyiz. Bu nedenle, hangi geçerlilik denetimlerinin yapılması gerektiğini kendimiz belirleyeceğiz.

9.6.5. [global.asax] kontrol kodu

[global.asax] denetleyicisi şu şekildedir:

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

İlgili kod [global.asax.vb]:


Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Data
Imports Microsoft.VisualBasic
Imports System.Collections

Public Class Global
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' yapılandırma bilgilerini alıyoruz
        Dim chaînedeConnexion As String = ConfigurationSettings.AppSettings("OLEDBStringConnection")
        Dim defaultProduitsPage As String = ConfigurationSettings.AppSettings("defaultProduitsPage")
        Dim erreurs As New ArrayList
        If IsNothing(chaînedeConnexion) Then erreurs.Add("Le paramètre [OLEDBStringConnection] n'a pas été initialisé")
        If IsNothing(defaultProduitsPage) Then
            erreurs.Add("Le paramètre [defaultProduitsPage] n'a pas été initialisé")
        Else
            Try
                Dim defProduitsPage As Integer = CType(defaultProduitsPage, Integer)
                If defProduitsPage <= 0 Then Throw New Exception
            Catch ex As Exception
                erreurs.Add("Le paramètre [defaultProduitsPage] a une valeur incorrecte")
            End Try
        End If
        ' yapılandırma hataları var mı?
        If erreurs.Count <> 0 Then
            ' hataları not ediyoruz
            Application("erreurs") = erreurs
            ' çıkılıyor
            Exit Sub
        End If
        ' burada yapılandırma hatası yok
        ' ürün nesnesi oluşturuluyor
        Dim dtProduits As DataTable
        Try
            dtProduits = New produits(chaînedeConnexion).getProduits
        Catch ex As ExceptionProduits
            ' ürünlere erişimde hata oluştu, uygulama içinde not ediliyor
            Application("erreurs") = ex.erreurs
            Exit Sub
        Catch ex As Exception
            ' yönetilemeyen hata
            erreurs.Add(ex.Message)
            Application("erreurs") = erreurs
            ' alt işlevden çıkış
        End Try
        ' burada başlatma hatası yok
        ' sayfa başına ürün sayısı kaydediliyor
        Application("defaultProduitsPage") = defaultProduitsPage
        ' ürün tablosu kaydediliyor
        Application("dtProduits") = dtProduits
    End Sub

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' oturum değişkenlerini başlatma
        If IsNothing(Application("erreurs")) Then
            ' ürün tablosuna bakış
            Session("dvProduits") = CType(Application("dtProduits"), DataTable).DefaultView
            ' sayfa başına ürün sayısı
            Session("nbProduitsPage") = Application("defaultProduitsPage")
            ' Görüntülenen mevcut sayfa
            Session("pageCourante") = 0
        End If
    End Sub
End Class

[Application_Start] dosyasında, öncelikle uygulamanın [web.config] yapılandırma dosyasından iki bilgi alınır:

  • OLEDBStringConnection: Ürün veritabanına bağlantı için OLEDB dizesi
  • defaultProduitsPage: Görüntülenen sayfa başına varsayılan ürün sayısı

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key="OLEDBStringConnection" value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\devel\aspnet\poly\webforms3\vs\majproduits1\produits.mdb" />
        <add key="defaultProduitsPage" value="5" />
    </appSettings>
</configuration>

Bu iki bilgiden biri eksikse, bir hata listesi oluşturulur ve uygulamaya eklenir. [defaultProduitsPage] parametresi mevcut ancak hatalıysa da aynı durum geçerlidir. Beklenen iki parametre de mevcut ve doğruysa, bir [dtProduits] tablosu oluşturulur ve uygulamaya eklenir. Çeşitli istemciler tarafından kullanılacak ve güncellenecek olan bu tablodur. Veritabanı ise değişmeden kalır. Veritabanının güncellenmesi konusuna bir sonraki uygulamada değineceğiz. Bu tablo, daha önce incelenen [produits] sınıfının bir örneği ve bu sınıfa ait [getProduits] yönteminden oluşturulur. [dtProduits] tablosunun elde edilmesi başarısız olabilir. Bu durumda, [produits] sınıfının [ExceptionProduits] türünde bir istisna attığı bilinmektedir. Bu istisna burada yakalanır ve ilgili hata listesi, [erreurs] anahtarına bağlı uygulamaya kaydedilir. Her istek işleme sırasında, uygulamada kaydedilen bilgilerde bu anahtarın varlığı kontrol edilecektir. Anahtar bulunursa, [erreurs] görünümü müşteriye gönderilecektir.

[dtProduits] tablosu tüm web istemcileri tarafından paylaşılsa da, her birinin bu tablo üzerinde kendine ait [dvProduits] görünümü olacaktır. Nitekim, her web istemcisi [dtProduits] tablosuna bir filtre ve bir sıralama düzeni belirleyebilir. Her web istemcisine özgü bu bilgiler, istemcinin görünümünde depolanır. Bu nedenle, bu görünüm her istemcinin kendi oturumuna yerleştirilmek üzere [Session_Start]'te oluşturulur. Tabloda varsayılan bir görünüm elde etmek için [dtProduits] tablosunun [DefaultView] özniteliği kullanılır. Başlangıçta ne filtre ne de sıralama düzeni vardır. Ayrıca, oturuma iki bilgi daha eklenir:

  • [nbProduitsPage] anahtarına sahip sayfa başına ürün sayısı. Oturumun başlangıcında bu sayı, yapılandırma dosyasında tanımlanan varsayılan sayıya eşittir.
  • ürünlerin mevcut sayfa numarası. Başlangıçta bu, ilk sayfadır.

9.6.6. [main.aspx.vb] kontrol kodu

Denetleyicinin iskeleti şöyledir:


Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Data
Imports st.istia.univangers.fr
Imports System
Imports System.Xml
Imports System.Web.UI.WebControls

Public Class main
    Inherits System.Web.UI.Page

    ' sayfa bileşenleri
    Protected WithEvents txtPages As System.Web.UI.WebControls.TextBox
    Protected WithEvents btnExécuter As System.Web.UI.WebControls.Button
    Protected WithEvents vueFormulaire As System.Web.UI.WebControls.Panel
    Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents lnkErreurs As System.Web.UI.WebControls.LinkButton
    Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
    Protected WithEvents rdCroissant As System.Web.UI.WebControls.RadioButton
    Protected WithEvents rdDécroissant As System.Web.UI.WebControls.RadioButton
    Protected WithEvents txtFiltre As System.Web.UI.WebControls.TextBox
    Protected WithEvents lblInfo2 As System.Web.UI.WebControls.Label
    Protected WithEvents lblinfo1 As System.Web.UI.WebControls.Label
    Protected WithEvents lblErreurs As System.Web.UI.WebControls.Label
    Protected WithEvents DataGrid2 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents vueProduits As System.Web.UI.WebControls.Panel
    Protected WithEvents vueAjout As System.Web.UI.WebControls.Panel
    Protected WithEvents lnkMisajour As System.Web.UI.WebControls.LinkButton
    Protected WithEvents lnkFiltre As System.Web.UI.WebControls.LinkButton
    Protected WithEvents txtNom As System.Web.UI.WebControls.TextBox
    Protected WithEvents txtPrix As System.Web.UI.WebControls.TextBox
    Protected WithEvents btnAjouter As System.Web.UI.WebControls.Button
    Protected WithEvents lblInfo3 As System.Web.UI.WebControls.Label
    Protected WithEvents rfvLignes As System.Web.UI.WebControls.RequiredFieldValidator
    Protected WithEvents rvLignes As System.Web.UI.WebControls.RangeValidator
    Protected WithEvents rfvNom As System.Web.UI.WebControls.RequiredFieldValidator
    Protected WithEvents rfvPrix As System.Web.UI.WebControls.RequiredFieldValidator
    Protected WithEvents cvPrix As System.Web.UI.WebControls.CompareValidator
    Protected WithEvents lnkAjout As System.Web.UI.WebControls.LinkButton
    Protected WithEvents DataGrid3 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents DataGrid4 As System.Web.UI.WebControls.DataGrid
    Protected WithEvents DataGrid5 As System.Web.UI.WebControls.DataGrid

    ' sayfa verileri
    Protected dtProduits As DataTable
    Protected dvProduits As DataView
    Protected defaultProduitsPage As Integer
    Protected erreur As Boolean = False

    ' sayfa yükleniyor
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
    End Sub

    Private Sub afficheErreurs()
...
    End Sub

    Private Sub afficheFormulaire()
...
    End Sub

    Private Sub btnExécuter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExécuter.Click
....
    End Sub

    Private Sub afficheProduits(ByVal page As Integer, ByVal taillePage As Integer)
...
    End Sub

    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
...
    End Sub

    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
....
    End Sub

    Private Sub DataGrid1_DeleteCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.DeleteCommand
....
    End Sub

    Private Sub DataGrid1_EditCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.EditCommand
...
    End Sub

    Private Sub DataGrid1_CancelCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.CancelCommand
...
    End Sub

    Private Sub DataGrid1_UpdateCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.UpdateCommand
...
    End Sub

    Private Sub supprimerProduit(ByVal idProduit As Integer)
...
    End Sub

    Private Sub modifierProduit(ByVal idProduit As Integer, ByVal item As DataGridItem)
....
    End Sub

    Private Sub lnkMisajour_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkMisajour.Click
...
    End Sub

    Private Sub lnkAjout_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkAjout.Click
...
    End Sub

    Private Sub afficheAjout()
...
    End Sub

    Private Sub lnkFiltre_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkFiltre.Click
....
    End Sub

    Private Sub btnAjouter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAjouter.Click
....
    End Sub
End Class

9.6.7. Örnek verileri

[main] sınıfı aşağıdaki örnek verilerini kullanır:

Public Class main
    Inherits System.Web.UI.Page

     ' sayfa bileşenleri
    Protected WithEvents txtPages As System.Web.UI.WebControls.TextBox
...

     ' sayfa verileri
    Protected dtProduits As DataTable
    Protected dvProduits As DataView
    Protected defaultProduitsPage As Integer
dtProduits
tüm müşteriler için ortak olan [DataTable] ürün tablosu
dvProduits
ürünler için [DataView] görünümü - her müşteriye özgü
defaultProduitsPage
Varsayılan olarak önerilen sayfa başına ürün sayısı

9.6.8. Sayfayı yükleme prosedürü [Page_Load]

[page_Load] prosedürünün kodu şöyledir:


    ' sayfa yükleme
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' uygulamada hata olup olmadığına bakılıyor
        If Not IsNothing(Application("erreurs")) Then
            ' uygulama düzgün bir şekilde başlatılamadı
            afficheErreurs(CType(Application("erreurs"), ArrayList))
            Exit Sub
        End If
        ' ürün tablosundan bir referans alınır
        dtProduits = CType(Application("dtProduits"), DataTable)
         ' ürünler sayfasını alınıyor
        dvProduits = CType(Session("dvProduits"), DataView)
         ' sayfa başına ürün sayısını alıyoruz
        defaultProduitsPage = CType(Application("defaultProduitsPage"), Integer)
         ' devam eden bir güncelleme varsa iptal ediliyor
        DataGrid1.EditItemIndex = -1
        'İlk istek
        If Not IsPostBack Then
            ' ilk form görüntüleniyor
            txtPages.Text = defaultProduitsPage.ToString
            afficheFormulaire()
        End If
    End Sub
  • Öncelikle, uygulamanın başlatılması sırasında ürün tablosunun yüklenip yüklenmediğini kontrol ederiz. Yüklenmemişse, uygun hata mesajlarıyla birlikte [erreurs] görünümünü görüntüleriz ve ardından [erreur] boole değerini "true" olarak ayarladıktan sonra prosedürden çıkarız. Bu gösterge, bazı prosedürler tarafından test edilecektir.
  • Uygulamada [dtProduits] ürün tablosu alınır ve sayfanın tüm yöntemleri tarafından paylaşılabilmesi için [dtProduits] örnek değişkenine atanır.
  • Aynı işlem, oturumdan alınan [dvProduits] görünümü ve uygulamadan alınan varsayılan sayfa başına ürün sayısı için de yapılır.
  • Müşterinin ilk isteği ise, ona filtreleme ve sayfalandırma koşullarını tanımlama formu sunulur; bu formda varsayılan sayfalandırma, [defaultProduitsPage] ürün/sayfa şeklindedir.
  • [dataGrid1] bileşeninin düzenleme modu iptal edilir. Bu bileşen, düzenlenmekte olan öğenin indeksini belirten [EditItemIndex] özniteliğine sahiptir. Eğer [EditItemIndex]=-1 ise, düzenlenmekte olan hiçbir öğe yoktur. [EditItemIndex]=i ise, [DataGrid] bileşeninin i numaralı öğesi düzenlenmektedir. Bu durumda, [dataGrid] bileşenindeki diğer öğelerden farklı bir şekilde görüntülenir:

Image

Yukarıda, [Produit8, 80] öğesi [édition] modundadır. Yukarıda görüldüğü gibi, kullanıcı [Mettre à jour] bağlantısı ile güncellemesini onaylayabilir, [Annuler] bağlantısı ile iptal edebilir. Ayrıca, düzenleme aşamasındaki öğeyle hiçbir ilgisi olmayan diğer bağlantıları da kullanabilir. Sayfa her yüklendiğinde [DataGrid1.EditItemIndex=-1] bağlantısını ekleyerek, [DataGrid1] bağlantısının düzenleme modunun sistematik olarak iptal edilmesini sağlarız. Yalnızca [Modifier] bağlantısı tıklandığında ona farklı bir değer atanacaktır. Bu, söz konusu olayı işleyen prosedürde yapılacaktır. Aşağıdaki durumlarla karşılaşılacaktır:

  1. [DataGrid1]'in 8 numaralı öğesindeki [Modifier] bağlantısı tıklandı. [Page_Load], öncelikle [DataGrid1.EditItemIndex]'e -1 değerini atar. Ardından, [Modifier] olayını işleyen prosedür, [DataGrid1.EditItemIndex]'e 8 değerini atayacaktır. Sonuç olarak, sayfa müşteriye geri gönderildiğinde, 8 numaralı öğe [édition] modunda olacak ve yukarıda gösterildiği gibi görünecektir.
  2. Kullanıcı, [édition] modunda olan ürünü değiştirir ve [Mettre à jour] bağlantısı aracılığıyla değişikliğini onaylar. [Page_Load], [DataGrid1.EditItemIndex]'e -1 değerini atar. Ardından, [Mettre à jour] olayını işleyen prosedür çalıştırılacak ve [dtProduits]'in 8 numaralı öğesi güncellenecektir. Sayfa müşteriye geri gönderildiğinde, [DataGrid1]'teki hiçbir öğe düzenleme modunda olmayacaktır (DataGrid.EditItemIndex=-1).
  3. Bir kullanıcı bir ürünün güncelleme sürecine girmişse, [Annuler] aracılığıyla bu güncellemeyi iptal etmesi mantıklı olacaktır. Ancak başka bir bağlantıya tıklamasını engelleyen hiçbir şey yoktur. Dolayısıyla bu durumu da öngörmemiz gerekir. Bu durumda da öncekilerde olduğu gibi, [Page_Load] önce [DataGrid1.EditItemIndex]'e -1 değerini atar, ardından meydana gelen olayı işleyen prosedür yürütülür. Bu yordam, [DataGrid1.EditItemIndex] özelliğini değiştirmeyecek ve bu özellik -1 değerinde kalacaktır. Sayfa müşteriye geri gönderildiğinde, [DataGrid1]'teki hiçbir öğe düzenleme modunda olmayacaktır.

Görüldüğü gibi, sayfa yüklendiğinde [EditItemIndex] değerini -1 olarak ayarlayarak, kullanıcının bir bağlantıya tıkladığı sırada güncelleme modunda olup olmadığını dert etmek zorunda kalınmaz.

9.6.9. [erreurs], [formulaire] ve [ajout] görünümlerinin görüntülenmesi

Uygulamanın hatalı bir şekilde başlatıldığı tespit edilirse, [Page_Load] sayfasının yüklenmesi sırasında [erreurs] görünümünün görüntülenmesi istenir. Bu görüntüleme, [rptErreurs] veri bileşenini parametre olarak geçirilen hata listesiyle ilişkilendirerek ve uygun konteyneri görünür hale getirerek gerçekleştirilir. Son olarak, hata durumunda uygulama kullanılamayacağı için üç [Filtre, Mise à jour, Ajout] bağlantısı gizlenir.


    Private Sub afficheErreurs(ByVal erreurs As ArrayList)
        ' hata listesini tekrarlayıcıya eklenir rptErreurs
        With rptErreurs
            .DataSource = erreurs
            .DataBind()
        End With
        'seçenekler devre dışı bırakılıyor
        lnkAjout.Visible = False
        lnkMisajour.Visible = False
        lnkFiltre.Visible = False
        ' [erreurs] görünümü görüntülenir
        vueErreurs.Visible = True
        vueFormulaire.Visible = False
        vueProduits.Visible = False
        vueAjout.Visible = False
    End Sub

Diğer görünümler, kullanıcıya sunulan seçeneklerden talep edilir:

Image

Kullanıcı sayfadaki [Ajout] bağlantısına tıkladığında [Ajout] görünümünün görüntülenmesi istenir:


    Private Sub lnkAjout_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkAjout.Click
        ' [ajout] görünümünü görüntüler
        afficheAjout()
    End Sub

    Private Sub afficheAjout()
        ' ekleme görünümünü görüntüler
        vueAjout.Visible = True
        vueFormulaire.Visible = False
        vueProduits.Visible = False
        vueErreurs.Visible = False
    End Sub

Kullanıcı sayfadaki [Filtrage] bağlantısına tıkladığında [Formulaire] görünümünün görüntülenmesi istenir. Bu durumda,

  • ürün tablosundaki filtreyi ayarlamaya olanak tanıyan görünüm
  • her web sayfasında gösterilecek ürün sayısını

Oturumda depolanan değerleri içeren bir form sunulur:


    Private Sub lnkFiltre_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkFiltre.Click
        ' filtreleme koşullarını tanımla
        txtFiltre.Text = dvProduits.RowFilter
        ' sayfalandırmayı tanımlayın
        txtPages.Text = CType(Session("nbProduitsPage"), String)
        ' filtreleme formu görüntülenir
        afficheFormulaire()
    End Sub

Bir görünümün filtreleme koşulu, [RowFilter] özniteliğinde tanımlanır. Hatırlanacağı üzere, filtrelenmiş ve sayfalandırılmış görünümün adı [dvProduits]'tir ve [Page_Load] sayfası yüklendiğinde oturumdan alınmıştır. Dolayısıyla filtreleme koşulu [dvProduits.RowFilter]'ten alınır. Sayfa başına ürün sayısı da oturumdan alınır. Kullanıcı bu bilgiyi daha önce hiç tanımlamamışsa, bu sayı varsayılan sayfa başına ürün sayısına eşittir. Bu işlem tamamlandıktan sonra, bu iki bilgiyi tanımlamaya yarayan form görüntülenir:


    Private Sub afficheFormulaire()
        ' [formulaire] görünümünü görüntüler
        vueFormulaire.Visible = True
        vueErreurs.Visible = False
        vueProduits.Visible = False
        vueAjout.Visible = False
    End Sub

Image

9.6.10. [formulaire] görünümünün doğrulanması

Yukarıdaki [Exécuter] düğmesine tıklama, aşağıdaki prosedürle işlenir:


    Private Sub btnExécuter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExécuter.Click
        ' sayfa geçerli mi?
        rfvLignes.Validate()
        rvLignes.Validate()
        If Not rfvLignes.IsValid Or Not rvLignes.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If
        ' filtrelenmiş verilerin tabloya eklenmesi
        Try
            dvProduits.RowFilter = txtFiltre.Text.Trim
        Catch ex As Exception
            lblinfo1.Text = "Erreur de filtrage (" + ex.Message + ")"
            afficheFormulaire()
            Exit Sub
        End Try
        ' sayfa başına ürün sayısını kaydedilir
        Session("nbProduitsPage") = txtPages.Text
        've geçerli sayfa
        Session("pageCourante") = 0
        ' her şey yolunda - veriler görüntüleniyor
        afficheProduits(0, CType(txtPages.Text, Integer))
    End Sub

Öncelikle, sayfanın iki doğrulama bileşeni olduğunu hatırlatalım:

no
ad
tür
rol
7
rfvLignes
RequiredFieldValidator
[txtPages]'te bir değer olup olmadığını kontrol eder
8
rvLignes
RangeValidator
txtPages'in [3,10] aralığında olup olmadığını kontrol eder

İşlem, yukarıdaki iki bileşenin kontrol kodunu [Validate] yöntemini kullanarak çalıştırmakla başlar ve ardından [IsValid] özniteliğinin değerini kontrol eder. Bu değer, yalnızca kontrol edilen verinin geçerli olduğu tespit edildiğinde [true] olur. İki bileşenden birinin geçerlilik testleri başarısız olursa, kullanıcının hatalarını düzeltmesi için [formulaire] görünümü yeniden görüntülenir. Filtreleme koşulu, [dvProduits.RowFilter] özniteliğine uygulanır. Aşağıda gösterildiği gibi, kullanıcı yanlış bir filtreleme kriteri girmişse burada bir istisna meydana gelebilir:

Image

Bu durumda, [formulaire] görünümü yeniden görüntülenir. Girilen iki bilgi de doğruysa, oturuma iki veri kaydedilir:

  • kullanıcı tarafından seçilen sayfa başına ürün sayısı
  • görüntülenecek sayfanın numarası - başlangıçta sayfa 0

Bu iki bilgi, [produits] görünümü her çağrıldığında kullanılır.

9.6.11. [produits] görünümünün görüntülenmesi

[produits] görünümü şöyledir:

Image

[DataGrid] adlı 5 bileşenimiz var ve her biri [dtProduits] ürün tablosunun belirli bir görünümünü sergiliyor. Soldan başlayarak:

ad
rol
DataGrid1
Ürün tablosunun filtrelenmiş bir görünümünü gösteren ızgara. Filtre, [formulaire] görünümü tarafından belirlenen filtredir. Ayrıca .AllowPaging=true, .AllowSorting=true
DataGrid2
ürün tablosunun tamamını görüntüler - güncellemeleri takip etmeyi sağlar
DataGrid3
ürün tablosunda silinen ürünleri gösterir
DataGrid4
ürün tablosunda değiştirilen ürünleri gösterir
DataGrid5
ürün tablosunda eklenen ürünleri gösterecektir

[afficheProduits] prosedürü, önceki görünümü görüntülemekle görevlidir:


    Private Sub afficheProduits(ByVal page As Integer, ByVal taillePage As Integer)
        ' veriler iki bileşene ekleniyor [DataGrid]
        Application.Lock()
        With DataGrid1
            .DataSource = dvProduits
            .PageSize = taillePage
            .CurrentPageIndex = page
            .DataBind()
        End With
        Dim dvCurrent As New DataView(dtProduits)
        dvCurrent.RowStateFilter = DataViewRowState.CurrentRows
        With DataGrid2
            .DataSource = dvCurrent
            .DataBind()
        End With
        Dim dvSupp As DataView = New DataView(dtProduits)
        dvSupp.RowStateFilter = DataViewRowState.Deleted
        With DataGrid3
            .DataSource = dvSupp
            .DataBind()
        End With
        Dim dvModif As DataView = New DataView(dtProduits)
        dvModif.RowStateFilter = DataViewRowState.ModifiedOriginal
        With DataGrid4
            .DataSource = dvModif
            .DataBind()
        End With
        Dim dvAjout As DataView = New DataView(dtProduits)
        dvAjout.RowStateFilter = DataViewRowState.Added
        With DataGrid5
            .DataSource = dvAjout
            .DataBind()
        End With
        Application.UnLock()
        ' [produits] görünümü görüntüleniyor
        vueProduits.Visible = True
        vueFormulaire.Visible = False
        vueErreurs.Visible = False
        vueAjout.Visible = False
        ' geçerli sayfa kaydediliyor
        Session("pageCourante") = page
    End Sub

Bu prosedür iki parametre kabul eder:

  • [page]'te görüntülenecek sayfa numarası [DataGrid1]
  • sayfa başına ürün sayısı [taillePage]

[DataGrid]'in ürün tablosuna bağlanması, 5 farklı görünüm aracılığıyla gerçekleştirilir.

  • [DataGrid1], sayfalandırılmış ve sıralanmış [dvProduits] görünümüne bağlıdır.

        With DataGrid1
            .DataSource = dvProduits
            .PageSize = taillePage
            .CurrentPageIndex = page
            .DataBind()
        End With

[DataGrid1]'in veri kaynağına bağlanması sırasında, prosedüre parametre olarak aktarılan bu iki bilgiye ihtiyaç duyulur.

  • [DataGrid2], [dtProduits] tablosundaki tüm güncel öğeleri gösteren bir görünüme bağlanmıştır. Bu görünüm, [DataGrid1] gibi [dtProduits] tablosunun güncellenmiş bir görünümünü sunar, ancak sayfalandırma ve sıralama içermez.

        Dim dvCurrent As New DataView(dtProduits)
        dvCurrent.RowStateFilter = DataViewRowState.CurrentRows
        With DataGrid2
            .DataSource = dvCurrent
            .DataBind()
        End With

Burada [DataView] sınıfının [RowStateFilter] özniteliğini kullanıyoruz. Bir tablonun veya buradaki bir görünümün bir satırı, satırın durumunu belirten [RowState] özniteliğine sahiptir. İşte bunlardan birkaçı:

  • (devamı)
    • [Added]: satır eklenmiştir
    • [Modified]: satır değiştirildi
    • [Deleted]: satır silinmiştir
    • [Unchanged]: satırda değişiklik yapılmadı

[dtProduits] tablosu ilk olarak [Application_Start]'te oluşturulduğunda, tüm satırları [Unchanged] durumundaydı. Bu durum, web istemcilerindeki güncellemelerle birlikte değişecektir:

  • (devamı)
    • bir müşteri yeni bir ürün oluşturduğunda, [dtProduits] tablosuna bir satır eklenir. Bu satır, [Added] durumunda olacaktır.
    • Bir ürün değiştirildiğinde, durumu [Unchanged]'ten [Modified]'e geçer.
    • Bir ürün silindiğinde, satır fiziksel olarak silinmez. Bunun yerine silinecek olarak işaretlenir ve durumu [Deleted]'e geçer. Bu silme işlemini geri almak mümkündür.

[DataView] sınıfındaki [RowStateFilter] özniteliği, satırların [RowState] durumuna göre bir görünümü filtrelemeye olanak tanır. Olası değerleri, [DataRowViewState] sıralamasındaki değerlerdir:

  • (devamı)
    • DataRowViewState.CurrentRows: Silinmemiş satırlar, güncel değerleriyle birlikte görüntülenir
    • DataRowViewState.Added: Eklenen satırlar mevcut değerleriyle birlikte görüntülenir
    • DataRowViewState.Deleted: Silinen satırlar, orijinal değerleriyle birlikte görüntülenir
    • DataRowViewState.ModifiedOriginal: Değiştirilen satırlar orijinal değerleriyle birlikte görüntülenir
    • DataRowViewState.ModifiedCurrent: Değiştirilen satırlar, güncel değerleriyle birlikte görüntülenir

Değiştirilen bir satırın iki değeri vardır: ilk değişiklikten önce satırın sahip olduğu orijinal değer ve bir veya daha fazla değişiklikten sonra elde edilen güncel değer. Bu iki değer aynı anda saklanır.

DataGrid 2 ila 5, [dtProduits] tablosunun [RowStateFilter] özniteliğine göre filtrelenmiş bir görünümünü gösterir

DataGrid
Filtre
DataGrid2
RowStateFilter= DataRowViewState.CurrentRows
DataGrid3
RowStateFilter= DataRowViewState.Deleted
DataGrid4
RowStateFilter= DataRowViewState.ModifiedOriginal
DataGrid5
RowStateFilter= DataRowViewState.Added

[dtPoduits] tablosu, farklı web istemcileri tarafından eşzamanlı olarak güncellenir; bu durum, tabloya erişim çakışmalarına yol açabilir. Bir istemci [dtProduits] tablosunun görünümlerini görüntülerken, tablonun başka bir web istemcisi tarafından değiştirilmekte olduğu için kararsız bir durumda olması durumunda bunun engellenmesi istenir. Ayrıca, bir istemci yukarıda belirtildiği gibi [dtProduits] tablosuna okuma erişimi gerektiğinde veya ürün ekleme, değiştirme ve silme işlemleri için yazma erişimi gerektiğinde, aşağıdaki sıraya göre diğer istemcilerle senkronize olacaktır:

Application.Lock
... section critique 1
Application.Unlock

Uygulama kodunda birkaç kritik bölüm bulunabilir:

Application.Lock
... section critique 2
Application.Unlock

İşleyiş şu şekildedir:

  1. Bir veya daha fazla istemci, kritik bölüm 1'deki [Application.Lock] komutuna ulaşır. Bu, tek girişli bir jeton dağıtıcısıdır. Tek bir istemci bu jetonu alır. Buna C1 diyelim.
  2. C1 numaralı istemci, [Application.Unlock] komutuyla jetonu iade etmedikçe, başka hiçbir istemcinin [Application.Lock] tarafından kontrol edilen kritik bölüme girmesine izin verilmez. Yukarıdaki örnekte, hiçbir istemci kritik bölüm 1 ve 2’ye giremez.
  3. C1 istemcisi, [Application.Unlock]'i çalıştırır ve böylece giriş jetonunu iade eder. Bu jeton daha sonra başka bir istemciye verilebilir. 1'den 3'e kadar olan adımlar tekrarlanır.

Bu mekanizma sayesinde, [dtProduits] tablosuna okuma veya yazma erişimi olan tek bir istemci olmasını sağlayacağız.

[Mise à jour] bağlantısı, [Produits] görünümünü de görüntüler:

Image

İlgili prosedür şöyledir:


    Private Sub lnkMisajour_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkMisajour.Click
        ' ürünler görünümü görüntülenir
        afficheProduits(CType(Session("pageCourante"), Integer), CType(Session("nbProduitsPage"), Integer))
    End Sub

[produits] görünümünü görüntülemeliyiz. Bu, iki parametre kabul eden [afficheProduits] prosedürü kullanılarak yapılır: görüntülenecek geçerli sayfa numarası ve sayfa başına görüntülenecek ürün sayısı. Bu iki bilgi oturumda bulunur; kullanıcı bunları daha önce kendisi tanımlamamışsa, başlangıç değerleriyle birlikte olabilir.

9.6.12. [DataGrid1]'te Sayfalandırma ve Sıralama

[DataGrid] prosedürünün sayfalandırma ve sıralama işlemlerini yöneten prosedürlerle başka bir örnekte daha önce karşılaşmıştık. Sayfa değişimi durumunda, yeni sayfa numarasını [afficheProduits] prosedürüne parametre olarak geçirerek [produits] görünümünü görüntüleriz.


    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
        ' sayfa değiştirme
        afficheProduits(e.NewPageIndex, DataGrid1.PageSize)
    End Sub

Kullanıcı, [DataGrid1]'teki sütun başlıklarından birine tıkladığında [DataGrid1_SortCommand] prosedürü yürütülür. Bu durumda, [DataGrid1] tarafından görüntülenen [dvProduits] görünümünün [Sort] özniteliğine sıralama ifadesi atanmalıdır. Bu ifade, sözdizimi açısından SQL SELECT deyimindeki [order by] yan tümcesinin arkasına yerleştirilen sıralama ifadesiyle eşdeğerdir. Prosedürün [e] argümanı, başlığı tıklanan sütunla ilişkili sıralama ifadesini bize veren [SortExpression] özniteliğine sahiptir. [DataGrid1] bileşeni oluşturulduğunda, bu sıralama ifadesi tanımlanmıştı. Aşağıda, [DataGrid1]'in [nom] sütunu için tanımlanan ifade yer almaktadır:

Image

[DataGrid] tasarlanırken sıralama ifadesi tanımlanmamışsa, [DataGrid] sütunuyla ilişkili veri alanının adı kullanılır. Sıralama yönü (artarak, azalarak) burada kullanıcı tarafından [rdCroissant, rd Décroissant] radyo düğmeleri kullanılarak belirlenir:

Image

Sıralama prosedürü şu şekildedir:


    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
        ' veri görünümünü sıralama
        With dvProduits
            .Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        End With
        ' görüntüleme
        afficheProduits(0, DataGrid1.PageSize)
    End Sub

9.6.13. Bir ürünü silme

Bir ürünü silmek için kullanıcı, ürün satırındaki [Supprimer] bağlantısını kullanır:

Image

Bunun üzerine [DataGrid1_DeleteCommand] prosedürü yürütülür:


    Private Sub DataGrid1_DeleteCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.DeleteCommand
        ' bir ürün siliniyor
        ' silinecek ürünün anahtarı
        Dim idProduit As Integer = CType(DataGrid1.DataKeys(e.Item.ItemIndex), Integer)
        ' ürünün silinmesi
        Dim erreur As Boolean = False
        Try
            supprimerProduit(idProduit)
        Catch ex As Exception
            ' sorun
            lblInfo2.Text = ex.Message
            erreur = True
        End Try
        ' sayfa değişimi mi?
        Dim page As Integer = DataGrid1.CurrentPageIndex
        If Not erreur AndAlso DataGrid1.Items.Count = 1 Then
            page = DataGrid1.CurrentPageIndex - 1
            If page < 0 Then page = 0
        End If
        ' ürünlerin görüntülenmesi
        afficheProduits(page, DataGrid1.PageSize)
    End Sub

Ürünlerin güncellenmesi, ürünün [id] anahtarı aracılığıyla yapılacaktır. Nitekim, [dtProduits] tablosundaki [id] sütunu birincil anahtardır ve bu anahtar sayesinde, [DataGrid1] bileşeninde güncellenen ürünün satırını [dtProduits] tablosunda bulabiliriz. Dolayısıyla prosedür, [Supprimer] bağlantısı tıklanan ürünün anahtarını almakla başlar:


         ' silinecek ürün kodu
        Dim idProduit As Integer = CType(DataGrid1.DataKeys(e.Item.ItemIndex), Integer)

[e.Item]'in, olayı tetikleyen [DataGrid1] öğesi olduğunu biliyoruz. Genel olarak bu öğe, [DataGrid]'in bir satırıdır. [e.Item.ItemIndex], olayı tetikleyen satır numarasıdır. Bu indeks, görüntülenen geçerli sayfaya göredir. Dolayısıyla, ürün tablosunda 17 numaralı satır olsa bile, görüntülenen sayfanın ilk satırı [ItemIndex=0] özelliğine sahiptir. [DataGrid1.DataKeys], [DataGrid]'in anahtar listesidir. Tasarım aşamasında [DataGrid1.DataKey=id] yazdığımız için, [DataGrid1]'in anahtarları, aynı zamanda birincil anahtar olan [dtProduits] tablosunun [id] sütunundaki değerlerden oluşur. Dolayısıyla [DataGrid1.DataKeys(e.Item.ItemIndex)], silinecek ürünün [id] anahtarıdır. Bu bilgi elde edildikten sonra, [supprimerProduit] prosedürü kullanılarak silme işlemi talep edilir:


         ' ürünün silinmesi
        Dim erreur As Boolean = False
        Try
            supprimerProduit(idProduit)
        Catch ex As Exception
            ' sorun
            lblInfo2.Text = ex.Message
            erreur = True
        End Try

Bu silme işlemi bazı durumlarda başarısız olabilir. Nedenini göreceğiz. Bu durumda bir istisna atılır. Bu istisna burada yönetilir. Hata varsa, [DataGrid1]'in değişmesi gerekmez. Sadece [produits] görünümüne bir hata mesajı eklenir. Hata yoksa ve kullanıcı mevcut sayfadaki tek ürünü silmişse, önceki sayfa görüntülenir.


         ' sayfa değişimi mi?
        Dim page As Integer = DataGrid1.CurrentPageIndex
        If Not erreur AndAlso DataGrid1.Items.Count = 1 Then
            page = DataGrid1.CurrentPageIndex - 1
            If page < 0 Then page = 0
        End If

Her durumda, [produits] görünümü, [afficheProduits] prosedürüyle yeniden görüntülenir:

         ' ürünlerin görüntülenmesi
        afficheProduits(page, DataGrid1.PageSize)

[supprimerProduit] prosedürü şu şekildedir:


    Private Sub supprimerProduit(ByVal idProduit As Integer)
        Dim erreur As String
        Try
            ' senkronizasyon
            Application.Lock()
            ' silinecek satırın aranması
            Dim ligne As DataRow = dtProduits.Rows.Find(idProduit)
            If ligne Is Nothing Then
                erreur = String.Format("Produit [{0}] inexistant", idProduit)
            Else
                ' satırın silinmesi
                ligne.Delete()
            End If
        Catch ex As Exception
            erreur = String.Format("Erreur de suppression : {0}", ex.Message)
        Finally
            ' senkronizasyonun sonu
            Application.UnLock()
        End Try
        ' hata durumunda istisna atılır
        If erreur <> String.Empty Then Throw New Exception(erreur)
    End Sub

Bu prosedür, silinecek ürün anahtarını parametre olarak alır. Güncellemeleri yapacak tek bir müşteri olsaydı, bu silme işlemi şu şekilde gerçekleştirilebilirdi:

             ' satırın silinmesi [idProduit]
            dtProduits.Rows.Find(idProduit).Delete()

Aynı anda güncelleme yapan birden fazla müşteri olduğunda durum daha karmaşıktır. Nitekim, şu zaman dizisini ele alalım:

Zaman
Eylem
T1
Müşteri A, [dtProduits] tablosunu okur – burada anahtar değeri 20 olan bir ürün vardır
T2
Müşteri B, [dtProduits] tablosunu okur - anahtar 20'ye ait bir ürün vardır
T3
A istemcisi, anahtar 20'ye ait ürünü siler
T4
B müşterisi, anahtar 20'ye ait ürünü siler

A ve B istemcileri ürün tablosunu okuduğunda ve içeriğini bir web sayfasında görüntülediklerinde, tabloda anahtar 20'ye ait ürün bulunmaktadır. Dolayısıyla bu ürün üzerinde bir işlem yapmak isteyebilirler, örneğin onu silmek gibi. İkisinden biri mutlaka bunu ilk yapacak. Daha sonra gelen müşteri ise artık mevcut olmayan bir ürünü silmek isteyecektir. Bu durum, [supprimerProduits] prosedürü tarafından çeşitli şekillerde ele alınır:

  • öncelikle [Application.Lock] ile senkronizasyon yapılır. Bu, bir istemci bu bariyeri geçtiğinde, ürün tablosunu değiştirecek veya okuyacak başka bir istemci olmayacağı anlamına gelir. Aslında bu işlemlerin tümü bu şekilde senkronize edilir. Okuma işlemi için bunu daha önce görmüştük.
  • Ardından, silinmek istenen ürünün mevcut olup olmadığı kontrol edilir. Varsa, ürün silinir; yoksa bir hata mesajı hazırlanır.

             ' silinecek satır aranıyor
            Dim ligne As DataRow = dtProduits.Rows.Find(idProduit)
            If ligne Is Nothing Then
                erreur = String.Format("Produit [{0}] inexistant", idProduit)
            Else
                 ' satırın silinmesi
                ligne.Delete()
            End If
  • Diğer müşterilerin güncellemelerini yapabilmelerini sağlamak için [Application.Unlock] komutuyla kritik sekansın dışına çıkılır.
  • Satır silinememişse, prosedür bir hata mesajıyla ilişkili bir istisna oluşturur.

Bir örneğe bakalım. [Mozilla] istemcisini başlatıyoruz ve hemen [Mise à jour] seçeneğini (kısmi görünüm) seçiyoruz:

Image

Aynı işlemi [Internet Explorer] istemcisi için de yapıyoruz (kısmi görünüm):

Image

[Mozilla] müşterisiyle [produit2] ürününü siliyoruz. Sonuç olarak şu yeni sayfayı elde ediyoruz:

Image

Silme işlemi başarıyla tamamlandı. Silinen ürün, silinen ürünler listesindeki [DataGrid] olarak görünüyor ve şu anda tabloda bulunan ürünleri gösteren [DataGrid1] ve [DataGrid2] bileşenlerinin ürün listesinde artık görünmüyor. Aynı işlemi [Interbet Explorer] için de yapalım. [produit2] öğesini siliyoruz:

Image

Elde edilen sonuç şöyledir:

Image

Bir hata mesajı, kullanıcıya [2] anahtar ürününün mevcut olmadığını bildirir. Yanıt, müşteriye tablonun mevcut durumunu yansıtan yeni bir görünüm gönderir. Görüldüğü gibi, [2] anahtar ürünü gerçekten de silinen ürünler listesinde yer almaktadır. Dolayısıyla, [produits] görünümü, tek bir müşterinin değil, tüm müşterilerin güncellemelerini yansıtmaktadır.

9.6.14. Ürün Ekleme

Bir ürün eklemek için kullanıcı, seçeneklerdeki [Ajout] bağlantısını kullanır. Bu bağlantı, yalnızca [Ajout] görünümünü görüntüler:

Image

Image

Bu işlemde yer alan iki prosedür şunlardır:


    Private Sub lnkAjout_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkAjout.Click
        ' [ajout] görünümü görüntülenir
        afficheAjout()
    End Sub

    Private Sub afficheAjout()
        ' ekleme görünümünü görüntüler
        vueAjout.Visible = True
        vueFormulaire.Visible = False
        vueProduits.Visible = False
        vueErreurs.Visible = False
    End Sub

[Ajout] görünümünün aşağıdaki doğrulama bileşenlerine sahip olduğunu hatırlatmak isteriz:

ad
tür
rol
rfvNom
RequiredFieldValidator
[txtNom]'te bir değer olup olmadığını kontrol eder
rfvPrix
RequiredFieldValidator
[txtPrix]'te bir değer olup olmadığını kontrol eder
cvPrix
CompareValidator
fiyatın >= 0 olup olmadığını kontrol eder

[Ajouter] düğmesine tıklama işleme prosedürü şu şekildedir:


    Private Sub btnAjouter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAjouter.Click
        ' ürün tablosuna yeni bir öğe ekleniyor
        ' öncelikle verilerin geçerli olması gerekir
        rfvNom.Validate()
        rfvPrix.Validate()
        cvPrix.Validate()
        If Not rfvNom.IsValid Or Not rfvPrix.IsValid Or Not cvPrix.IsValid Then
            ' giriş formu tekrar görüntüleniyor
            afficheAjout()
            Exit Sub
        End If
        ' bir satır oluşturuluyor
        Dim produit As DataRow = dtProduits.NewRow
        produit("nom") = txtNom.Text.Trim
        produit("prix") = txtPrix.Text.Trim
        ' satırı tabloya ekleniyor
        Application.Lock()
        Try
            dtProduits.Rows.Add(produit)
            lblInfo3.Text = "Ajout réussi"
            ' temizleme
            txtNom.Text = ""
            txtPrix.Text = ""
        Catch ex As Exception
            lblInfo3.Text = String.Format("Erreur : {0}", ex.Message)
        End Try
        Application.UnLock()
    End Sub

Öncelikle, üç bileşen olan [rfvNom, rfvPrix, cvPrix] için geçerlilik denetimleri gerçekleştirilir. Denetimlerden herhangi biri başarısız olursa, [Ajout] görünümü, geçerlilik denetim bileşenlerine ait hata mesajlarıyla birlikte yeniden görüntülenir. İşte bir örnek:

Image

Veriler geçerliyse, [dtProduits] tablosuna eklenecek satır hazırlanır.


         ' bir satır oluşturuluyor
        Dim produit As DataRow = dtProduits.NewRow
        produit("nom") = txtNom.Text.Trim
        produit("prix") = txtPrix.Text.Trim

Öncelikle, [DataTable.NewRow] yöntemi kullanılarak [dtProduits] tablosuna yeni bir satır oluşturulur. Bu satır, [dtProduits] tablosundaki [id, nom, prix] adlı üç sütunu içerecektir. [nom, prix] sütunları, ekleme formuna girilen değerlerle doldurulur. [id] sütunu ise doldurulmaz. Bu sütun, [AutoIncrement] ve c.a.d türündedir. SGBD, yeni bir ürüne anahtar olarak mevcut en yüksek anahtar değerine 1 eklenmiş değeri verecektir. Burada oluşturulan [produit] satırı, [dtProduits] tablosundan ayrılmıştır. Şimdi bunu bu tabloya eklememiz gerekiyor. [dtProduits] tablosunu güncelleyeceğimiz için istemciler arası senkronizasyonu etkinleştiriyoruz:

        Application.Lock()

Bu işlem tamamlandıktan sonra, satırı [dtProduits] tablosuna eklemeye çalışıyoruz. Başarılı olursak bir başarı mesajı, aksi takdirde bir hata mesajı görüntülenir.

        Try
            dtProduits.Rows.Add(produit)
            lblInfo3.Text = "Ajout réussi"
             ' temizleme
            txtNom.Text = ""
            txtPrix.Text = ""
        Catch ex As Exception
            lblInfo3.Text = String.Format("Erreur : {0}", ex.Message)
        End Try

Normalde herhangi bir istisna söz konusu değildir. Yine de tedbir amaçlı olarak bir istisna yönetimi kurulmuştur. Ekleme işlemi tamamlandıktan sonra, [Application.Unlock] komutuyla kritik bölümden çıkılır.

9.6.15. Bir ürünün değiştirilmesi

Bir ürünü değiştirmek için kullanıcı, ürün satırındaki [Modifier] bağlantısını kullanır:

Image

Böylece düzenleme moduna geçilir:

Image

[DataGrid] bileşeni sayesinde bu sonuç çok az kodla elde edilir:


    Private Sub DataGrid1_EditCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.EditCommand
        ' geçerli öğeyi düzenleme moduna geçirilir
        DataGrid1.EditItemIndex = e.Item.ItemIndex
        ' ürünleri yeniden görüntüler
        afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize)
    End Sub

[Modifier] bağlantısına tıklandığında, sunucu tarafında [DataGrid1_EditCommand] prosedürü yürütülür. [DataGrid1] bileşeninde [EditItemIndex] adlı bir alan bulunur. [EditItemIndex] değerine sahip satır düzenleme moduna alınır. Bu şekilde güncellenen satırdaki her bir değer, yukarıdaki ekran görüntüsünde gösterildiği gibi bir giriş kutusunda değiştirilebilir. Böylece, [Modifier] bağlantısına tıklandığında ilgili ürünün indeksini alıyoruz ve bunu [DataGrid1] bileşeninin [EditItemIndex] özniteliğine atıyoruz. Artık [produits] görünümünü yeniden görüntülememiz yeterlidir. Görünüm eskisiyle aynı görünecek, ancak bir satır düzenleme modunda olacaktır.

Düzenlenmekte olan ürünün [Annuler] bağlantısı, kullanıcının güncellemeyi iptal etmesine olanak tanır. Bu bağlantıyla ilgili prosedür şöyledir:


    Private Sub DataGrid1_CancelCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.CancelCommand
        ' ürünleri yeniden görüntülenir
        afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize)
    End Sub

Bu işlem, yalnızca [produits] görünümünü yeniden görüntüler. Güncelleme modunu iptal etmek için [DataGrid1.EditItemIndex]'e -1 girmek cazip gelebilir. Aslında, [Page_Load] prosedürünün bunu sistematik olarak yaptığını biliyoruz. Dolayısıyla bunu tekrar yapmaya gerek yoktur.

Değişiklik, düzenlenmekte olan satırdaki [Mettre à jour] bağlantısı ile onaylanır. Ardından aşağıdaki prosedür yürütülür:


    Private Sub DataGrid1_UpdateCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.UpdateCommand
        ' değiştirilecek ürün anahtarı
        Dim idProduit As Integer = CType(DataGrid1.DataKeys(e.Item.ItemIndex), Integer)
        ' değiştirilen öğeler
        Dim nom As String = CType(e.Item.Cells(0).Controls(0), TextBox).Text.Trim
        Dim prix As String = CType(e.Item.Cells(1).Controls(0), TextBox).Text.Trim
        ' değişiklikler geçerli mi?
        lblInfo2.Text = ""
        If nom = String.Empty Then lblInfo2.Text += "[Indiquez un nom]"
        If prix = String.Empty Then
            lblInfo2.Text += "[Indiquez un prix]"
        Else
            Dim nouveauPrix As Double
            Try
                nouveauPrix = CType(prix, Double)
                If nouveauPrix < 0 Then Throw New Exception
            Catch ex As Exception
                lblInfo2.Text += "[prix invalide]"
            End Try
        End If
        '  Hata varsa, güncelleme sayfası yeniden görüntülenir
        If lblInfo2.Text <> String.Empty Then
            ' satırı güncelleme moduna geri alın
            DataGrid1.EditItemIndex = e.Item.ItemIndex
            ' ürünlerin görüntülenmesi
            afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize)
            ' son
            Exit Sub
        End If
        ' hata yoksa - tabloyu değiştir
        Try
            modifierProduit(idProduit, e.Item)
        Catch ex As Exception
            ' sorun
            lblInfo2.Text = ex.Message
        End Try
        ' ürünlerin görüntülenmesi
        afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize)
    End Sub

Silme işleminde olduğu gibi, [dtProduits] ürün tablosunda ilgili satırı bulabilmek için değiştirilecek ürünün anahtarını almamız gerekiyor:


         ' değiştirilecek ürün anahtarı
        Dim idProduit As Integer = CType(DataGrid1.DataKeys(e.Item.ItemIndex), Integer)

Ardından, satıra atanacak yeni değerleri almamız gerekiyor. Bu değerler [DataGrid1] bileşeninde yer alıyor. Prosedürün [e] argümanı bize bu konuda yardımcı oluyor. [e.Item], olayın kaynağı olan [DataGrid1] satırını temsil eder. Dolayısıyla, [Mettre à jour] bağlantısı yalnızca bu satırda mevcut olduğundan, güncellenmekte olan satır budur. Bu satır, satırın [Cells] koleksiyonu ile belirtilen sütunları içerir. Dolayısıyla [e.Item.Cells(0)], güncellenen satırın 0. sütununu temsil eder. Yeni değerlerin giriş alanlarında olduğunu biliyoruz. [e.Item.Cells(i).Controls] koleksiyonu, [e.Item] satırının i numaralı sütunundaki denetimlerin koleksiyonunu temsil eder. Aşağıdaki iki komut, güncellenen satırın giriş kutularındaki değerleri [DataGrid1]'te alır:


         ' değiştirilen öğeler
        Dim nom As String = CType(e.Item.Cells(0).Controls(0), TextBox).Text.Trim
        Dim prix As String = CType(e.Item.Cells(1).Controls(0), TextBox).Text.Trim

Böylece, değiştirilen satırın yeni değerlerini karakter dizeleri biçiminde elde etmiş oluruz. Ardından bu verilerin geçerli olup olmadığını kontrol ederiz. Ad alanı boş olmamalı ve fiyat pozitif bir sayı veya sıfır olmalıdır:


         ' değişiklikler geçerli mi?
        lblInfo2.Text = ""
        If nom = String.Empty Then lblInfo2.Text += "[Indiquez un nom]"
        If prix = String.Empty Then
            lblInfo2.Text += "[Indiquez un prix]"
        Else
            Dim nouveauPrix As Double
            Try
                nouveauPrix = CType(prix, Double)
                If nouveauPrix < 0 Then Throw New Exception
            Catch ex As Exception
                lblInfo2.Text += "[prix invalide]"
            End Try
        End If

Hata durumunda, [lblInfo2] etiketi bir hata mesajı görüntüler ve aynı sayfa yeniden görüntülenir:


          ' hata varsa, güncelleme sayfası yeniden görüntülenir
        If lblInfo2.Text <> String.Empty Then
            ' satırı güncelleme moduna geri alın
            DataGrid1.EditItemIndex = e.Item.ItemIndex
            ' ürünlerin görüntülenmesi
            afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize)
             ' son
            Exit Sub
        End If

Yukarıdaki kodda gösterilmeyen şey, girilen değerlerin kaybolduğudur. Aslında, [DataGrid1], değiştirilen satırın orijinal değerlerini içeren [dtProduits] tablosundaki verilerle bağlantılıdır. İşte bir örnek.

Image

Alınan yanıt şöyledir:

Image

Görüldüğü gibi, başlangıçta girilen değerler kaybolmuştur. Profesyonel bir uygulamada bu durum muhtemelen kabul edilemez. Burada, [DataGrid] bileşeninin standart güncelleme modunun bazı sınırlarına ulaşılmış oluyor. [Ajout] görünümüne benzer bir [Modification] görünümüne sahip olmak daha uygun olurdu.

Veriler geçerliyse, [dtProduits] tablosunu güncellemek için kullanılır:


         ' hata yoksa - tablo değiştirilir
        Try
            modifierProduit(idProduit, e.Item)
        Catch ex As Exception
            ' sorun
            lblInfo2.Text = ex.Message
        End Try
        ' ürünlerin görüntülenmesi
        afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize)

Bir ürünün silinmesi sırasında belirtilen aynı nedenden ötürü, değişiklik başarısız olabilir. Nitekim, müşterinin [dtProduits] tablosunu okuduğu an ile ürünlerinden birini değiştireceği an arasında, söz konusu ürün başka bir müşteri tarafından silinmiş olabilir. [modifierProduit] prosedürü, bu durumu bir istisna oluşturarak yönetir. Bu istisna burada işlenir. Güncelleme başarılı veya başarısız olduktan sonra, uygulama [produits] görünümünü müşteriye geri döndürür. Şimdi [modifierProduit] prosedürünün güncellemeyi nasıl gerçekleştirdiğine bakalım:


    Private Sub modifierProduit(ByVal idProduit As Integer, ByVal item As DataGridItem)
        Dim erreur As String
        Try
            ' senkronizasyon
            Application.Lock()
            ' değiştirilecek satırın aranması
            Dim ligne As DataRow = dtProduits.Rows.Find(idProduit)
            If ligne Is Nothing Then
                erreur = String.Format("Produit [{0}] inexistant", idProduit)
            Else
                ' satırı değiştiriyoruz
                With ligne
                    .Item("nom") = CType(item.Cells(0).Controls(0), TextBox).Text.Trim
                    .Item("prix") = CType(item.Cells(1).Controls(0), TextBox).Text.Trim
                End With
            End If
        Catch ex As Exception
            erreur = String.Format("Erreur lors de la modification : {0}", ex.Message)
        Finally
            ' senkronizasyonun sonu
            Application.UnLock()
        End Try
        ' hata durumunda istisna atılır
        If erreur <> String.Empty Then Throw New Exception(erreur)
    End Sub

Kodu, daha önce ayrıntılı olarak açıklanan [supprimerProduit] prosedürüne benzeyen bu prosedürün ayrıntılarına girmeyeceğiz. Sadece iki örnek üzerinde duralım. Öncelikle, [produit1] ürününün fiyatını değiştiriyoruz:

Image

Yukarıdaki [Mettre à jour] bağlantısının kullanılması şu yanıtı verir:

Image

Değişiklik, [DataGrid] 1 ve 2 bileşenlerinde açıkça görülmektedir; bu bileşenler, [dtProduits] tablosunun güncel durumunu yansıtmaktadır. Değişiklik, değiştirilen satırları gösteren ve bu satırları orijinal değerleriyle birlikte görüntüleyen [DataGrid4] bileşeninde de mevcuttur. Şimdi bir erişim çakışması durumuna bakalım. Silme örneğinde olduğu gibi, iki farklı web istemcisi kullanacağız. [Mozilla] istemcisi, [dtProduits] tablosunu okur ve [produit1] ürününü düzenlemeye başlar:

Image

[Internet Explorer] numaralı bir müşteri, [produit1] numaralı ürünü silmek üzere:

Image

[Internet Explorer] müşterisi, [produit1] ürününü siliyor:

Image

[produit1]'in artık değiştirilen satırlar tablosunda değil, silinen satırlar tablosunda yer aldığına dikkat edilmelidir. [Mozilla] istemcisi ise güncellemesini onaylıyor:

Image

[Mozilla] istemcisi, güncellemesine şu yanıtı alır:

Image

[produit1]'in silindiğini, silinen ürünler listesinde yer aldığından görebilir.

9.7. Ürünlerin fiziksel tablosunu güncelleyen web uygulaması

9.7.1. Önerilen çözümler

Önceki uygulama, [DataTable] nesnesinin önbellekte yönetilmesini göstermek amacıyla hazırlanmış bir örnek olaydan ibaretti; gerçek hayatta sık karşılaşılan bir durum değildi. Aslında, er ya da geç, verilerin gerçek kaynağını güncellemek gerekir. Bu konuda iki farklı strateji izlenebilir:

  1. Verilerin kaynağını güncellemek için bellekteki [dtProduits] önbelleğini kullanabiliriz. Önceki uygulamanın web dizin yapısında, bu uygulamanın [dtProduits] önbelleğine erişim sağlamak için bir sayfa oluşturulabilir. Bu sayfa, bir yöneticiye [dtProduits] önbelleğindeki değişiklikleri fiziksel veri kaynağına yansıtma olanağı sunar. Bunu yapmak için, [produits] erişim sınıfına, [dtProduits] önbelleğini parametre olarak kabul eden ve bu önbellek ile fiziksel veri kaynağını güncelleyen yeni bir yöntem eklenebilir.
  2. Fiziksel veri kaynağı, önbellekle aynı anda güncellenir.

1 numaralı strateji, fiziksel veri kaynağıyla yalnızca bir bağlantı açılmasına olanak tanır. 2 numaralı strateji ise her güncellemede bir bağlantı gerektirir. Bağlantıların kullanılabilirliğine göre bu stratejilerden biri tercih edilebilir. Bunu uygulamak için gerekli araçlara ([produits] sınıfı) sahip olduğumuzdan, 2 numaralı stratejiyi seçiyoruz.

9.7.2. Çözüm 1

Önceki uygulamayla tutarlılık sağlamak amacıyla aşağıdaki stratejiyi seçiyoruz:

  • fiziksel kaynak, önbellek ile aynı anda güncellenir
  • önbellek, [global.asax.vb]'te uygulamanın başlatılması sırasında yalnızca bir kez oluşturulur. Bu, fiziksel veri kaynağının web istemcileri dışındaki diğer istemciler tarafından güncellenmesi durumunda, web istemcilerinin bu değişiklikleri görmeyeceği anlamına gelir. Yalnızca kendilerinin önbellekteki tabloya yaptıkları değişiklikleri görürler.

Fiziksel veri kaynağını güncellemek için, ürün erişim sınıfının bir örneğine ihtiyacımız var. Her istemcinin kendine ait bir örneği olabilir. Alternatif olarak, uygulamanın başlatılması sırasında oluşturulacak tek bir örneği paylaşabiliriz. Burada tercih ettiğimiz çözüm budur. [global.asax.vb] kontrol kodu şu şekilde değiştirilir:


Imports System
Imports System.Web
...

Public Class Global
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' yapılandırma bilgilerini alınıyor
        Dim chaînedeConnexion As String = ConfigurationSettings.AppSettings("OLEDBStringConnection")
        Dim defaultProduitsPage As String = ConfigurationSettings.AppSettings("defaultProduitsPage")
        Dim erreurs As New ArrayList
...
        ' burada yapılandırma hatası yok
        ' ürün nesnesi oluşturuluyor
        Dim objProduits As New produits(chaînedeConnexion)
        Dim dtProduits As DataTable
        Try
            dtProduits = objProduits.getProduits
        Catch ex As ExceptionProduits
            'Ürünlere erişimde hata oluştu, bu durum uygulamada kaydedilir
            Application("erreurs") = ex.erreurs
            Exit Sub
        Catch ex As Exception
            ' yönetilemeyen hata
            erreurs.Add(ex.Message)
            Application("erreurs") = erreurs
            ' alt işlevden çıkış
        End Try
        ' burada başlatma hatası yok
...
        ' veri erişim örneği kaydediliyor
        Application("objProduits") = objProduits
    End Sub

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
...
    End Sub
End Class

Uygulamada, [objProduits] anahtarıyla ilişkili bir veri erişim sınıfı örneği depolanmıştır. Her istemci, fiziksel veri kaynağına erişmek için bu örneği kullanacaktır. Bu örnek, [main.aspx.vb]'in [Page_Load] yordamından alınacaktır:


    Protected objProduits As produits
....
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
        ' ürünlere erişim örneği alınıyor
        objProduits = CType(Application("objProduits"), produits)
...
    End Sub

[objProduits] veri erişim örneğine sayfanın tüm yöntemlerinden erişilebilir. Bu örnek, ekleme, silme ve değiştirme olmak üzere üç güncelleme işlemi için kullanılacaktır.

Ekleme prosedürü şu şekilde değiştirilir:


    Private Sub btnAjouter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAjouter.Click
...
        ' satırı ekleniyor
        Application.Lock()
        Try
            ' satırı önbellek tablosuna ekleniyor
            dtProduits.Rows.Add(produit)
             ' satırı fiziksel kaynağa eklenir
            Dim nouveauProduit As sProduit
            With nouveauProduit
                .nom = CType(produit("nom"), String)
                .prix = CType(produit("prix"), Double)
            End With
            objProduits.ajouterProduit(nouveauProduit)
            ' izleme
            lblInfo3.Text = "Ajout réussi"
            ' temizleme
            txtNom.Text = ""
            txtPrix.Text = ""
        Catch ex As Exception
            ' hata
            lblInfo3.Text = String.Format("Erreur : {0}", ex.Message)
        End Try
        Application.UnLock()
    End Sub

Değişiklik prosedürü aşağıdaki şekilde değiştirilmiştir:


    Private Sub modifierProduit(ByVal idProduit As Integer, ByVal item As DataGridItem)
        Dim erreur As String
        Try
            ' senkronizasyon
            Application.Lock()
            ' değiştirilecek satırı arama
            Dim ligne As DataRow = dtProduits.Rows.Find(idProduit)
            If ligne Is Nothing Then
                erreur = String.Format("Produit [{0}] inexistant", idProduit)
            Else
                ' önbellekteki satır değiştiriliyor
                With ligne
                    .Item("nom") = CType(item.Cells(0).Controls(0), TextBox).Text.Trim
                    .Item("prix") = CType(item.Cells(1).Controls(0), TextBox).Text.Trim
                End With
                 ' satır fiziksel kaynakta değiştiriliyor
                Dim nouveauProduit As sProduit
                With nouveauProduit
                    .id = idProduit
                    .nom = CType(ligne.Item("nom"), String)
                    .prix = CType(ligne.Item("prix"), Double)
                End With
                objProduits.modifierProduit(nouveauProduit)
            End If
        Catch ex As Exception
            erreur = String.Format("Erreur lors de la modification : {0}", ex.Message)
        Finally
            ' senkronizasyonun sonu
            Application.UnLock()
        End Try
        ' hata durumunda istisna atılıyor
        If erreur <> String.Empty Then Throw New Exception(erreur)
    End Sub

Silme prosedürü aşağıdaki şekilde değiştirilmiştir:


    Private Sub supprimerProduit(ByVal idProduit As Integer)
        Dim erreur As String
        Try
            ' senkronizasyon
            Application.Lock()
            ' silinecek satır aranıyor
            Dim ligne As DataRow = dtProduits.Rows.Find(idProduit)
            If ligne Is Nothing Then
                erreur = String.Format("Produit [{0}] inexistant", idProduit)
            Else
                ' önbellekteki satırın silinmesi
                ligne.Delete()
                 ' satırın fiziksel kaynaktan silinmesi
                objProduits.supprimerProduit(idProduit)
            End If
        Catch ex As Exception
            erreur = String.Format("Erreur de suppression : {0}", ex.Message)
        Finally
            ' senkronizasyonun sonu
            Application.UnLock()
        End Try
        ' hata durumunda istisna atılır
        If erreur <> String.Empty Then Throw New Exception(erreur)
    End Sub

9.7.3. Testler

ACCESS dosyasındaki aşağıdaki veri tablosundan yola çıkıyoruz:

Image

Bir web istemcisi başlatılır:

Image

Bir ürün ekliyoruz:

Image

[produit1]'i siliyoruz:

Image

[produit2]'in fiyatını değiştiriyoruz:

Image

Bu değişikliklerin ardından, ACCESS veritabanındaki [liste] tablosunun içeriğine bakıyoruz:

Image

Üç değişiklik de fiziksel tabloda doğru şekilde yansıtılmıştır. Şimdi bir çakışma durumunu inceleyelim. ACCESS'in hemen altındaki [produit2] satırını siliyoruz:

Image

Web istemcimize geri dönelim. İstemci, yapılan silme işlemini görmüyor ve [produit2]'i de silmek istiyor:

Image

Şu yanıtı alıyor:

Image

[produit2] satırı, silinen ürünler listesinde de görüldüğü gibi önbellekten başarıyla kaldırılmıştır. Hata mesajında da belirtildiği gibi, fiziksel kaynakta [produit2]'in silinmesi başarısız olmuştur.

9.7.4. Çözüm 2

Önceki çözümde, web istemcileri fiziksel veri kaynağını eşzamanlı olarak güncelliyor ancak diğer istemciler tarafından yapılan değişiklikleri göremiyorlar. Yalnızca kendi değişikliklerini görüyorlar. Şimdi, bir istemcinin fiziksel veri kaynağını uygulamanın başlatıldığı andaki haliyle değil, o anki haliyle görebilmesini istiyoruz. Bunun için istemciye yeni bir seçenek sunacağız:

Image

[Rafraîchir] seçeneği ile istemci, fiziksel veri kaynağının yeniden okunmasını zorlar. Bunun diğer istemcileri etkilememesi için, bu okumadan elde edilen tablonun yenileme işlemini gerçekleştiren istemciye ait olması ve diğer istemcilerle paylaşılmaması gerekir. Bu, önceki uygulamadan ilk farkıdır. Veri kaynağının [dtProduits] önbelleği, uygulama tarafından değil, her bir istemci tarafından oluşturulacaktır. Değişiklik, [global.asax.vb]'te yapılmıştır:


Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Data
Imports Microsoft.VisualBasic
Imports System.Collections

Public Class Global
    Inherits System.Web.HttpApplication

    Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' yapılandırma bilgileri alınır
...
        ' burada yapılandırma hatası yok
        ' ürün nesnesi oluşturuluyor
        Dim objProduits As New produits(chaînedeConnexion)
        ' sayfa başına ürün sayısı kaydedilir
        Application("defaultProduitsPage") = defaultProduitsPage
        ' veri erişim örneğini kaydediyoruz
        Application("objProduits") = objProduits
    End Sub

    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' oturum değişkenleri başlatılıyor
        If IsNothing(Application("erreurs")) Then
            ' veri kaynağını önbelleğe alınıyor
            Dim dtProduits As DataTable
            Try
                Application.Lock()
                dtProduits = CType(Application("objProduits"), produits).getProduits
            Catch ex As ExceptionProduits
                'Ürünlere erişimde hata oluştu, bu durum oturuma kaydedilir
                Session("erreurs") = ex.erreurs
                Exit Sub
            Finally
                Application.UnLock()
            End Try
            ' [dtProduits] önbelleği oturuma ekleniyor
            Session("dtProduits") = dtProduits
            ' ürün tablosu görüntüleniyor
            Session("dvProduits") = dtProduits.DefaultView
            ' sayfa başına ürün sayısı
            Session("nbProduitsPage") = Application("defaultProduitsPage")
            ' Görüntülenen mevcut sayfa
            Session("pageCourante") = 0
        End If
    End Sub

End Class

Oturum bilgisi, [Page_Load] prosedüründeki her istekte alınır:


     ' sayfa verileri
    Protected dtProduits As DataTable
    Protected dvProduits As DataView
    Protected objProduits As produits
    Protected nbProduitsPage As Integer
    Protected pageCourante As Integer

    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' uygulamada hata olup olmadığı kontrol ediliyor
        If Not IsNothing(Application("erreurs")) Then
            ' uygulama düzgün bir şekilde başlatılamadı
            afficheErreurs(CType(Application("erreurs"), ArrayList))
            Exit Sub
        End If
        ' oturumda hata olup olmadığı kontrol ediliyor
        If Not IsNothing(Session("erreurs")) Then
            ' oturum düzgün bir şekilde başlatılamadı
            afficheErreurs(CType(Session("erreurs"), ArrayList))
            Exit Sub
        End If
        ' ürün tablosundan bir referans alınır
        dtProduits = CType(Session("dtProduits"), DataTable)
        ' ürünler sayfasını alınıyor
        dvProduits = CType(Session("dvProduits"), DataView)
        ' sayfa başına ürün sayısı alınır
        nbProduitsPage = CType(Session("nbProduitsPage"), Integer)
        ' geçerli sayfa alınmaktadır
        pageCourante = CType(Session("pageCourante"), Integer)
        ' ürünlere erişim örneği alınır
        objProduits = CType(Application("objProduits"), produits)
        ' devam eden bir güncellemeyi iptal eder
        DataGrid1.EditItemIndex = -1
        'İlk istek
        If Not IsPostBack Then
            ' ilk form görüntülenir
            txtPages.Text = nbProduitsPage.ToString
            afficheFormulaire()
        End If
    End Sub

Oturumdan alınan bilgiler, her istek sonunda bu oturumun sonuna kaydedilecektir:


    Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.PreRender
        ' bazı bilgiler oturumda saklanır
        Session("dtProduits") = dtProduits
        Session("dvProduits") = dvProduits
        Session("nbProduitsPage") = nbProduitsPage
        Session("pageCourante") = pageCourante
    End Sub

[PreRender] olayı, yanıtın müşteriye gönderileceğini bildiren olaydır. Bu fırsatı değerlendirerek, saklanması gereken tüm verileri oturumda kaydederiz. Çoğu zaman sadece bazı verilerin değerleri değiştiği için bu işlem aşırıya kaçabilir. Ancak bu sistematik kaydetme işleminin avantajı, sayfanın diğer yöntemlerinde oturum yönetiminden bizi kurtarmasıdır.

Önbelleğin yenilenmesi işlemi aşağıdaki prosedürle gerçekleştirilir:


    Private Sub lnkRefresh_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkRefresh.Click
        ' [dtProduits] önbelleğini fiziksel veri kaynağıyla yenilenmesi gerekiyor
        ' senkronizasyon başlıyor
        Application.Lock()
        Try
            ' ürün tablosu
            dtProduits = CType(Application("objProduits"), produits).getProduits
            ' mevcut filtre kaydediliyor
            Dim filtre As String = dvProduits.RowFilter
            ' yeni filtrelenmiş görünüm oluşturuluyor
            dvProduits = New DataView(dtProduits)
            ' filtreyi geri yükleniyor
            dvProduits.RowFilter = filtre
        Catch ex As ExceptionProduits
            'ürünlere erişimde hata oluştu, oturuma kaydediliyor
            Session("erreurs") = ex.erreurs
            ' görünüm görüntülenir [erreurs]
            afficheErreurs(ex.erreurs)
            ' işlem tamamlandı
            Exit Sub
        Finally
            ' senkronizasyon tamamlandı
            Application.UnLock()
        End Try
        ' işlem başarıyla tamamlandı - ürünler 1. sayfadan itibaren görüntüleniyor
        afficheProduits(0, nbProduitsPage)
    End Sub

Bu prosedür, [dtProduits] önbelleği ve [dvProduits] görünümü için yeni değerler oluşturur. Bu değerler, yukarıda açıklanan [Page_PreRender] prosedürü tarafından oturuma yerleştirilecektir. [dtProduits] önbelleği yeniden oluşturulduktan sonra, ürünler ilk sayfadan itibaren görüntülenir.

İşte bir yürütme örneği. [mozilla] istemcisi başlatılır ve ürünleri görüntüler:

Image

[Internet Explorer] istemcisi de aynısını yapar:

Image

[Mozilla] istemcisi, [produit1]'i siler, [produit2]'i değiştirir ve yeni bir ürün ekler. Sonuç olarak şu yeni sayfayı elde eder:

Image

[Internet Explorer] müşterisi, [produit1]'i silmek istiyor.

Image

Aşağıdaki yanıtı alır:

Image

[produit1]'in artık mevcut olmadığı bildirildi. Kullanıcı, yukarıdaki [Rafraîchir] bağlantısını kullanarak önbelleğini yenilemeye karar verdi. Aşağıdaki yanıtı aldı:

Image

Artık [Mozilla] istemcisiyle aynı veri kaynağına sahiptir.

9.8. Conclusion

Bu bölümde, veri konteynerleri ve bunların veri kaynaklarıyla olan bağlantıları üzerinde uzun süre durduk. Son olarak, [DataGrid] bileşenini kullanarak bir veri kaynağını nasıl güncelleyebileceğimizi gösterdik. Veri kaynağı olarak bir veritabanı tablosunu kullandık. [DataGrid] bileşeni veri gösterimini biraz kolaylaştırsa da, asıl zorluk sunum kısmında değil, farklı istemciler tarafından yapılan veri kaynağı güncellemelerinin yönetilmesinde yatmaktadır. Erişim çakışmaları ortaya çıkabilir ve bunların yönetilmesi gerekir. Burada, [Application.Lock] bileşenini kullanarak denetleyicide bu çakışmaları yönettik. Denetleyicinin kendi yetki alanı dışında kalan bu tür ayrıntılarla uğraşmak zorunda kalmaması için, veri kaynağına erişimi veri erişim sınıfında senkronize etmek muhtemelen daha akıllıca olacaktır.

Uygulamada, bir veritabanındaki tablolar ilişkilerle birbirine bağlıdır ve bu tabloların güncellenmesi sırasında bu ilişkiler dikkate alınmalıdır. Bu durum öncelikle veri erişim sınıfını etkiler; bu sınıf, bağımsız bir tablo için gerekli olandan daha karmaşık hale gelir. Ayrıca, genellikle web uygulamasının sunum kısmını da etkiler; çünkü çoğu zaman tek bir tablo değil, ilişkilerle birbirine bağlı tabloların görüntülenmesi gerekir.

Bu bölümde ayrıca, [DataTable, DataView] gibi çeşitli veri yapıları da tanıtılmıştır.