Makale Özeti

Bu makalemizde ASP.NET uygulamalarında bir sayfa içerisinde veri taşıma işlemlerinde kullanabileceğimiz nesnelerden biri olan View State'i derinlemesine inceliyoruz.

Makale

Durum yönetiminde (state management) bir sayfa içerisinde veri taşıma işleminde kullanılan nesnelerden biri de View State'dir. View State, bir ASP.NET sayfası içerisinde bulunan kontrollerin özelliklerinin ve programcının istediği verilerin saklanmasını sağlar. Varsayılan olarak biz belirtmesek bile, sayfada Postback işlemi gerçekleştiğinde kontrollere ait bilgiler sunucu tarafında HTML kodları üretilirken şifrelenmiş bir şekilde View State içerisine yazılır. Sayfa tekrar yüklendiğinde ise kontrollerin özellikleri bu nesneden okunur. Böylece Postback işlemi sonucunda kontroller üzerinde yapılan değişiklikler sayfa tekrar yüklendiğinde kaybedilmeden elde edilebilir.

ViewState Olmasaydı?
Çoğumuzun hatırlayacağı gibi, ASP veya PHP ile geliştirilen uygulamalarda bir sayfa içerisindeki kontrollerin değerleri, sayfa post edildiğinde kaybolurdu ve tekrar elde etmek için ekstra işlemler yapmamız gerekirdi. ASP.NET'e geçiş yaptığımız ilk zamanlarda en çok dikkatimizi çeken durumlardan birisi de bir formu post ettiğimizde form üzerindeki verilerin kaybolmamasıydı. İşte ASP.NET uygulamalarında bizi bu sıkıntıdan kurtaran ve post işleminden sonra da verilerin form üzerinde saklanmasını sağlayan View State nesnesidir. Dilerseniz View State nesnesinin uygulamalarda bize ne gibi bir kolaylık sağladığını daha net bir şekilde görebilmek için şu basit örneği gerçekleştirelim. Açtığımız bir sayfaya iki tane Label kontrolü ve iki tane Button kontrolü ekleyelim. Label kontrollerinden bir tanesinin EnableViewState özelliğini true, diğerinin ise bu özelliğini false olarak ayarlayalım. (EnableViewState özelliği bir kontrolün özelliklerinin ViewState içerisinde taşınıp taşınmaması durumunu belirler. Makalemizin ilerleyen kısımlarında EnableViewState özelliğini tekrar inceliyor olacağız.) Butonlardan bir tanesinin Click eventi içerisine aşağıdaki ifadeleri ekleyelim. Diğer butonda ise herhangi bir değişiklik yapmayacağız, sadece postback yapmak amaçlı kullanacağız.

protected void Button1_Click(object sender, EventArgs e)
{
   Label1.Text = "EnableViewState'i kapalı olan label";
   Label2.Text = "EnableViewState'i açık olan label";
}

 
Resim: EnableViewState özelliği açık olan kontrolün ve kapalı olan kontrolün postback işlemleri sonucundaki davranışları

Görüldüğü gibi ilk postback işlemi sonucunda butonun Click eventi aracılığıyla her iki labela da değerler yazıldı. Fakat sayfa üzerinde farklı bir postback işlemi (labelların durumunu değiştirmeyen bir postback işlemi) gerçekleştiğinde EnableViewState özelliği false olan label kontrolü (Label1), bir önceki postbackten kazandığı değerini kaybetti. Diğer yandan, ViewState nesnesi içerisinde değeri saklanan label (Label2) farklı postback işlemlerinden geçmesine rağmen önceki sayfadaki değerini kaybetmedi.

ViewState Hangi Durumlarda, Hangi Kontrollerin Değerlerini Saklar?
View State içerisinde sadece formda bulunan input elementleri değil, GridView, DetailsView ve Calendar diğer ASP.NET sunucu kontrollerinin özellikleri de saklanmaktadır. HTML kodları içerisinde taşınan View State nesnesini gözlemlemek için isterseniz basit bir web uygulaması açalım. Bu noktada View State içerisinde kontrollerin değerlerinin saklanması ile ilgili önemli birkaç hususa değinebilmek için dilerseniz uygulamanın gidişatını aşağıdaki gibi takip edelim.

