2 Ağustos 2013 Cuma

ASP.NET'de PostBack

ASP.NET'de PostBack


ASP.NET'in temel taşlarından birisi olan Post back ( geri gönderim) bizlere (bizler
farkında olmasak bile) asp.net'de kod yazarken kolaylıklar sağlar ve yazdığımız
kod miktarını oldukça kısaltır. Bu makalemizde ASP.NET'i diğer web programlama dillerinden
ayıran en büyük farklardan birisi olan post back mantığını göreceğiz.

Giriş

ASP geliştiricilerinin iyi bildiği gibi, bir web sayfasında form işleme oldukça güçtür. Genelde form sayfası ve formu veritabanına işleyen sayfa olarak bir form için 2 tane sayfa yapılırdı. Hatta bazen bir form için 3 ve daha fazla sayfaya ihtiyaç olduğuda olurdu. Veya tüm işlemleri tek sayfaya yapmak istediğimiz zaman kodumunuzda bir sürü if else deyimi kullanmak zorunda kalıyorduk. ASP.NET'te artık bir form için tek sayfa yapmak yeterli olmaktadır. Bu işlem ASP.NET'te bulunan PostBack mekanizması sayesinde olmaktadır. ASP.NET'te postback sayesinde formlar her zaman aynı sayfaya submit edilir (sunulur), gerekli işlemler ( form kontrolü, formu bilgilerini veritabanına yazmak gibi) olay yakalayıcı metodlar içine yazılır. Ayrıca PostBack sayesinde form alanlarına kullanıcı tarafından girilen değerler her zaman sabit kalır. Yani form kullanıcıya submit'ten sonra geri dönerse form alanlarda kullanıcının daha önce formda girdiği değerler yazar.

PostBack Nedir?

Aspx sayfaları üzerindeki bir butona veya buton tarzı bir kontrole basıldığında (imagebutton, linkbutton gibi) adres satırından gözükeceği gibi devamlı olarak aynı sayfa karşımıza gelir. Bu işleme postback deniyor. Yani postback bir sayfanın sunucuda tekrak değerlendirilmesi için tekrar sunucuya gönderilmesidir. Postback durumundaki bir sayfa ilk açıldıktan sonra en az bir kez sunucuya gönderilmiştir. Bir sayfa ilk açıldığında postback durumunda değildir. Sayfa üzerinde postback yapabilen bir objeye (buton gibi) tıklandığında sayfa postback durumuna geçer.
Postback'ın bazı dezavantajları vardır. Bunlardan birincisi, sayfayı refresh etmek istendiğinde (tazelendiğinde) browserların bir uyarı mesajı çıkarmasıdır. Diğer bir dezavantajı sayfayı favorilere atmada sorunlar yaşanmasıdır. Sayfayı favorilere eklediğimizde devamlı olarak sayfanın ilk hali gelir. (istediğiniz bilgilere ulaşmak için devamlı olarak sayfada aynı işlemleri yapmak gerekir).
Şekil 1: Web sayfasının sunucu tarafından döndürülmesi.
ASP.NET'te sayfa postback yapıldığında, hangi nesneye basıldığı, nesnenin varsa ek özellikleri sunucuya gönderilir. Sunucuda basylan nesneye göre o nesnenin olay yakalayıcı metodlarını (Event Handler) çalıştırılır. Sunucuya bilgi gönderme işi basit bir javascript metoduyla yapılıyor. Bu metodun adı __doPostBack(eventTarget, eventArgument)'dir ve otomatik olarak ASP.NET tarafından sayfaya konulur.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="BasitSayfa.aspx.cs" Inherits="BasitSayfa" %>
2
3<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
4<html xmlns="http://www.w3.org/1999/xhtml">
5<head runat="server">
6    <title>Untitled Page</title>
7</head>
8<body>
9    <form id="form1" runat="server">
10        <div>
11            <asp:Label ID="Label1" runat="server" Text="basit bir sayfa!!!"></asp:Label>
12            <br />
13            <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
14            <br />
15            <asp:Button ID="Button1" runat="server" Text="buton !!!" />
16            <br />
17            <asp:LinkButton ID="LinkButton1" runat="server">link butonu</asp:LinkButton>
18            <br />
19        </div>
20    </form>
21</body> 
                                               Kod 1: BasitSayfa.aspx dosyası
şekildeki gibi bir basit bir ASP.NET sayfası yapalım. Bu sayfayı çalıştırdığımızda açylan sayfanın html kodları şekilde görüldüğü gibi olacaktır.

<body>
2    <form name="form1" method="post" action="BasitSayfa.aspx" id="form1">
3<div>
4<input type="hidden" name="__EVENTTARGET" id="__EVENTTARGET" value="" />
5<input type="hidden" name="__EVENTARGUMENT" id="__EVENTARGUMENT" value="" />
6<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJLTMwMDkyNjI4ZGTZ7IZZRGLHh5UvKzyW5tMtgm9q8A==" />
7</div>
8<script type="text/javascript">
9<!--
10var theForm = document.forms['form1'];
11if (!theForm) {
12    theForm = document.form1;
13}
14function __doPostBack(eventTarget, eventArgument) {
15    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
16        theForm.__EVENTTARGET.value = eventTarget;
17        theForm.__EVENTARGUMENT.value = eventArgument;
18        theForm.submit();
19    }
20}
21// -->
22</script>
23        <div>
24            <span id="Label1">basit bir sayfa!!!</span>
25            <br />
26            <input name="TextBox1" type="text" id="TextBox1" />
27            <br />
28            <input type="submit" name="Button1" value="buton !!!" id="Button1" />
29            <br />
30            <a id="LinkButton1" href="javascript:__doPostBack('LinkButton1','')">link butonu</a>
31            <br />
32        </div>    
33<div>
34 <input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWBAKjgq2mDALs0bLrBgKM54rGBgLM9PumD8EdOtwzJ8eHg1PZnXzp9WtO0eiA" />
35</div></form>
36</body> 
                                   Kod 2: BasitSayfa.aspx html kodu.
şekilde görüldüğü gibi ASP.NET tarafından oluşturulan sayfada __doPostBack(eventTarget, eventArgument) javascript fonksiyonu otomatik olarak oluşturuldu. Bu fonksiyon sayesinde aspx sayfalarında postback yapılabiliniyor.Bu metod 2 tane parametre alıyor. 1. parametre'de tıklanılan kontrolün id'si, 2. parametrede varsa bu tıklamaya ilişkin ek bilgiler bulunuyor. Bu javascript metodu çalıştığında birinci parametdeki değer __EVENTTARGET adındaki gizli form alanına yazılıyor. 2. parametredeki alan __EVENTARGUMENT gizli alanına yazılıyor. Daha sonra tüm form sunucuya (aynı sayfa üzerine) submit ediliyor. Formda bulunan gizli bu form alanları sayesinde (form submit yapıldığından) ASP.NET hangi kontrolün tıklandığını ve bu kontrole ait ek argumanları bilmektedir ayrıca bu kontrole ait Event Handler metodları var ise (olay yakalayıcı) o metodları çalıştırmaktadır.
Kodda görüldüğü gibi sayfaya eklediğimiz LinkButton sunucu kontrolü'ne tıklandığında __doPostBack('LinkButton1','') metodu çalışıyor. Bu kontrollde ek event argumanı olmadığı için 2. parametre boş bulunuyor. Sunucu kontrolleri bu javascript metodunu render olurken ( html'ye çevrilirken) GetPostBackEventReference metodu çağırarak html kodlarına yazılabilinir. GetPostBackEventReference() metodu IPostBackDataHandler veya IPostBackEventHandler sınıflarının bir metodudur. Yani PostBack yapacak bir sunucu kontrolü bu 2 sınıftan birisini extend yapması gerekmektedir. Ayrıca ileride örneğini yapacağımız gibi bu javascript metodunu sayfanın istediğimiz yerinde çağırabiliriz.

İPUCU:

Kodda görüldüğü gibi Linkbutton kontrolünde _doPostBack('LinkButton1','') metodu vardır fakat buton kontrolünde _doPostBack('LinkButton1','') metodu yoktur. Bunun sebebi butonun türünün submit olmasıdır. Yani bu butona basıldığında sayfadaki form otomatik olarak submit olur. Peki ASP.NET hangi butona basıldığını nasyl biliyor? HTML formları submit edildiğinde form alanlarıyla beraber basylan butonun adıda sunucuya gönderilir. Yani sayfada birden çok buton olsa bile sadece basılan butonun ismi sunucuya gönderileceği için o butonun Event Handler metodları herhangi bir karışıklık yaşanmadan çalıştırılabilinecek.

