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.