Adım-1: Sayfamıza btnTikla adında bir button ve lblIcerik adında bir label kontrolü ekleyelim. Button'un Text özelliğini Tıkla, label'ın Text özelliğini Merhaba olarak değiştirelim. Sayfayı çalıştırdıktan sonra tarayıcı üzerinde boş bir alana fare ile sağ tıklayıp Kaynağı Görüntüle (View Source) seçeneğinden oluşan sayfanın HTML kodlarına göz atalım.


Resim: Kontrolleri ekledikten sonra __VIEWSTATE nesnesinin içeriği

Görüldüğü gibi bilgiler HTML kodları içerisinde __VIEWSTATE adı verilen hidden bir input elementi bulunmaktadır. Bir ASP.NET sayfasında varsayılan olarak her zaman __VIEWSTATE değeri saklanmaktadır. Base64 formatı kullanarak şifrelenen veriler sayfa içerisinde saklanır ve sayfanın postback ile tekrar yüklenmesi durumunda gerekli bilgiler çözümlenerek sayfa içerisinde kullanılır. Yukarıdaki resimde seçili olarak tutulan View State değerine dikkat edelim.

Adım-2: Sayfamızdaki button'un Text özelliğini Click, label'ın Text özelliğini de Hello olarak değiştirelim. Sayfayı çalıştırdıktan sonra tekrar HTML kodları içerisindeki __VIEWSTATE değerini kontrol edelim.


Resim: Button ve label içeriğinin değiştirilmesinden sonra __VIEWSTATE'in sakladığı değer

İki resimdeki seçili __VIEWSTATE alanlarına dikkat edecek olursanız, button ve label kontrollerinin içeriğini değiştirmemize rağmen View State içerisinde saklanan değerin aynı şekilde kaldığını görebilirisiniz. Bunun sebebi şu anda View State içerisinde sadece sayfamızdaki kontrollerin hiyerarşik olarak diziliminin tutulması ve bizim Design aşamasında yapmış olduğumuz değişikliklerin tutulmamasıdır . Yani kontrolün özelliklerinde belirlediğimiz atamalar direkt olarak HTML kodları içerisinde <input type="submit" name="Button1" value="Click" ... şeklinde tutulacağı için bu bilgilerin __VIEWSTATE'e yazılmasına gerek yoktur. Zaten View State'in en temel kullanım amaçlarından biri de Design aşamasında belirlenen kontrol özelliklerini saklaması değil, form üzerinde Postback işlemi yapıldıktan sonra değişen kontrol değerlerinin taşınmasını ve sayfa tekrar yüklendiğinde bu verilerin kaybolmamasını sağlamaktır. Dilerseniz son cümlede bahsettiğimiz olayı Adım-3'te görelim.

Adım-3: Aşağıdaki kod örneğinde görüldüğü gibi, Button'un Click olayı (event) gerçekleştiğinde lblIcerik'in Text özelliğini ASP.NET olarak değiştirelim. Sayfayı çalıştırdıktan sonra buttona tıklayarak Click eventini gerçekleştirelim ve Postback ile sayfayı tekrar elde edelim. Yeniden oluşan sayfanın kodlarına bakacak olursak aşağıdaki gibi __VIEWSTATE'in içeriğinin değiştiğini görebiliriz.

protected void Button1_Click(object sender, EventArgs e)
{
    lblIcerik.Text = "ASP.NET";
}


Resim: Button'un Click eventinde label'in değeri değiştiği için bu değişiklik Postback işlemi sonunda __VIEWSTATE'e yansıtılmıştır

Bu sonuç, __VIEWSTATE içerisinde formdaki kontrollerin dizilimi dışında Postback işlemi sonucunda kontrollerin özelliklerinde yapılan değişikliklerin de tutulduğunun bir göstergesidir. Kontrol üzerinde sadece değişen Text özelliği değil, değişen diğer tüm özellikler de (BackColor, Width, Height, ... gibi diğer özellikler) View State içerisinde saklanır. Burada kontroller ile ilgili söyleyebileceğimiz önemli ve ilginç bir hususta şudur ki; TextBox, CheckBox ve RadioButton gibi kontrollerin postback esnasında değişen özellikleri View State içerisinde taşınmaz!