Javascript ile Kendi Post Back'lerimizi yazmak

Postback işlemleri javascript fonksiyonları ile yapıldığı için biz bu fonksiyonları sayfamızda istediğimiz yere koyarak bu fonksiyonları çalıştırabiliriz. ,Mesela sayfamızda bir link buton var. Bu link butonuna basılmadan önce kullanıcıya bu işlemi yapmak istiyormusunuz diye soracağız. Bunun için sayfamızda gerçek işlemi yapacak bir link button koyuyoruz ve bu link butonun görevini görmesi için yalancı bir link koyacağız. Bu linkin görevi onay penceresi çıkartıp onaylandığı zaman esas linkin tıklanmasını sağlamak olacaktır.

<head runat="server">
2    <style>
3        .gizli{ display:none; }
4    </style>
5    <script language="javascript">
6        function postbackYaptir()
7        {
8            if ( confirm("bu işlemi yapmak istiyormusunuz" ) )
9                __doPostBack('LinkButton1','');
10        }
11    </script>
12</head>
13<body>
14    <form id="form1" runat="server">
15        <asp:Label ID="Label1" runat="server"></asp:Label>
16        <br />
17        <asp:LinkButton ID="LinkButton1" CssClass="gizli" runat="server" CommandArgument="ca" CommandName="cn" OnClick="link_clicked" Visible="False">Test Link Button</asp:LinkButton>
18        <br />
19        <br />
20        <a href="javascript:postbackYaptir()">tıklarsanız</a> uygulamada büyük değişiklikler yapılacaktır.
21    </form>
22</body>
23</html> 
                                Kod 3: javascriptPostBack.aspx dosyası.
Bunu yapmak için öncelikle LinkButtona bir adet Event Handler Metodu ekliyoruz. şekilde görüldüğü gibi link butona tıklamayı yakalayıcı metod olarak link_clicked metodunu ekledik. Daha sonra bu link butonunu sayfada görünmemesi için gizli adında bir sitil sınıfı oluşturduk ve bu sitil sınıfını Link Butona verdik. Bundan sonra bir javascript metodu yazıyoruz. Bu metodun görevi onay penceresi çıkartıp onaylandıktan sonra linkin post back fonksiyonunu çağırmak olacak (yani __doPostBack('LinkButton1','') metodu). Daha sonra yaptığımız bu fonksiyonu bir linke vererek postback'i el ile gerçekleştirmiş bulunuyoruz.
Benzer yöntemle gerekli javascript metodlarını yazarak postback işlemini onclick javascript metodunu destekleyen her alana koyup istediğimiz alana tıklandığında postback işlemi yapabiliriz.

DİKKAT:

ASP.NET tarafından otomatik olarak sayfaya __doPostBack javascript yazılması için sayfada en az bir adet __doPostBack metodunu kullanan bir sunucu kontrolü olması lazım. Eğer örnekte olduğu gibi bir sunucu kontrolünün gözükmesini istemiyorsanız fakat __doPostBack metodunun otomatik olarak oluşturulması isteniyorsa örnekte yaptığımız gibi kullanıcı taraflı bu kontrolün gözükmemesi sağlanmalıdır. Eğer kontrolün gözükmemesi için sunucu kontrolünün Visible değerini false yaparsak, sayfada __doPostBack metodunu kullanan bir kontrol olmayacağı için sayfada otomatik olarak bu metod yazılmayacaktır.
Ayrıca __doPostBack metodunun parametlerini dikkatli girmemiz lazım. Eğer sunucu kontrolü id'sini yanlış girersek istediğimiz olay yakalayıcı metodlar çalışmayacaktır.

Sonuç

Bu makalemizde ASP.NET'in bize sağladığı en büyük kolaylıklardan birisi olan PostBack mantığını gördük. PostBack bir sayfanın sunucuya işlemler yapılması için tekrar geri gönderilmesidir. Sayfa geri döndürüldüğünde sunucu olay yakalayıcı ( event handler) metodlarınu çalıştırıp sayfayı kullanıcıya geri döndürür.
Ayrıca PostBack'in __doPostBack adında bir javascript metodu ile yapıldığını gördük. Bu metodu kullanarak sayfanın istediğimiz yerine basarak sanki bir sunucu kontrolü basılmış gib PostBack yapabiliriz.

Referanslar

  1. MSDN
  2. http://www.codeproject.com/aspnet/ClientObjectPostBack.asp
  3. http://www.eggheadcafe.com/articles/20050609.asp
  4. http://www.xefteri.com/articles/show.cfm?id=18
  5. http://delphi.about.com/library/weekly/aa051705a.htm
  6. http://aspnet.4guysfromrolla.com/articles/111704-1.aspx

ASP.NET'de ViewState

ASP.NET'de ViewState


Bu makalemizde ASP.NET sayfalarında dinamik verileri tutmaya
yarayan View State konusunu göreceğiz.

Giriş

ASPiştiricilerinin iyi bildiği gibi, bir web sayfasında dinamikliği korumak oldukça güçtür. Mesela asp ile yapılmış bir data grid'imiz (veli listeleme tablosu) var. Kullanıcı önce listede 2. sayfaya bastı , daha sonra listeyi id kolonuna göre sıraladı. ASP'de data grid'in doğru çalışması için hem grid sayfasının, hemde grid sıralama kolonunun belli bir yerde tutulması lazımdır. Genelde ASP geliştiricileri bu tip bilgileri tutan bir mekanizma olmadığı için bu bilgileri session nesnesinde veya query string olarak adres satırında tutarlar. Bu da asp geliştiricilerinin dinamik verileri tutmak için onlarca satır kod yazmak zorunda bırakır. ASP.NET bu tür dinamik özellikleri tutmak için ve için view state ve post back mekanizmasını geliştirmiştir. View state ile sayfada bulunan dinamik veriler otomatik olarak view state'te saklanr ve sayfanın bir dahaki gösteriminde bu veriler view state'ten alınarak otomatik olarak sayfadaki dinamik veriler korunur. Bu kısa girişten sonra view state mekanizmasını detaylı olarak inceleyelim.

View State

Önceki makalemde Post Back hakkında gerekli bilgiyi aldıysak şimdi Post Back'ın tamamlayıcısı olan view state'e geçebiliriz. View State aslynda asp.net sayfalarında bulunan gizli(hidden) bir alandır. Bu alanda asp.net sayfasındaki sunucu kontrolleri ve sayfalar ile ilgili dinamik özellikleri tutulur. View State ve postback bir elmanın iki yarısı gibi düşünebiliriz. view state sayesinde sayfa bir önceki sayfa gösterimde saklanan dinamik verilerin alynmasıyla sayfa kullanıcıya en son sunulan hale getirilir, post back sayesinde ise sayfada event handler (olay yakalayıcı) metodların çalışırılmasıyla sayfa istenilen son haline getirilir.
View State ve post back mantığını tamamen kavramak için ilk önce asp.net sayfaları nasıl üretildiğini detayli olarak bilmemiz gerekir. Önceki makalemde IIS'den ASP.NET sayfalarına kadar olanki iş akışını anlatmıştım (IIS & ASP.NET Isbirligi). O makalemde bahsedildiği gibi her http handler sınıfında bir adet ProcessRequest metodu bulunmaktadır ve bu metod asıl http üretme işini üstlenmektedir. Asp.net sayfalaryda (Page sınıfını extend ettikleri için ) birer http handler'dır ve Page sınıfında ProcessRequest metodu bulunmaktadyr. Bu Page sınıfındaki ProcessRequest metodu bir biri ardına bir kaç metod çağırmaktadır. Bu metodlar sayesinde kullanıcıya html cevap döndürülür.
Daha önceki makalede bir miktar bahsedildiği gibi aspx sayfalarının sunumu için ilk önce Http handler Factory sınıfı çalışır. Bu sınıfın görevi o sayfa için otomatik olarak merge edilmiş sınıfı bulmaktır. Merge edilmiş sınıf aspx sayfalarında ön plan ASP.NET taglarının, arka plan kodlarıyla birleştirilip programlama dillerine çevrilmesidir. Programlama diline çevrilmiş dosya WINDOWS\Microsoft.NET\Framework\versiyon\Temporary ASP.NET Files\proje_ady\ klasöründe bulunur. Mesela Default.aspx dosyası için otomatik olarak üretilen sınıfın adı _Default'dır. Kullanıcıdan Default.aspx isteği geldiğinde Http Handler Factory bu klasördeki _Default sınıfını bulur. Daha sonra o sınıfı initialize eder (oluşturur) ve bu sınıfın ProcessRequest metodunu çalıştırır.
şekil 1: ASPX sayfaları oluşum iş akış şeması.
Page sınıfının ProcessRequest Metodu çalıştığında öncelikle sayfadaki sunucu kontrolü ağacı oluşturulur. Bu ağaçın en üst nodu Page (sayfanın kendisidir) 'dir. Bu nodun altında genelde 3 adet alt node vardır. Bunlardan birincisi <form runat="server" tagına kadar olan tüm html kodları Literal kontrolü yapılmasıyla oluşan kontroldür. 2. node HtmlForm nesnesidir. Bu node altında Formun içine koyduğumuz sunucu kontrollerine göre alt nodelar oluşturulur. 3. node(düğüm) ise </form> tagının altındaki html kodlarından oluşturulan bir literal kontroldür.
Kod 1: BasitSayfa.aspx dosyası

