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 |
dize | birincil anahtar | |
dize | liste temasının adı | |
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 |
dize | birincil anahtar | |
dize | liste temasının adı |
Tek sayfalık uygulama şu şekildedir:
![]() |
numara | ad | tür | özellikler | rol |
DataGrid | abone olunabilecek e-posta listeleri | |||
DataList | Kullanıcının yukarıdaki listelere abonelikleri listesi | |||
panel | kullanıcının seçtiği konuya ilişkin bilgi paneli ve bir bağlantı [Plus d'informations] | |||
Etiket | [panelInfos]'in bir parçasıdır | tema adı | ||
Etiket | [panelInfos]'in bir parçasıdır | tema açıklaması | ||
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:

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

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

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:

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ème" HeaderText="Thè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:
<columns>...</columns> bölümünde görüntülenecek sütunları kendimiz belirliyoruz | |
verilerin sayfalandırılması için | |
[DataGrid]'in [thème] (HeaderText) sütununu tanımlar; bu sütun, veri kaynağındaki (DataField) [thème] sütunuyla ilişkilendirilecektir | |
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>
[DataList] başlığının metnini tanımlar | |
[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:
İ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:
kısıtlamanın adı - herhangi bir isim olabilir | |
birincil anahtar olacak sütun - [DataColumn] türü | |
[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:
[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:
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:

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

Üçüncü sayfa:

[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:
[Integer] türü - bu, son istek sırasında görüntülenen geçerli sayfanın numarasıdır | |
[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:
Bu, önceki istek sırasında görüntülenen mevcut sayfanın numarasıdır | |
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
veritabanına bağlantı - SQL komutunun yürütülmesi için açılacak ve hemen ardından kapatılacaktır | |
SQL [select] sorgusu, tüm tabloyu alır [liste] | |
[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. | |
[id] anahtarına sahip [liste] tablosundaki satırın (ad, fiyat) alanlarının güncellenmesini sağlayan sorgu | |
[liste] tablosunda [id] anahtarına sahip satırı silmeye izin veren sorgu | |
[OleDbCommand] nesnesi, [selectText] sorgusunu [connexion] bağlantısı üzerinden yürütür | |
[OleDbCommand] nesnesi, [connexion] bağlantısı üzerinde [updateText] sorgusunu yürütüyor | |
[OleDbCommand] nesnesi, [connexion] bağlantısı üzerinde [insertText] sorgusunu yürütüyor | |
[OleDbCommand] nesnesi, [connexion] bağlantısı üzerinde [deleteText] sorgusunu yürütüyor | |
[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:

[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 |
LinkButton | filtreleme koşulunu belirlemek için kullanılan [Formulaire] görünümünü görüntüler | ||
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 | ||
LinkButton | ürün eklemek için kullanılan [Ajout] görünümünü görüntüler | ||
vueFormulaire | [Formulaire] görünümü | ||
TextBox | filtreleme koşulu | ||
TextBox | sayfa başına ürün sayısı | ||
RequiredFieldValidator | [txtPages]'te bir değer olup olmadığını kontrol eder | ||
RangeValidator | txtPages'in [3,10] aralığında olup olmadığını kontrol eder | ||
(5) numaralı koşulla filtrelenmiş [produits] görünümünü görüntüleyen [submit] düğmesi | |||
Etiket | Hata durumunda görüntülenen bilgilendirme metni |
Örneğin, [formulaire] görünümü aşağıdaki şekilde doldurulmuşsa:

şu sonuç elde edilir:
![]() |
no | ad | tür | rol |
panel | |||
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. | ||
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 | ||
DataGrid | ürün tablosunun tamamını gösterir - güncellemeleri takip etmenizi sağlar | ||
Etiket | özellikle hata durumlarında bilgi metni | ||
DataGrid | ürün tablosunda silinen ürünleri gösterecektir | ||
DataGrid | ürün tablosunda değiştirilen ürünleri gösterecektir | ||
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:
hatların durumuna ilişkin filtre yok | ||
DataViewRowState.CurrentRows | ürün tablosunun mevcut durumunu gösterir | |
DataViewRowState.Deleted | ürün tablosundan silinen satırları gösterir | |
DataViewRowState.ModifiedOriginal | ürün tablosundaki değiştirilen satırları başlangıç değerleriyle birlikte görüntüler | |
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 |
panel | |||
TextBox | ürün adı | ||
RequiredFieldValidator | [txtNom]'te bir değer olup olmadığını kontrol eder | ||
TextBox | ürün fiyatı | ||
RequiredFieldValidator | [txtPrix]'te bir değer olup olmadığını kontrol eder | ||
CompareValidator | fiyatın >= 0 olup olmadığını kontrol eder | ||
Düğme | [submit] ürün ekleme düğmesi | ||
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 |
panel | |||
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):

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

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

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:

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

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:

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 à jour" CancelText="Annuler" EditText="Modifier"></asp:EditCommandColumn>
<asp:ButtonColumn Text="Supprimer" CommandName="Delete"></asp:ButtonColumn>
</Columns>
<PagerStyle NextPageText="Suivant" PrevPageText="Précé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 la table LISTE. Exemple : prix<100 and
prix>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 à jour"
CancelText="Annuler" EditText="Modifier">
</asp:EditCommandColumn>
<asp:ButtonColumn Text="Supprimer" CommandName="Delete">
</asp:ButtonColumn>
</Columns>
<PagerStyle NextPageText="Suivant" PrevPageText="Précé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:
İ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
tüm müşteriler için ortak olan [DataTable] ürün tablosu | |
ürünler için [DataView] görünümü - her müşteriye özgü | |
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:

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

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

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 |
RequiredFieldValidator | [txtPages]'te bir değer olup olmadığını kontrol eder | ||
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:

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:

[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 |
Ü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 | |
ürün tablosunun tamamını görüntüler - güncellemeleri takip etmeyi sağlar | |
ürün tablosunda silinen ürünleri gösterir | |
ürün tablosunda değiştirilen ürünleri gösterir | |
ü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 |
RowStateFilter= DataRowViewState.CurrentRows | |
RowStateFilter= DataRowViewState.Deleted | |
RowStateFilter= DataRowViewState.ModifiedOriginal | |
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:
Uygulama kodunda birkaç kritik bölüm bulunabilir:
İşleyiş şu şekildedir:
- 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.
- 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.
- 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:

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

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

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:

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

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

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

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:

Elde edilen sonuç şöyledir:

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:


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 |
RequiredFieldValidator | [txtNom]'te bir değer olup olmadığını kontrol eder | |
RequiredFieldValidator | [txtPrix]'te bir değer olup olmadığını kontrol eder | |
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:

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

Böylece düzenleme moduna geçilir:

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

Alınan yanıt şöyledir:

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:

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

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:

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

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

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

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

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

Bir web istemcisi başlatılır:

Bir ürün ekliyoruz:

[produit1]'i siliyoruz:

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

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

Üç 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:

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

Şu yanıtı alıyor:

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

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

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

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

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

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

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

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.