Hangi Durumlarda Verilerin ViewState İçerisinde Taşınması Engellenmeli?
Kontrollerin değerlerinin ne gibi durumlarda View State içerisinde saklanıp saklanmayacağı konusunda genel bir fikre sahip olduğumuzu düşünüyorum. Yukarıda yapılan Adım-3 işleminden sonra eğer dikkatinizi çektiyse label kontrolü üzerinde yaptığımız değişiklikten dolayı __VIEWSTATE içerisindeki verinin boyutu arttı. Peki sayfamızda büyük miktarda veri saklayan bir kontrol (örneğin GridView gibi) kullansak ve bu kontrolün içeriğini Postback işlemi sonucunda değiştirsek, __VIEWSTATE'in boyutu ne şekilde değişir? Gelin sayfamıza bir GridView nesnesi ekleyerek ne gibi değişiklikler olduğunu gözlemleyelim. Bu örneği sayfaya eklediğim GridView'i AdventureWorks veritabanı altında yer alan Production.Product tablosuna bağlayarak gerçekleştirdim.


Resim: GridView'e doldurulan veriler ve HTML kısmında yer alan __VIEWSTATE nesnesinin içeriği

Her ne kadar resmin boyutunu küçültmüş olsamda, __VIEWSTATE'in oldukça fazla yer kapladığı aşina bir şekilde görünmektedir. Yine sayfanın HTML kodlarını ayrı bir dosyaya, __VIEWSTATE içerisindeki metinsel kısmı da farklı bir dosyada kaydedip aralarındaki farkı kıyaslayacak olursak __VIEWSTATE değerinin sayfa içerisinde oldukça fazla yer kapladığını göreceksiniz. Örneğin benim hazırladığım bu örnekte sayfanın tamamı 6.66 KB, sadece __VIEWSTATE içerisindeki değer ise 2.85 KB yer kaplamaktadır. Yani View State'de saklanan veri, boyut olarak sayfanın neredeyse yarısını kaplamaktadır. Burada oluşturulan View State değeri her Postback işlemi gerçekleştiğinde sunucuya gönderileceği ve her sayfa oluşumunda da istemciye getirileceği için sürekli olarak sunucu-istemci arasında fazladan veri transferi söz konusu olacaktır. İşte bu noktada uygulamayı geliştiren kişi olarak bize önemli bir performans ayarlaması görevi düşmektedir. GridView vb. bir kontrolün View State içerisinde saklanması, sayfanın düzgün bir biçimde çalışması için gerekli midir, yoksa gereksiz midir? Bir başka söylemle; sayfa, Postback olduğunda önceki halinde bulunan verileri hatırlamaya ihtiyacı var mıdır, yok mudur? Eğer cevabımız "Hayır, bu sayfanın tekrar oluşturulması esnasında, önceki halindeki GridView vb. bir nesnenin verisinin taşınmasına gerek yok" ise, bu noktada sayfanın sunucu-istemci arasında daha hızlı şekilde çalışabilmesi bu değerlerin View State içerisinde saklanmasını engellememiz gerekmektedir. ASP.NET sunucu kontrollerinin tamamında EnableViewState adında bir özellik bulunmaktadır. Bu özellik kontrolün postback işlemi sonucunda değişen değerlerinin View State içerisinde saklanıp saklanmayacağını belirler. bool tipinden bir değer alabilen EnableViewState özelliğinin varsayılan değeri true'dur. EnableViewState değerinin true olması, kontrolün özelliklerinin View State içerisinde saklanır, false olması durumunda ise saklanmaz. Eğer bir kontrolün özelliklerinin View State içerisinde taşınmasını istemiyorsanız, o kontrolün EnableViewState özelliğini false olarak ayarlamanız gerekecektir. Aşağıdaki örnek kod parçasında bir GridView kontrolünün EnableViewState özelliğinin kapatılmıştır.

<asp:GridView ID="GridView1" runat="server" EnableViewState="False"></asp:GridView>

 
Resim: EnableViewState özelliğinin false olması durumunda değişen değerler ViewState'de taşınmaz ve sayfanın boyutu azalır

Yukarıdaki resimde gördüğünüz gibi __VIEWSTATE nesnesinin içeriği ciddi anlamda azalmıştır. Benim yaptığım testte, sayfanın boyutu 6.66 KB'dan 3.96 KB gibi bir değere düştü.