şekil 2: Aspx sayfası kontrol ağacı.
Sunucu kontrolü ağacını ASP.NET page directive kısmında trace="true" yazarak görebiliriz. Sekildeki kod'u trace="true" yazarak çalıştırdığımızda şekil 2'deki gibi sonuç sayfası karşımıza çıkacaktır. Ağaç yapısında görüldüğü gibi en üstte Sayfanın kendisi bunulmaktadır. Onun altında LiteralControl ve HtmlHead ve form kontrolü bulunuyor. Form kontrolü altında sayfaya koyduğumuz kontroller ağaç şeklinde karşımıza çıkmaktadır. Ayrıca ağaç yapısı oluşturulurken kontrollerinin özellikleri'de yazılır. Mesela button kontrolünün Text özelliğini ASP.NET tagı ile ( Text="Gönder" ) veya arka plan kodları ile (Button1.Text = "Gönder!!";) belirlemişsek bu girilen değer sayfa ağacı oluşturulma aşamasında Button kontrolüne atanır.
Ağaç yapısı oluştuktan sonra bu metodun içinde şekil 1 belirtildi?i gibi bazı metodlar sırasıyla çalıştırılır. Öncelikle Page sınıfının ve sayfadaki tüm sunucu kontrollerinin Init metodları çalıştırılır. Init metodunda sayfanın ve sunucu kontrollerinin Init olayını handle eden metodlar çalıştırılır. Bu aşamada kontrol ağacı oluşturulduğu için sunucu kontrollere Init metodlarında ulaşılabiliriz.
Init'den sonra eğer sayfa PostBack durumunda ise LoadViewState metodu çalışır. Eğer sayfa ilk açılıyor ise (yani postback durumunda değil ise) bu metod çalışmaz. Bu metodda VievState tutulduğu yerden (varsayılan olarak __VIEWSTATE gizli form alanında tutulur) okunur, daha sonra Page ve Page sınıfının altındaki ViewState değerlerine atanır. Ayrıca view state değerinin geçerliliği kontrol edilir. ( view state'nin kullanıcı tarafından değiştirilmesini önlemek için bazı mekanizmalar vardır. İlerleyen bölümde ayrıntılı olarak bahsedilecektir). Ayrıca alynan view state değişkenleri, kontrollerin özelliklerine atanır ve sayfa bu bölümün sonunda view state değerleri yüklendiğinden kullanıcıya en son gösterilen sayfa ile aynı görünümdedir.
Daha sonra aynı şekilde sayfa eğer PostBack durumunda ise LoadPostbackData metodu çalıştırılır. Bu bölümde postback yapılarak sunucuya gönderilen form alanlarına bakılır. Gönderilen form alanı ile aynı isimde bir sunucu kontrolü var ise bu sunucu kontrolünün LoadPostbackData() metodu çalıştırılır. Bu metod çalıştığında sunucu kontrolü kendi özellik değerlerini formdan gelen değer ile değiştirir.
Örnek vermek gerekirse sayfamızda <asp:TextBox runat="server" Text="isim" ID="txtIsim"/> ASPNET koduyla yazılmış bir Text Boxımız var. Text box'ın sayfa ilk açıldığında içinde yazan değer isim olacaktır. Kullanıcı bu değeri "süleyman" olarak değiştirip sunucuya gönderdiğinde ASP.NET ilk önce LoadPostbackData aşamasında sunucu kontrollerinde txtIsim adında bir kontrol olup - olmadığını kontrol edecek, olduğunu gördüğünde TextBox sınıfının LoadPostbackData() metodunu çalıştıracaktır. Bu metod çalıştığında TextBox formdan dönen "süleyman" değerini kendi Text özelliğine atayacaktır. Böylelikle html oluşturulduğunda kullanıcıya gönderilen sayfada TextBox'ın içinde "isim" yerine "süleyman" yazacaktır.

İPUCU:

LoadPostbackData() metodu IPostBackDataHandler sınıfının bir metodudur. Bu yüzden DropDownList, TextBox gibi PostBack yapıldığında özelliklerini değiştirecek sunucu kontrolleri bu sınıfı extend ederler. Ayrıca eğer kendi sunucu kontrolümüzü yazıyorsak ve bu kontrolün değerlerini sayfadan gelen form alanına göre değiştirmesi gerekiyorsa kontrol için yazdığımız sınıfta bu metodu overwrite(üzerine yazılması) etmemiz gerekmektedir.

DİKKAT:

Genel bir yanılgı olarak ViewState'in formda girilen değerleri tuttuğu zannedilir. Yani form'a basıldığında dönen sayfadaki değerlerin aynı kalmasının sebebi ViewState olduğu zannedilir. Fakat bu PostBack mekanizması tarafından yapılır. ViewState form ile gönderilmeyen, ve dinamik olarak güncellenebilen değerleri tutar. Mesela Label kontrolünün Text özelliği, bu özellik form ile döndürülmez. Bu değer View State'te saklanır. İleride ayrıntısıyla ViewState'in hangi değerleri tuttuğu açıklanacaktır.
LoadPostBack evresinden sonra bizim bildiğimiz Load evresi gelir. Load evresinde Load hareketi için kayıt edilmiş event handler metodları çalıştırılır. (Sayfa için Page_Load adynda Load evresinde çalıştırılıcak metod otomatik olarak Visual Studio tarafından oluşturulur.)
Event bölümünde değişiklik ve tıklama için kayıt edilmiışmetodlar çalıştırılır. Mesela bir buton'un click olayı için yazılmış bir Button1_Click metodu varsa bu aşamada metod çalıştırılır.

DİKKAT:

