İstemcilerin, sayfalarımızı mümkün olduğunca kısa bir süre de görüntüleyebilmesi öncelikli hedeflerimizdendir. Bu performans bir çok etkene bağlıdır. Örneğin, veritabanı işlemlerimizin hızı, sayfadaki hesaplama vb işlemlerin hızı, sayfa aktarımının sıkıştırılması vs vs. Daha önce sayfa içeriğinin sıkıştırılması ile ilgili HTTP Sıkıştırması (HTTP Compression) konulu bir yazı yazmıştım. HTTP Sıkıştırması konulu bu yazıyı okumanızı şiddetle tavsiye ediyorum.
Bu yazımda ise performans konusunda yine önemli kazançlar sağlamanızı sağlayabilecek bir konudan yani ViewState sıkıştırmasından bahsetmeye çalışacağım.
Bildiğiniz üzere web sayfalarımızdaki sınıflarımız, alanlarımız, değişkenlerimiz vs vs kontrollerdeki değerler de dahil olmaz üzere sayfanın her gidiş gelişinde(postback) kaybolur. Zaten aksi durumda, binlerce kullanıcısı olan bir web uygulamasında-sitesinde- gerekecek bellek ve işlemciye sahip sunucuyu yer yüzünde bulmak mümkün olmazdı herhalde. Ancak sayfalardaki değerlerin her postbackte kaybolması web programcılığını imkansız bir hale getirirdi. Ya da bu değerleri tamamen kaybetmesek de her seferinde post edilen formun içinden tek tek alıp ilgili kontrollere geri yüklememiz gerekse bu da işimizi çok zorlaştırırdı. Çünkü gerçekte sizin sayfanın kod kısmında kullandığınız bir textbox ile ziyaretçinin sayfada gördüğü textbox aynı şeyler değildir. Kodda kullandığımız textbox metodları eventleri olan bir kontroldür ancak istemcide görünün textbox ise basit bir html yazısıdır. Ziyaretçinin sayfayı post etmesi, sayfadaki form elemanlarının ikili değerler halinde ("textBoxKullanıcıAdi", "Ömer Faruk") ... şeklinde gönderilmesi şeklindedir. İşte bu aşamada bu verinin kullanılarak kod tarafındaki textBoxKullanıcıAdi kontrolüne bu değerin atanması ile işimiz kolaylaşmış olur.
Asp.Net' te bu mekanizma ViewState ile sağlanır. Sayfadaki kontrollerde bulunan değerler ya da daha sonra kullanmak üzere sizin ViewState' e attığınız bilgiler özel şekilde bir metin haline dönülştürülerek sayfanın içinde gizli bir alanda saklanır. Bu işlem sayfanın SavePageStateToPersistenceMedium metodunda yapılır. ViewState değerini sayfada göremezsiniz ama sayfanın kaynağına yani html sine bakarsanız görebilirsiniz, insanın okuyamayacağı saçma sapan görünen ama uzun mu uzun bir yazı şeklindedir. Ziyaretçi sayfada metin girme seçenek seçme vb işlemler yapıp da düğmeye basarak vb bir şekilde sayfayı tekrar post ettiğinde sayfanın LoadPageStateFromPersistenceMedium metodunda ViewState' deki değerler ilgili kontrollere geri yüklenir elbette aralarından değişenler olmuşsa yeni değerler kullanılır.
ViewState bilgisi illa ki sayfanın html sinde saklanmak zorunda değildir. SessionState, veritabanı vb. yerlerde de saklanabilir. Ancak web ortamında kullanıcının sayfanızı kapattığı ya da terkettiğini bilememeniz sebebi ile ilgili ViewState bilgisini ne zamana kadar saklamanız gerektiğini bilemezsiniz. Bunu bilseniz bile ziyaretçi sayınız da çok ise bu kadar veriyi saklayacak kaynaklara sahip olamayabilirsiniz. Bu sebeple genel olarak ViewState' in sayfanın içerisinde yani bir daha lazım olana kadar istemci makinede saklanması doğru bir tercihtir.
Biraz karışık da olsa işin özünü anlatmaya çalıştıktan sonra şimdi gelelim konumuza. ViewSate' in sayfanın içinde istemciye gönderilmesinin yukarıda anlatılan avantajlarının yanında sayfanın aktarımını ve dolayısı ile de yüklenmesini geciktireceği bariz bir dezavantajdır. Bu bilginin istemci tarafında hiç kullanılmayacağını da göz önüne alırsak bu düz yazı şeklindeki bilgiyi sıkıştırmamız için hiç bir engel yok denebilir. Tabi sıkıştırma işleminin sunucunun işlemci kaynaklarını kullanacağını düşünürsek içinde bulunduğumuz duruma göre aktarım hızı yerine işlemci hızını da tercih etmemiz gerekebilir. Sunucumuzun işlemcisinin boş kapasitesinin olduğunu yani işlemcinin sorun oluşturmadığını varsayarak konumuza devam edelim.
Daha önce de belirttiğim gibi ViewState' in kaydedilmesi ve yüklenmesi sırasıyla sayfanın SavePageStateToPersistenceMedium ve LoadPageStateFromPersistenceMedium metodları içinde olur. İşte biz de bu metodları override ederek ve framework içindeki sıkıştırma araçlarını kullanarak ViewState bilgisini sıkıştıracağız. Bunun için aşağıda göreceğiniz şekilde Page(Sayfa)' dan türeyen bir class ile bu metodları override edeceğiz. Bundan sonra yapmanız geerken tek şey web sayfalarınızı System.Web.UI.Page de bu class' tan yani PageWithCompressedViewstate ten türetmenizdir.
Yazıyı daha fazla uzatmamak için kodu açıklamıyorum. İşte kodumuz:
Imports System.IO
Imports System.IO.Compression
Public Class PageWithCompressedViewstate : Inherits System.Web.UI.Page
Private Const buffer_size As Integer = 1000
Public Shared Function ReadAllBytesFromStream(ByVal stream As Stream) As Byte()
Dim readBytes() As Byte = New Byte(0) {}
Dim offset As Integer = 0
While True
Array.Resize(readBytes, readBytes.Length + buffer_size)
Dim bytesRead As Integer = stream.Read(readBytes, offset, buffer_size)
Array.Resize(readBytes, readBytes.Length - buffer_size + bytesRead)
If bytesRead = 0 Then
Exit While
End If
offset += bytesRead
End While
Return readBytes
End Function
Protected Overrides Function LoadPageStateFromPersistenceMedium() As Object
Dim vState As String = Me.Request.Form("__VSTATE")
Dim compressedbytes As Byte() = System.Convert.FromBase64String(vState)
Dim ms As New MemoryStream(compressedbytes)
Dim gz As New GZipStream(ms, CompressionMode.Decompress)
Dim bytes() As Byte = ReadAllBytesFromStream(gz)
gz.Close()
ms.Close()
Dim format As New LosFormatter
Return format.Deserialize(System.Convert.ToBase64String(bytes))
End Function
Protected Overrides Sub SavePageStateToPersistenceMedium(ByVal viewState As Object)
Dim format As New LosFormatter
Dim writer As New StringWriter
format.Serialize(writer, viewState)
Dim viewStateStr As String = writer.ToString()
Dim bytes As Byte() = System.Convert.FromBase64String(viewStateStr)
Dim ms As New MemoryStream()
Dim compressedzipStream As New GZipStream(ms, CompressionMode.Compress, True)
compressedzipStream.Write(bytes, 0, bytes.Length)
compressedzipStream.Close()
Dim compressedBytes() As Byte = ms.ToArray
ms.Close()
Dim vStateStr As String = System.Convert.ToBase64String(compressedBytes)
If String.IsNullOrEmpty(Request("HTTP_X_MICROSOFTAJAX")) = False Then
ScriptManager.RegisterHiddenField(Me, "__VSTATE", vStateStr)
Else
ClientScript.RegisterHiddenField("__VSTATE", vStateStr)
End If
End Sub
End Class
Sonuç: Bu işlemi denemek için bir gridview içinde veritabanından getirilmiş verileri gösteren bir sayfa hazırladım. Sayfa normal bir şeklide oluşturulup da istemciye yüklendiğinde dosya boyutu 313KB oldu. Aynı sayfayı ViewState bilgisini sıkıştırarak oluşturduğumuz da ise boyutu 233KB' a düştü. Yani yaklaşık olarak %25' lik bir kazanç sağlamış olduk. Tabi bu oran her sayfanın içeriğine göre değişecektir ama performans performanstır.