NOT: ViewState değerinin bazı durumlarda çok ciddi boyutlara ulaşması uygulamanın performansı açısından ciddi sorunlara yol açabilir. ViewState nesnesi içerisinde saklanan değerlerin sıkıştırılarak daha küçük boyutlara ulaşılmasını sağlayan bazı teknikler bulunmaktadır. Fakat bu konu başlı başına bir makale anlamına geleceği için bu yazımızda, bu detaya gimeyeceğiz. Arama motorlarında ViewState compression veya ViewState sıkıştırma şeklinde aramalar yapacak olursanız çok sayıda kaynağa erişebilirsiniz.

Bir kontrolün ViewState özelliğini kapatma işlemini kontrolün EnableViewState özelliği üzerinden gerçekleştirebileceğimiz gibi, bir sayfa içerisindeki tüm kontrollerin ViewState özelliğini de sayfamızın Page direktifi içerisinden veya Page nesnesi üzerinden yine EnableViewState özelliği ile kapatabiliriz. Aşağıdaki örnek kodlarda bu işlemlerin nasıl gerçekleştirileceği gösterilmiştir.

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs"
    Inherits="_Default" EnableViewState="false" %>

protected void Page_Load(object sender, EventArgs e)
{
   Page.EnableViewState = false;
}

Sayfamızın EnableViewState özelliğini kapatmamız durumunda artık kontrollerin postback sonrasında değişen özellikleri ViewState içerisinde tutulmayacaktır. Fakat böyle bir durumda oluşan HTML kodları içerisine bakacak olursanız, __VIEWSTATE alanında hala bazı bilgiler olacağını görebilirsiniz. Her ne kadar kontrollerin değişen özellikleri artık bu nesnede saklanmasa da, sayfaya eklene kontrollerin hiyerarşik yapısı burada tutulmaya devam edilecektir.

ViewState Nesnesi İçerisinde Kendi Verilerimizi Saklamak
View State ile sayfa içerisindeki kontrollerin özellikleri dışında kendi istediğimiz verileri de taşıyabilmekteyiz. View State ile aynı adı taşıyan ViewState isimli nesne aracılığıyla istenilen verileri saklayabilir ve aynı sayfa üzerinde gerçekleşen Postback işlemlerinden sonra bu verileri okuyabiliriz. ViewState nesnesinin yapısını inceleyecek olursak geriye StateBag adında bir nesne örneği döndürdüğünü ve içerisindeki nesneleri IDictionary tipinde bir kolleksiyonda sakladığını görebiliriz. Indeksleyiciler aracılığıyla içerisinde key-value çiftleri taşıyabilmektedir. Burada key değerimiz string, value değerimiz ise object tipinden olmalıdır. Yine saklanacak olan object tipindeki değer serileştirilebilir (serializable) olmak zorundadır. Yani .NET ortamında kendi yazdığımız serileştirilebilir tipleri de ViewState içerisinde saklayabiliriz. Aşağıdaki kod örneğinde ViewState nesnesine int ve DataTable tipinden değerlerin atanması gösterilmiştir.

ViewState["miktar"] = 128535;
ViewState["tablo"] = new DataTable("TestTable");

İndeksleyiciler aracılığıyla ViewState'e ekleme yapılabileceği gibi, Add metodunu kullanarakta ViewState.Add("KeyAdı", value) şeklinde bir nesne eklenebilir. Bu şekilde ViewState'e atılan değerler Postback işlemi sonunda sayfanın HTML kodları içerisinde saklanacaktır. Postback işlemleri sonucunda hala aynı sayfa üzerindeysek bu verilere aşağıdaki şekilde erişebiliriz.

int sayi = (int)ViewState["miktar"];
DataTable dtTest = (DataTable)ViewState["tablo"];

Görüldüğü gibi, ViewState içerisinde sakladığımız değerler object tipinde saklanacağı için sayfa içerisinde bu verilere erişirken gerekli dönüşüm (cast) işlemlerini de gerçekleştirmek gerekecektir. ViewState içerisinden saklanan bir değeri çıkarmak için Remove metodu, programatik yollarla eklenen tüm verileri çıkarmak ise Clear metodu kullanılabilir. Aşağıda bu iki metodun kullanımı gösterilmiştir.