Önce change metodları daha sonra kontrol tarafından çalıştırılan diğer event handler metodları çalıştırılır. Change metodu textBox kontrolünde bulunan TextChange gibi olayları yakalayan metodlardır. Yani bir değişkeni hem Change metodlarında ve hem de Click metodlarında değiştirirseniz (tabiki her 2 metodun çalıştırılması için uygun kullanıcı hareketleri yapılması lazım. Mesela hem Textbox yazısı değiştirilecek hem de butona basılacak) Click için yazılmış metotdaki son değer işlenir.
Bütün event handler metodları çalıştırıldıktan sonra artık sayfa render (html koduna çavrilmesi) edilerek kullanıcıya gönderilmeye hazırdır. Fakat ASP.NET sayfayı render etmeden önce LoadViewState aşamasından sonra değiştirilen kontrol değişkenlerini (tabi bu değişkenlerin view state'te saklanması gerekmektedir) toplayarak view state'i oluşturur ve bunu bir alanda saklar. (varsayılan alan sayfadaki __VIEWSTATE gizli form alanıdır)
Son olarak sayfadaki tüm sunucu kontrolleri html'ye çevrilir (yani render edilir) ve sayfa kullanıcıya döndürülür.

ViewState Nasıl Çalışır?

Bu aşamaya kadar olan kısımda gördüğümüz ASP.NET sayfalarının nasyl oluştuğunu gördük. Biraz kafamız karışmış olabilir fakat view state'in nasıl çalıştığını anlamak için bu kısımları bilmek gerekmektedir. Ayrıca ilerde kendimize özel Page sınıfları ve sunucu kontrolleri yaptığımızda (Page ve WebControl sınıfını extend eden sınıflar) bu kısımları bilmek oldukça işimize yarayacaktır. Daha önceden belirttiğimiz gibi View State PostBack ile sunucuya gönderilemeyen değerileri tutmak için yapılmış bir mekanizmadır. Bir değerin view state'te tutulması için o değerin view state'de tutulacağının belirtilmesi gerekmektedir.
Kod 2: Label kontrolü Text Özelliği
Mesela Label kontrolünün text özelliği varsayılan olarak view state'de tutulur. Text özelliğini View State'te tutulması için Text'in get ve set özelliği şekildeki gibi yazlmıştır. Kod'da Text özelliği okunduğunda (get yapıldığında) ilk önce View State'te Label kontrolüne ait Text adlı bir veri varmı bakılacak eğer var ise bu değer döndürülecek. Ayrıca Label kontrolünün Text özelliği atandığında ( set olduğunda ) girilen değer View state içine yazılacaktır.
Daha açıklayıcı olması için bir örnek yapacak olursak; mesela sayfamızda bir Label kontrolü var. Ayrıca sayfamızda 2 tane button var. Butonlardan birisine basıldığında Label kontrolünün text özelliğini "butona basıldı" yapacak diğer butona basıldığında hiç bir şey yapılmayacak.
Kod 3: viewState_deneme.aspx dosyası

Kod 4: viewState_deneme.aspx.cs dosyası
Yaptığımız sayfada butonlara basıldığında veya ilk kez çağırıldıgında sırasıyla aşağıda belirtilen olaylar gerçekleşecektir.
şekil 3: Senaryolara göre Label ve View State durumu.
şekilde görüldüğü gibi View State sayfada bulunan sunucu kontrollerinin dinamik özellikleri tutmak için kullanılıyor. View State'ı sayfa oluşumu bitiminde ASP.NET kaydediyor ve tekrar aynı sayfa çağırıldığında ASP.NET view state'i kaydedilen yerden alıp sunucu kontrollerinin dinamik değerlerini View State'ten çıkartıp bu değerleri kontrollere atıyor.

View State Büyüklüğü

Her güzel şeyin bir bedeli olduğu gibi ViewState'inde bir bedeli vardır. View State varsayılan olarak sayfada bulunan __VIEWSTATE gizli form alanına kaydediliyor. Buda istemeden sayfanın büyüklüğünü belli miktar attırıyor. Sayfadaki View State miktarını page directive kısmında trace="true" yazarak görebiliriz.
şekil 4: ASPX sayfası kontrol ağacı ve view state büyüklükleri.
Önceki örneği (1 label 2 buton olan örnek) trace="true" olarak yazıp sayfayı tazelediğimizde şekilde görüldüğü gibi Label sunucu kontrolü'nün html olarak render edilmiş hali 43 Byte ve Label'yn bilgilerini saklayan ViewState büyüklüğü 44 Byte'tır. Sayfada şu anda toplam 44 Byte ViewState bulunmaktadyr. Bu şu anda ihmal edilebilir bir büyüklüktür. Fakat sayfadaki sunucu kontrolü sayısı arttığı zaman ViewState büyüklüğü'de ona orantılı şekilde artmaktadır.Mesela sayfaya bir gridView koyduğumuzda ViewState büyüklüğü 10K kadar olabilmektedir.
Kod 5: gridViewStateOrnek.aspx dosyası
Toplam view State büyüklüğünü öğrenmek için yukardaki kodda olduğu gibi ufak bir javascript fonksiyonu yazalım ve bu fonksiyonu çalıştırmak için bir buton koyalım.
şekil 5: Grid view olan bie sayfada view state büyüklüğü.
şekilde görüldüğü gibi sayfada ASP.NET tarafından üretilen html'nin miktarı 23K (bunu trace=true yaparak öğrenebiliriz.) ayrıca buna ek olarak sayfada 8K kadar View State bulunuyor. Görüldüğü gibi view state sayfaya 1/3 oranında ek yük getiriyor. Buda internet hızı az olan kullanıcılar için sorun olabiliyor.

View State'i Büyüklüğünü Kontrol Altına Alma

Öncelikle iyi bir geliştirici sayfanın büyüklüğünü olabilecek en minimum seviyede tutması gerekmektedir. Çünkü büyük sayfalar kullanıcılara gösterilirken daha fazla zaman alır. Bahsettiğimiz gibi bazen ASP.NET'te view state büyüklüğü oldukça fazla olabilmektedir. Bunu kontrol altına almak için ASP.NET'te view state'i sunucu kontrolü bazlı veya sayfa bazlı disable(devre dışı alma) özelliği vardır. Bu işlemi ASP.NET sunucu kontrollerinde EnableViewState özelliğini false yaparak yapabiliyoruz.
şekil 6: EnableViewState=false yapıldığında view state büyüklüğü.
şekilde görüldüğü gibi view state büyüklüğü 8K olan gridview'li sayfada grid view sunucu kontrolünde EnableViewState'i false yaptığımızda sayfadaki view state büyüklüğü 88 B'ta inmektedir. Fakat view state'i disable yapmanın bazı dezavantajları vardır. Mesela gridView'in view state'inde tablo hücrelerinde yazılan değerler tutulmaktadır. Eğer biz EnableViewState=false yaptığımızda her postback yapılışında gridView verileri view state'ten okuma yerine veritabanından tekrar okuyacaktır. Ayrıca sayfa bazında view state'i disable ettiğimizde sayfadaki hiç bir sunucu kontrolü view state üretmeyecektir.
View state disable etmenin yanında view state'i sayfadaki gizli bir alan yerine Session, dosya veya veritabanında tutabiliyoruz. Fakat bu güçlü web sunucuları için önerilen bir yöntemdir. Çünkü bu işlem web sunucularına ek yük getirmektedir. View state'in form alanına kayıt işlemi Page sınıfında bulunan SavePageStateToPersistenceMedium() metoduyla yapılmaktadır, ve view state'in form alanından okunması işlemi LoadPageStateFromPersistenceMedium() ile yapılmaktadır. Eğer view state'in saklanma yerini değiştirmek istiyorsak bu 2 metodu sayfada overwrite(üzerime yazmamız) etmemiz gerekmektedir.
Aspx sayfalarının kaynak koduna baktığımızda __VIEWSTATE gizli form alanında yazılan değer okunabilir bir yazı değildir. Bunun sebebi view state değerleri yazılmadan önce System.Web.UI.LosFormatter sınıfı tarafından serialize edilmektedir. Ayrıca view state form alanından okunurken aynı sınıf tarafından deserialize edilmektedir. Yapacağımız örnekte Page sınıfındaki SavePageStateToPersistenceMedium ve LoadPageStateFromPersistenceMedium metodlarını overwrite edeceğiz ve LosFormatter sınıfını kullanarak view state değerlerini Session içine kaydedeceğiz.
Kod 6: ViewStateSessionKayit.aspx.cs dosyası
şekildeki kodda görüldüğü gibi view state'i gizli form alanı yerine Session'a kaydediyoruz. Ayrıca LoadViewState kademesinde çalışan LoadPageStateFromPersistenceMedium() metodunda view state'i session'dan okuyup ASP.NET'e döndürüyoruz.
şekil 7: View State'i Session'a kaydedildiğinde view state büyüklüğü.
View state session'a kaydedildiği için sayfadaki view state büyüklüğü 0B'dır. Böylelikle sayfa boyutu önemli ölçüde azalmıştır. Aynı zamanda View State'in bize sağladığı avantajlardan ödün vermemiş bulunuyoruz.

DİKKAT:

Bu kodun çalışması için kullanıcı browser'ında cookies'in devre dışı bırakılmaması gerekmektedir. Eğer devre dışı bırakılmışsa Session çalışmayacağı için view state kullanılamaz. Bu tip durumların önüne geçmek için sayfada gizli bir form alanına bir GUID (global unique id) koyulabilinir ve view state bu ad ile dosya sistemine kaydedilebilinir. Fakat dosya sistemine kaydedilen dosyaların belli aralıklarla silinmesi lazımdır. Aksi halde sistemin disk'i hızla dolabilir.

İPUCU:

Bu kodda view state'i biz sadece tek bir sayfa için Session'a kaydettik. Eğer tüm sayfalarda biz view state'i Session'a kaydetmek istiyorsak Page sınıfını extend eden bir sınıf oluşturmalıyız. Bu sınıfta LoadPageStateFromPersistenceMedium ve SavePageStateToPersistenceMedium metodlarını yaptığımız şekilde overwrite etmeliyiz.Daha sonra oluşturduğumuz sayfaların base sınıfı olarak yaptığımız sınıfı göstermeliyiz. (Daha fazla ayrıntılı bilgi için Nesne Tabanlı Programlama hakkındaki makalelere bakabilirsiniz).

View State Güvenliği:

Sayfada bulunan view state her ne kadar okunmaz bir formatta isede view state kullanıcılar tarafından biraz kod yazma ile veya internetten hazır yazılmış kod bularak parse edilebilinir.(içindeki veriler okunabilinir.) Aynı zamanda okunan view state'teki değerler değiştirilip tekrar LosFormatter ile deserialize edilebilinir. Örnek olarak GridView listeleme sunucu kontrolündeki veriler'in sayfa her açılışında veritabanına başlanmamak için ASP.NET tarafından otomatik olarak view state'te tutulduğundan bahsetmiştik. Eğer grid view üzerinde ürünler ve fiyatları yazıyorsa kullanıcı view state'teki fiyatları modifiye edip sayfayı postback yaptığında karşısına gelen sayfada fiyatları kendi değiştirdiği şekilde görecektir. Hele bir de sepete atma fonksiyonu fiyatları grid view'den okuyorsa kullanıcı istediği fiyattan ürünleri alabilecektir.
Neyseki ASP.NET'te view state'lere Machine Authentication Check(MAC) denilen bir mekanizma ile view state'in kullanıcı tarafından değiştirilmesini engellemektedir. MAC ile sunucu kullanıcıya gönderdiği view state ile kullanıcıdan gelen view state ile aynı olup olmadığına bakıyor. (Bunu view state'in hash değerini view state'in sonuna ekleyerek yapıyor). Eğer view state kullanıcı tarafından değiştirilmiş ise sayfa hata döndürüyor. Varsayılan olarak MAC tüm sayfalarda devrededir. Eğer bunu gereksiz bulunuyorsa (çünkü bu işlemde biraz kaynak tüketir) ve sayfalarınızda önemli bir veri bulunmuyorsa sayfada EnableViewStateMac özelliğini false yaparak bu işlemi devre dışı bırakabiliriz.
MAC dışıda eğer view state'in kullanıcılar tarafından parse edilmesi istenmiyorsa view state encrypt (şifreleme) edilebilinir. Bu işlem web.config dosyasında bulunan tagının içerisine validation = "3DES" yazarak yapylabilinir. Ayrıca bu tag içerisinde şifrelemelerde kullanılan anahtarlarda belirtilebilinir. validationKey özelliği MAC için kullanılacak anahtarı, decryptionKey 3DES için kullanılacak anahtarı belirler. Eğer bu anahtarları belirtilmemisse ASP.NET otomatik olarak rastgele anahtarlar üretir. Eğer birden fazla web sunucusunda aynı uygulama çalışıyorsa bu değerleri sabitlemekte fayda vardır.

Sonuç

Bu makalemizde ASP.NET geliştiricilerin en çok takıldığı konulardan birisi olan view state konusunu işledik. View state sayfada bulunan sunucu kontrollerinin ve sayfanın kendisinin dinamik değişkenlerini tuttuşu bir veri depolama alanıdır ve aynı sayfa nın PostBack çağırılmasında bu dinamik verilerin sayfada gösterilmesini sağlar.
Ayrıca bu makalede view state değerlerini sayfada bulunan gizli form alanı yerine Session veya dosyaya nasıl yazılacağından bahsedildi. Böylelikle sayfa boyutları önemli ölçüde azaltılabilinmektedir. Son olarak makalede view state ile akılda oluşabilinecek güvenlik ile ilgili bilgiler verildi.

Her Yönüyle ViewState Kavramı


Her Yönüyle ViewState Kavramı


Güzide Windows uygulamalarından, karanlık ASP.Net sayfalarına geçildiğinde yadırganılan ilk konulardan biri heralde veri aktarımı ve bu verilerin saklanmasıdır. Sonuç olarak sayfa her post back olduğunda veriler kaybolmakta ve buda programcılara bunalımlardan bunalım beğendirmektedir. İşte bu ihtiyaç, Durum Yönetimi (State Management) olgusunun ortaya çıkmasının sebebi olmuştur. Sayfa veya tüm uygulama bazında verileri saklamak için ASP.NET çatısı altında bize sunulan teknolojiler işte şunlardır:

Durum Yönetimine Genel Bakış
-View State
-Control State
-Hidden Form Fields
-Cookies
-Query String
-Application State
-Session State
Biz makalemizin konusuna geri dönerek View State kavramının anlamına bir bakalım.

ViewState soyut olarak nedir?

ViewState (Görüntü Durumu), verileri saklamak için default olarak kullanılan tekniktir. Sayfa post back olduğunda ve geri geldiğinde aynı sayfadaki değerlerin saklanması işlemini ViewState yapar. Kısacası sayfanın tarayıcıdan dönerkenki haline ulaşabilmemizi ve sayfa geri gönderilirken otomatikman tekrar bu bilgilerle gitmesini sağlar. Dolayısıyla sayfalar arası veri taşımaz. ViewState 2 türlü kullanılır. İlki ASP.NET tarafından otomatik olarak yönetilmesidir. Kapatılmadığı sürece arkada çalışmaya devam eder. İkincisi ise kod kısmında koleksiyon olarak kullanılmasıdır. Peki ViewState nasıl çalışır? ASP.NET arka tarafta gizli bir input kontrolü yaratır. Bu input kontrolünün içinde, sayfadaki istenilen kontrollerin bazı propertylerini base64 koduna çevirerek saklar. Bu gizlenmiş ViewState kontrolü sayfayla birlikte yaşar ve seyahat eder. Dolayısıyla eğer dikkatli ve kontrollü olarak kullanılmassa, View State kontrolümüz gittikçe şişer ve ciddi performans sıkıntılarına sebep olur. Üstelik aspx sayfamızın içinde bulunan bir kontrol olduğu için hem post back hem de request süresini uzatır.

Bu kadar teorik bilginin ardından View State kavramına biraz da pratik olarak bakalım

ViewState somut olarak nedir?

Basit bir web uygulaması açalım. İçersinde hiç bir değişiklik yapmadan debug edip, kaynak kodunu açtığımızda şu html çıktısı ile karşılaşırız;

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >

<head><title>
Untitled Page
</title></head>
<body>
<form name="form1" method="post" action="Default.aspx" id="form1">

<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJNzgzNDMwNTMzZGRKSpuLejDHB6P+RKbZyCauWTRAqA==" />
</div>

<div>
</div>

</form>
</body>

</html>
Burada görüldüğü üzere ASP.NET bizim için kendiliğinden bu gizli kontrolü oluşturmaktadır. Value özelliğine baktığımızda ise sayfadaki bazı bilgilerin kodlanmış hali görülmektedir.

Şimdide kısa bir örnekle ViewState nerelerde kullanılabilir ona bakalım. Öncelikle yeni web projesi açıp içine bir adet DropDownList ve bir adette Button nesnesi ekleyelim. Senaryomuz gereği, Button nesnemize basıldığında DropDownList içindeki nesneler ters sırayla yazılmalıdır (yada kısaca Reverse() metodu çalıştırılmalıdır);

 
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        DropDownList1.Items.Add("CSharp");
        DropDownList1.Items.Add("Nedir");
        DropDownList1.Items.Add(".com");
    }

}

protected void btnSirala_Click(object sender, EventArgs e)
{
    ArrayList ary = new ArrayList();
    ary.AddRange(DropDownList1.Items);
    ary.Reverse();

    DropDownList1.Items.Clear();

    foreach (object obj in ary)
    {
        DropDownList1.Items.Add(obj.ToString());
    }

}
Yukarıdaki kod çalıştırıldığında hiç bir sorun çıkmaycaktır. Çünkü ViewState bizin için herşeyi yapmıştır. Sayfa post back olduğunda !Page.IsPostBack olmasına rağmen DropDownList1 boş gelmemiştir. Bir de ViewState'i kapatarak bu programı çalıştıralım;

<asp:DropDownList ID="DropDownList1" runat="server" EnableViewState="false" >
ViewState kapatıldığında ise düğmeye basıldıktan sonra dropDownList1 boş gelecektir. Sebebi çok aşikardır. Sayfa post back olunca tüm html kontrolleri sunucuda yeniden hazırlanıp gönderilmiştir. !Page.IsPostBack deyimi de olduğu için DropDownList'imizin items koleksiyonu boş kalmıştır. Peki !Page.IsPostBack'in kaldırılması çözüm olacak mıdır? Hayır, bu seferde dropDownList1'e her seferinde aynı liste baştan eklenecek dolayısıyla Reverse metodu istediği kadar çalıştırılsın devamlı ".com, Nedir, Csharp" şeklinde sıralama olacaktır. Senaryomuzu farklı bir şekilde kurgulayalım. Datalarımızın çok fazla olduğunu ve bir veritabanından çekilmesi gerektiğini düşünelim. ViewState olmadığı takdirde her Page_Load olayında veri çekme işleminin yapılması gerekir. Bu da Request süresini hep uzatmaktadır. Tabi eğer çekilecek verilerin boyutları büyük ise bu seferde ViewState çok şişecek ve bize faydadan çok zararı olacaktır. Bu gibi durmlarda da ViewState kapatılmalıdır.