ViewState.Remove("tablo");  // tablo isimli nesne ViewState'den çıkarılır
ViewState.Clear();  // ViewState'e eklenen tüm kayıtlar silinir. (Kontrol bilgileri silinmez!)

ViewState Güvenliği
ViewState istemci tarafında HTML kodları içerisinde Base64 encoding formatında şifrelenerek saklandığından bahsetmiştik. Bildiğiniz gibi istemcinin bilgisayarında veri saklamamızı sağlayan durum yönetim nesneleri arasında, ViewState dışında QueryString ve Cookie nesneleri de bulunmaktadır. QueryString ve Cookie içerisinde taşınan verilere kullanıcı kolay bir şekilde erişebilmekte ve değişiklikler yapabilmektedir. ViewState nesnesinde taşınan veriler ise şifrelenmiş bir şekilde taşınacağı için okunabilir olmayacaktır ve diğer nesnelere göre daha güvenli bir veri taşıma yöntemi sunacaktır diyebiliriz. Fakat burada unutmamamız gereken bir nokta var ki; verilerin saklandığı Base64 encoding formatı acemi bir programcı tarafından dahi çok kolay bir şekilde çözümlenebilir. Base64 encoding formatında saklanan verilerin geriye dönüştürülmesi kolay bir şekilde gerçekleştirilebileceği için, ViewState nesnesinin çözümlenmesi ve içerisinde saklanan değerlerin görüntülenmesi de çok kolay olacaktır. (Internette ViewState Decoder şeklinde bir arama yapacak olursanız ViewState değerlerini çözümleyen çok sayıda araç ile karşılaşabilirsiniz.) Eğer ViewState içerisinde gizli bir bir güvenlik açığına sebebiyet verebilir. Bundan dolayı ViewState'in şifrelenmiş bir şekilde veri saklamasına güvenerek, bir kullanıcı veya site için güvenlik sorununa yol açabilecek verileri taşımamalıyız. Peki ViewState'de şifrelenerek taşınan bu verileri biraz daha güvenli bir biçimde saklamak mümkün olabilir mi? MAC (Machine Authentication Check) adı verilen bir yapı aracılığıyla ViewState nesnesinin kullanıcı tarafından değiştirilip değiştirilmediği ASP.NET sayfası tarafından algılanabilmekte ve kullanıcıya hata gönderilebilmektedir. Bir ASP.NET sayfasının MAC mekanizması bu tip güvenlik nedenleriyle varsayılan olarak açık tutulmakta ve sayfa postback işlemine tabi tutulduğunda sunucu tarafında sayfanın asıl ViewState değeri ile o an kendisine gelen ViewState değerlerini karşılaştırıp eğer farklılık varsa; yani ViewState nesnesi değiştirilmiş ise kullanıcıya hata gönderilecektir. Bu ayarın açık bırakılması bahsettiğimiz gibi güvenlik açısından gereklidir. Fakat bu durumda sayfa üzerinde postback işlemi yapıldığında sunucu tarafında ekstra kontrol işlemleri yapılacağını ve performans açısından bazı kayıplarımız olacağını da unutmamak gerekir. Eğer sayfada ViewState'de taşınan değerlerin kullanıcı tarafından değiştirilmesinin güvenlik açısından bir sakıncası yok ise, bu özelliği kapatmak isteyebiliriz. Sayfa bazında bu özelliği kapatmak istiyorsak Page direktifini, site genelinde kapatmak istiyorsak web.config dosyasını kullanabiliriz. Aşağıdaki örneklerde sadece bir sayfa içerisinde ve web.config dosyası içerisinde eklenecek kodlar görülmektedir.

<%@ Page Language="C#" AutoEventWireup="true" EnableViewStateMac="true"
CodeFile="Default.aspx.cs" Inherits="_Default" %>

<system.web>
.....
.....
   <pages enableViewStateMac="false"></pages>
.....
</system.web>

Bu yazımızda, durum yönetiminde sıklıkla kullandığımız ViewState nesnesinin davranışlarını, nerelerde ne şekilde verileri sakladığını, bu nesne içerisinde ne şekilde nesneler saklayabileceğimizi, performans ve güvenlik açısından dikkat etmemiz gereken noktaları incelemeye çalıştık. Bir başka makale görüşmek dileğiyle.

Uğur UMUTLUOĞLU
www.nedirTV.com