Şimdide kod tarafında ViewState ile nasıl oynayabiliriz ona bakalım. Buradaki senaryomuzda Bir ListBox kontrolü içine istediğimiz kelimeleri ekleyip çıkartalım:

Kod kısmımız ise şöyledir;

protected void Page_Load(object sender, EventArgs e)
{
}


protected void btnEkle_Click(object sender, EventArgs e)
{
    ViewState.Add(txtEkle.Text.ToString(), txtEkle.Text); //ViewState kontrolümüze dışarıdan bir nesne eklenmeye çalışıldığı zaman aynı Hashtable gibi çalışır. Bizden bir 'Key' birde 'Value' değeri ister. Hashtable'dan farklı olarak 'Key' değerini string olarak alır.'Value' değerini ise object olarak aldıktan sonra StateItem tipine cast eder. Artık eklendikten sonra bu verilere, istenildiği kadar post back yapılsın, ne zaman arzu edilirse 'Key' değeri üzerinden ulaşılabilir.

    Tazele();
}

protected void btnCikar_Click(object sender, EventArgs e)
{
    ViewState.Remove(txtCikar.Text.ToString());  //Remove metodu ise sadece key değeri üzerinden işlem yapmaktadır.

    Tazele();
}

private void Tazele()
{
    //ListBox1.Items.Clear(); Normalde bu metodu yazmadığımız takdirde ListBox nesnemiz içeriğini ViewState vasıtasıyla taşımaya devam eder. Bu da verilerin birikmesine sebep olmaktadır. Dolayısıyla html tarafında ListBox'ın ViewState özelliğini kapatarak hem bu satırı yazmaktan kurtuluruz, hem de ViewState'i daha az yorarız. Burdan da şu anlaşılmaktadır; Biz kod tarafında herhangi bir kontrolün Items.Clear() metodunu çalıştırdığımız zaman ViewState'ten o kontrole ait nesneler kaldırılmaktadır.

    foreach (StateItem sti in ViewState.Values)
    {
        ListBox1.Items.Add(sti.Value.ToString());
    }
}

ASP.NET bizim girdiğimiz StateItem nesneleri ile kendi yarattığı StateItem nesnelerini ViewState içerisinde ayrı ayrı saklamaktadır. Dolayısıyla kod tarafında ViewState'ten sadece kendimizin 'Key' değeri girerek oluşturduğu verilere ulaşabilirz.
Peki bu nesne arka tarafta nasıl çalışmaktadır, bir de ona bakalım. Öncelikle C# tarafında ViewState yazıp 'Go To Definiton' dersek karşımıza şu sonuç çıkmaktadır:

public class Control : IComponent, IDisposable, IParserAccessor,...
{
     ...
     protected virtual StateBag ViewState { get; }
     ...
}
Görüldüğü üzere ViewState aslında, Control sınıfı altında, StateBag isimli bir sınıfın tipinden, get bloğu olan bir özelliktir. Bir de StateBag üzerinden 'Go To Definiton' yaparsak;

public sealed class StateBag : IStateManager, IDictionary, ICollection, IEnumerable
{
     public StateBag();
     public StateBag(bool ignoreCase);
     public int Count { get; }
     public ICollection Keys { get; }
     public ICollection Values { get; }
     public object this[string key] { get; set; }
     public StateItem Add(string key, object value);
     public void Clear();
     public IDictionaryEnumerator GetEnumerator();
     public bool IsItemDirty(string key);
     public void Remove(string key);
     public void SetDirty(bool dirty);
     public void SetItemDirty(string key, bool dirty);
}
StateBag sınıfının türediği arayüzlerden ilki State Management için kullanılan temel arayüzdür. Son üç arayüz ise bakın hangi sınıflarda da vardır;

public class Hashtable : IDictionary, ICollection, IEnumerable ...
public class SortedList : IDictionary, ICollection, IEnumerable ...
Burada ViewState'in Hashtable yapısına benzerliğinin sebebi anlaşılmaktadır. Hatta kullanımları bile çok benzerdir.

ViewState ve Hashtable'ın kullanımlarını karşılaştırırsak:

ViewState Kullanımı Hashtable Kullanımı
for (int i = 0; i < 10; i++)
{
    ViewState[i.ToString()] = i;
}
Hashtable ht = new Hashtable();
for (int i = 0; i < 10; i++)
{
    ht[i] = i;
}
Üstteki iki kontrol arasında dört adet fark vardır:

1- ViewState 'Key' değer olarak string alırken, Hashtable object almaktadır.
2- ViewState 'Value' değerini StateItem, Hashtable ise object tipine çevirir.
3- Hashtable 'Key' değerlerini unique olarak istemektedir ve aynı olursa hata vermektedir. ViewState ise aynı 'Key' değeri olduğu durumlarda en son ekleneni alır.
4- Hashtable bir sınıf olduğundan ilk başta örneklenmesi gerekir. Fakat View State virtual olmasından dolayı bizim tarafımızdan örneklenemez. Eğer böyle bir yapıya sahip olsaydı sayfa her post back olduğunda içindeki değerler sıfırlanırdı. Sonuç olarak View State, kapalı veya açık olsun, her sayfada mutlaka debug sırasında bir adet örneklenir ve html kodları arasına yerleştirilir. Daima bir tane olmasının sebebi ise bazı kontrollerin ViewState'e ihtiyaç duymasıdır.

ViewState'in yüklenmesi ve kaydedilmesi

ASP.NET, sayfadan gelen ViewState'in içeriğindeki veriyi almak için LoadViewState, ViewState'e yeniden düzenlenmiş veriyi kaydetmek içinse SaveViewState metodunu kullanmaktadır. Bu iki metod framework tarafından otomatikman kullanıldığı gibi bizim tarafımızdan da istediğimiz yerde yazılabilir. ASP.NET bu metodları yaşam döngüsünde şu aralarda çalıştırmaktadır:

-Initialize
-TrackViewState() :'Bu metoda az aşağıda değinilmektedir.'
-LoadViewState()
-Process postback data
-Load
-Send postback change notifications
-Handle postback events
-Prerender
-SaveViewState()
-Render
-Dispose
-Unload
ViewState hakkında yanlış bir izlenim olabilir. ViewState kontrollerin sadece text veya koleksiyonlarını değil hemen hemen tüm özelliklerin saklayabilir. Örnek vermek gerekirse, aspx sayfamıza bir Label ve birden fazla Button kontrolü ekleyelim. Her bir düğmede Label'ın bir özelliğini değiştirelim.

protected void Page_Load(object sender, EventArgs e)
{
}

protected void Button1_Click(object sender, EventArgs e)
{
    Label1.Text = "Çarşı .Net'e karşı";
}

protected void Button2_Click(object sender, EventArgs e)
{
    Label1.BackColor = Color.Red;
}

protected void Button3_Click(object sender, EventArgs e)
{
    Label1.CssClass = "style1";
}
Her düğmeye basıldığında Label'ın değişen özelliği ViewState'e eklencektir.

Başlangıçta sayfamız bu haldedir:

Button1’e basıldığında:

Button2’ye basıldığında:

Button3’e basıldığında:
Küçük bir tüyo vermek de gerekirse ViewState'inizin boyutuna şu javascript kodunu yazarak bakabilirsiniz:

<script type="text/javascript">
    var size = document.getElementById("__VIEWSTATE").value.length;
    document.write(size+"bytes");
</script>
ViewState’i takip etmek: ViewState sakladığı verilerin her birine ekstra olark bir de 'dirty' özelliği ekler. Bu dirty özelliği o StateItem'ın değişip değişmediğini belirtir. SaveViewState metodu sırasında eğer dirty özelliği false ise ViewState'e kaydedilmez, eğer true ise kaydedilir. Ayrıca ViewState bu dirty özelliğini takip etmeden önce TrackViewState metodunu çalıştırır. TrackViewState metodu Page_Init zamanında çalışır ve SaveViewState metodu çalışana kadar çalışmaya devam eder. ViewState'e yeni bir item kod tarafından eklensede SaveViewState metodu çalışmadan ViewState'e kaydedilmez. Şöyle bir örnekle olayı aydınlatalım:

Sayfamıza bir DropDownList ve iki adet Button nesnesi ekleyelim. Button nesnelerinin görevi ViewState'e ekliyeceğimiz nesnenin dirty özelliğine true veya false atamaktır.

 
protected void Page_Load(object sender, EventArgs e)
{
    if (!Page.IsPostBack)
    {
        ViewState["key"] = "value";
    }
}

protected void Button1_Click(object sender, EventArgs e)
{
    ViewState.SetItemDirty("key", true);

    DropDownList1.Items.Clear();
    foreach (StateItem sti in ViewState.Values)
    {
        DropDownList1.Items.Add(sti.Value.ToString());
    }

}

protected void Button2_Click(object sender, EventArgs e)
{
    ViewState.SetItemDirty("key", false);

    DropDownList1.Items.Clear();
    foreach (StateItem sti in ViewState.Values)
    {
        DropDownList1.Items.Add(sti.Value.ToString());
    }

}
Bu kod çalıştırıldığında şöyle bir sonuç olmaktadır. DropDownList'e 'value' muhakkak bir kez eklenmektedir. Eğer TRUE düğmesine basarsak eklenmeye devam eder. Fakat FALSE düğmesine basıldığı anda son bir kez daha eklenir sonra silinir. Daha sonra TRUE'ya basılsa bile artık bu StateItem ViewState'ten kaldırıldığı için bir daha ulaşılamaz.

Buradan şu sonuç çıkmaktadır. Biz ViewState["key"]=value; kodunu yazdığımız anda ViewState’e bu veri eklenmek için SaveViewState metodunu bekler ve ’dirty’ özelliği ne olursa olsun eklenir. Fakat eğer bu veri ViewState’e sadece bir kez ekleniyorsa, (Yukarıdaki !Page.IsPostBack metodu içerisinde olduğu anlar gibi) ’dirty’ özelliği, true olana kadar taşınmaya devam eder ama false ise son bir kez taşınıp daha sonrasında silinir. ViewState.Remove() metodu ise hiç ViewState’e eklemeden siler.

-ChildControls ve TrackViewState: Eğer sayfaya bir kontrolü dinamik olarak ekliyorsak şu duruma dikkat etmemiz gerekir. Kontroller dinamik olarak eklendiği zaman TrackViewState metodu, kontrolü sadece formun Add() metodu çalıştırıldıktan sonra etkilemeye başlar. Dolayısıyla Add metodu, dolaylı olarak da TrackViewState metodu çalıştırılmadan önce yazılan veriler kontrole eklenir fakat ViewState'e eklenmez:

protected void Page_Load(object sender, EventArgs e)
{
    DropDownList drpList = new DropDownList();

    if (!Page.IsPostBack)
    {
        for (int i = 0; i < 10; i++)
        {
            drpList.Items.Add(i.ToString());
        }
    }

    this.form1.Controls.Add(drpList);//Bu durumda TrackViewState metodu burada çalıştırılır. Dolayısıyla veriler ViewState'e eklenmez sadece kontrole bir kereye mahsus olmak üzere eklenir. Postback olduğunda DropDown boş gelir.
}

//Bunun yerine Kontrolü önce forma ekleyip sonra verileri bağlamak daha sağlıklı olur.

protected void Page_Load(object sender, EventArgs e)
{
    DropDownList drpList = new DropDownList();
    this.form1.Controls.Add(drpList);//Artık TrackViewState metodu buradan itibaren çalışmaya başlıyacaktır ve veriler hem DropDown'a hemde ViewState'e eklenecektir.

    if (!Page.IsPostBack)
    {
        for (int i = 0; i < 10; i++)
        {
            drpList.Items.Add(i.ToString());
        }
    }
}
ViewState'te güvenlik

ViewState bilindiği üzere değerleri base64 kodlarına çevirmektedir. Şunu söylemek gerekir ki base64 kodları şifreli değildir ve kırılması çok basittir. ViewState'in diğer kötü yanlarından biri de bir html nesnesi olmasından dolayı herkes tarafından görülebilmesidir. Bu iki etken bir araya gelince veri güvenliği diye bir kavram kalmamaktadır. İşte değerli verilerimizi ViewState ile taşıdığımız anlarda yardımımıza ViewStateMac koşmaktadır.

ViewStateMac: EnableViewState özelliği ile EnableViewStateMac özelliği karıştırılmamalıdır. ViewStateMac(ViewState Machine Authentication Check) güvenlik amaçlı konulmuş bir özelliktir. ViewState üzerindeki orjinal datanın bozulmasına yönelik herhangi bir saldırı olup olmadığını kontrol eder ve verileri base64 yerine hash koduna çevirir. Default olarak açıktır ve ViewState özelliğinin olduğu heryerde bu özellikde vardır. ViewStateMac, sayfa tarayıcıya giderken verileri hash kodundan geçirir ve post back olduğunda gelen verilerle daha önceden oluşturduğu hash kodunu karşılaştırır. Bunun için küçük bir test yapalım. Boş bir web sayfası açıp <%@ Page %> kısmına EnableViewStateMac="false" özelliğini ekleyelim. Ardından debug edip sayfanın kaynak koduna bakalım. ViewState'in value değeri şu 24 haneli string çıkmıştır: "/wEPDwUJNzgzNDMwNTMzZGQ=" . Şimdide ViewStateMac'i aktif hale getirelim. Bu sefer value değerimiz 52 hanelidir: "/wEPDwUJNzgzNDMwNTMzZGTOg2Hi9CvgCScuhB5ASVZPIeJCsw==" . Bunun sebebi ViewStateMac açıldığından değerlerin hash koduna çevrilmesidir. Bu işlem verilerin değiştirilmesini çok zorlaştırır fakat diğer yandan da sayfanın boyutunu da arttırmaktadır.

ASP.NET’in kullandığı hash kodu standartlarına bakmadan önce web.config dosyasını kısaca bir hatırlayalım:

machine.config aynı web.config dosyası gibi xml tabanlı bir dosya olup o sunucuda bulunan tüm siteleri etkiler. ASP.NET'te özellikler aşağıdaki hiyerarşiye göre belirlenir:

1- Makine bazında: machine.config
2- Site bazında: web.config
3- Sayfa bazında: <%@ Page %>
4- Kontrol bazında.

Fakat üstünlük bakımından ise bu sıra tam tersidir. ASP.NET, özellikleri önce <%@ Page %> takısından kontrol etmeye başlar. Yani <%@ Page %> web.config'den, web.config'de machine.config'den daha önceliklilidir.

*machine.config dosyasına ulaşmak için ise şu path izlenir: WINDOWS\Microsoft.NET\Framework\Framework sürümü\CONFIG\machine.config
Hash Kodları: ASP.NET default olarak SHA1'ı kullanmaktadır. Eğer bu standart değiştirilmek istenirse web.config dosyasına başvurulması gerekir:

<configuration>
    <system.web>
        <machineKey validation="SHA1" />
Bu validation özelliği altındaki diğer hash algoritmaları ve anahtar büyüklükleri şunlardır:

-AES (Advanced Encryption Standart): 128, 192 veya 256 bit
-3DES (Triple Data Encryption Standart): 56x3 bit
-SHA1 (Secured Hashing Algrotihm 1): 160 bit
-MD5 (Message-Digest algorithm 5): 128 bit

Anahtar büyüklüğündeki Bit boyutunun artmasıyla doğru orantılı olarak kodun kırılma zorluğu da artar.

SHA1 ve MD5: Bu iki algoritma şifreleme yapmazlar. Şifreli olmamaları yanlış anlaşılmamalıdır. Bu tamamen açık oldukları anlamına gelmez, sadece ileri düzey hash kodlarından geçirilmediğini gösterir. Fakat bugün brute force saldırılarıyla kırılabilmektedirler. SHA1, MD5'a göre daha fazla yer tutar ve daha yavaş çalışır fakat daha güvenlidir.

- Tamper Proofing (Kırılmaya dayanıklı): ViewState'imizi eğer orta derecede güvenlik altına almak istiyorsak öncelikle EnableViewStateMac özelliğine true atamamız gerekir. Daha sonrasında ise machine.config dosyasına şu özellikler yazılmalıdır:

<configuration>
    <system.web>
        <machineKey
                validationKey="AutoGenerate,IsolateApps"
                validation="SHA1"  />
'veya MD5'
ViewStateMac, ValidationKey değerini taşınan veriyi doğrulamak için kullanır. Buradaki AutoGenerate framework'ün bizim için rastgele bir anahtar değeri belirlemesini sağlar. Eğer sitemiz bir web farm üzerindeyse kendimizin bir değer vermesi gerekir ve bu özellik hexadecimal sayıları kabul etmektedir. O zaman ise validationKey şöyle bir görüntü almalıdır: validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D7AD972A119482D15A4127461DB1DC347C1A63AE5F1CCFAACFF1B72A7F0A281B"
IsolateApps ise sunucuda bulunan her bir site için farklı bir id belirleyerek bu anahtar değerlerini birbirinden soyutlar. validationKey ile aynı şekilde çalışan bir de decryptionKey vardır. Bu özellik ise şifreleme yapılırken kontrol etmek için bir anahtar değer üretir.

AES ve 3DES: Bu algoritmalar ise verileri şifrelerler. Kırılmaları çok zordur ve maliyetlidir. AES, 3DES'e göre çok yeni bir standarttır. Dolayısıyla 2.0 ile birlikte eklenmiştir ve 3DES'e göre çok daha güvenlidir. 3DES'in yaptığı iş verileri 56 bitlik anahtar büyüklüğündeki hash kodundan 3 kere geçirmesidir. AES ise 3 ayrı anahtar değerine sahiptir.

- Encryption (Şifreleme): ViewState'in şifrelenmesi için bir kaç ayar vardır. İlk başta ViewStateMac açılmalıdır. Daha sonrasında web.config'e veya <%@ PAGE &>'e şu özellik eklenir:

...
<system.web>
    <pages viewStateEncryptionMode="Always"></pages>
...
Eğer her sayfanın şifrelenmesini istemiyorsanız ve bu işi framework’e emanet etmek istiyorsanız şifreleme modunu ’Auto’ yapabilirsiniz. Bu işlemin ardından kod kısmına şu satır eklenmelidir:

Page.RegisterRequiresViewStateEncryption();
Son olarakta machine.config kısmında şu özellikleri belirleriz:

<configuration>
    <system.web>
        <machineKey
                validationKey="AutoGenerate,IsolateApps"
                decryptionKey="AutoGenerate,IsolateApps"
                validation="AES"
'veya 3DES'
                decryption="Auto
'Bu özellik şifrelenmiş veriyi hangi standartla çözüceğimizi belirtmemizi sağlar. Özel bir durum olmadıkça Auto olarak ayarlanmalıdır. '/>
Bu işlemlerin ardından artık ViewState’imiz daha korunaklı bir hale getirilmiştir.

dipnot: Eğer siteniz bir web farm vasıtasıyla çalışmıyor ise bu özellikleri kullanınız.

View State'ten faydalanmamak

Her ne kadar bir çok yerden kolaylık sağlasada, bazı durumlarda, özellikle boyutunun çok büyüdüğü anlarda ViewState'in kapatılması gerekebilir. ViewState şu yöntemler kullanılarak kapatılabilir.

1- Kontrol bazında;
<asp:TextBox ID="TextBox1" runat="server" EnableViewState="false"></asp:TextBox>

2- Sayfa bazında;
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" EnableViewState="false" %>

3- Tüm sitede ViewState özelliğinin kapatılması için ise web.config dosyasının içine şu kod yazılır;
...
<system.web>
    <pages enableViewState="false"></pages>
...
Her ne kadar ViewState web.config'den bile kapatılsa, gizli input kontrolümüz hala sayfada yerini korumaktadır. Bu kontrolü kaldırmanın bir yolu yoktur. Fakat şu iki metod override edilerek ViewState etkisiz hale getirilebilir.

protected override void SavePageStateToPersistenceMedium(object viewState)
{
}

protected override object LoadPageStateFromPersistenceMedium()
{
    return null;
}
ViewState'in bölünmesi (ViewStateChunking)

ViewState'in çok büyüdüğü anlarda karşılaşılan sorunlardan biri de bazı firewall ve proxy'lerin bu olgunlaşmış ViewState'leri kabul etmemesidir. Bu zorluğun atlatılması kolaydır. Tek yapmamız gereken web.config dosyası içine şu satırı yazmaktır:

<configuration>
     <system.web>
          <pages maxPageStateFieldLength=""></pages>
maxPageStateFieldLength özelliği, ViewState'in uzunluğuna bir sınır koymamızı sağlar. Eğer atanacak değerin uzunluğu buradaki değerden fazlaysa, ViewState bölünmeye başlar. Bu özelliğin default değeri '-1' olup, bölünme olmayacağı anlamına gelir. Test etmek için maxPageStateFieldLength özelliğine 100 değerini verip, bir DropDownList'e 1'dan 10'a kadar olan sayıları ekleyelim. Kaynak kodumuzun görüntüsü şöyle olacaktır:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >
<head><title>
Untitled Page
</title></head>
<body>
<form name="form1" method="post" action="Default.aspx" id="form1">
     <div>
          <input type="hidden" name="__VIEWSTATEFIELDCOUNT"
id="__VIEWSTATEFIELDCOUNT" value="3" />

          <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKMTYxMTc1NjI2MA9kFgICAw9kFgICAQ8QZA8WCm
YCAQICAgMCBAIFAgYCBwIIAgkWChAFATEFATFnEAUBMgUBMmcQBQEz" />
          <input type="hidden" name="__VIEWSTATE1" id="__VIEWSTATE1" value="BQEzZxAFATQFATRnEAUBNQUBNWcQBQE2BQE2ZxAFATcFATdnEA
UBOAUBOGcQBQE5BQE5ZxAFAjEwBQIxMGdkZGT//mlq/vcPuLOi" />
          <input type="hidden" name="__VIEWSTATE2" id="__VIEWSTATE2" value="A2O9kXAuaxuGhw==" />

     </div>

     <div>
     <select name="DropDownList1" id="DropDownList1">
         <option value="1">1</option>
         <option value="2">2</option>
         <option value="3">3</option>
         <option value="4">4</option>
         <option value="5">5</option>
         <option value="6">6</option>
         <option value="7">7</option>
         <option value="8">8</option>
         <option value="9">9</option>
         <option value="10">10</option>
     </select>
     </div>
...
Toplam ViewState'e atanacak boyut 216 byte olmuş ve ASP.NET bunu 100 byte'lık 3 parçaya bölmüştür. Ayrıca ViewState'lerin sayısını tutmak için ise __VİEWSTATEFIELDCOUNT isimli farklı bir gizli input nesnesi de sayfayla birlikte gönderilmiştir.

Bu makale umarım size çok faydalı olmuştur ve ViewState ile ilgili soru işaretlerini minimuma indirmiştir. Herkese bol ViewState’li siteler.

Serkan YAZICIOĞLU
serkanyazicioglu@gmail.com

Kaynaklar: http://msdn2.microsoft.com/en-us/library/ms998288.aspx
http://weblogs.asp.net/ngur/archive/2004/03/08/85876.aspx
http://weblogs.asp.net/infinitiesloop/archive/2006/08/03/Truly-Understanding-Viewstate.aspx
 

Makale:
Her Yönüyle ViewState Kavramı ASP.NET Serkan Yazıcıoğlu