Burak Selim Senyurt(MVP)
Matematik Mühendisi bir .NET Severin Yazıları...

Screencast - Asp.Net 4.0 - Session State Compression [Beta 2]

Perşembe, 19 Kasım 2009 09:45 by bsenyurt

Merhaba Arkadaşlar,

Web uygulamalarının Stateless olmaları nedeniyle, istemcilerin sayfalar arasında gezerken takip etmek istedikleri verileri kolayca taşıyabilmeleri için ele alınan yöntemlerden birisi de Session kullanımıdır. Sunucu tarafında Session içerikleri 3 farklı şekilde yönetilebilmektedir. Varsayılan olarak In-Proc mod geçerlidir. Yani Asp.Net Worker Process tarafından web uygulamasına ait bellek alanında saklanmaktadır. Ancak Web Farm kullanıldığı vakalarda Session bilgilerinin arka tarafta yer alan başka sunucularda yönetilmesi de sağlanabilir. Bu noktada Sql veritabanı veya State Server isimli Windows Service seçenekleri geçerlidir. Özellikle In-Proc dışında kullanılan modlarda serileşen içeriğin çok büyük boyutlarda olmasına karşın Asp.Net 4.0 ile birlikte sıkıştırılabilme opsiyonunun getirildiği görülmektedir. Bunun için Web.config dosyasında yapılacak küçük bir ekleme yeterlidir. Devamı görsel dersimizde Wink

   

Süre : 14:53

Yeni görsel derslerde görüşmek dileğiyle hepinize mutlu günler dilerim.

Asp.Net 4.0 - Heryerde Cache [Beta 2]

Çarşamba, 18 Kasım 2009 13:20 by bsenyurt

Merhaba Arkadaşlar,

Kronometrelerinizi hazır edin! Bu yazımızda Absolute ve Sliding Expiration modelinde ön bellekleme işlemlerini Windows tabanlı bir uygulama üzerinde gerçekleştiriyor olacağız. Durun bir dakika...Windows mu? Evet evet yanlış duymadınız Windows. Aslında başlıktaki konu ile tamamen tezat bir durum. Gerçekten de öyle mi acaba? Gelin şu meseleyi açıklığa kavuşturalım. Smile

Ön bellekleme işlemleri ile performans arttırımı çoğunlukla web uygulamalarında akla gelen bir konudur. Ancak gerçek hayat uygulamaları sadece Web tabanlı değildir. Windows Forms, WPF gibi masaüstü uygulamalarından tutunda, katmanları ifade eden Class Library' lere kadar çok çeşitli ürünler yer almaktadır. Dolayısıyla performans kazanımı, iş yükünün hafifletilmesi için ön bellekleme işleminin sadece Web uygulamalarına bağımlı kalması düşünülemez. Peki Web ortamı dışında ön bellekleme(Caching) teknikleri için hangi imkanlar bulunmaktadır?

Aslında listenin başında System.Web.Caching.dll assembly' ının Web dışındaki uygulamalara referans edilerek kullanılmasının yer aldığını söyleyebiliriz. Ne varki bir Windows uygulamasına Web alanına ait bir Assembly' ın referans edilmeside son derece gariptir. Undecided Daha etkili bir yöntem olarak Enterprise Library içerisinde yer alan Caching bloğunun kullanılması tercih edilebilir. Bunların haricinde henüz incelediğim ve yakında sizinle ilk bilgilerimi paylaşacağım Velocity projesi de yer almaktadır ki dağıtık ön bellekleme(Distributed Caching) ve dolayısıyla Cache üzerinde Load Balancing vb imkanlar sunmakta olan bir projedir. Ancak Asp.Net 4.0' ın duyurulması ile birlikte özellikle Overivew dökümünında belirtilen yeni bir koz daha bulunmaktadır. System.Runtime.Caching.dll assembly' ı. Biz bu gün geliştireceğimiz örnekte ObjectCache tipinden yararlanarak ön bellekleme işlemlerini yapmaya çalışacağız.

Bu assembly içerisinde yer alan tiplerden yararlanarak özel Cache sağlayıcılar, tipler geliştirebileceğimiz gibi In-Memory ön bellekleme yeteneklerini web dışındaki uygulamalarda da kullanabiliriz. Üstelik .Net Framework içerisine dahil edilmiş olması, Enterprise Library gibi üçüncü parti kurulumlara ihtiyaç duymayışı, kullanımının Web tarafındaki Cache nesnesi ile neredeyse aynı olmasıda avantaj olarak görülebilir. Bu yazımızda söz konusu ön bellekleme tekniklerine sadece iki açıdan bakıyor olacağız. Sliding ve Absolute Expiration tipli ön bellekleme işlemleri.

Bilindiği üzere ön bellekte tutulacak olan verinin ne kadar süre ile duracağının belirtilmesi Absolute Expiration tekniğini ilgilendiren bir meseledir. Diğer yandan belirtilen süre içerisinde ön bellekte tutulan Cache nesnesine talep gelmesi sonrası, o andan itibaren tekrardan belirtilen süre kadar yaşam ömrünün uzatılması da Sliding Expiration tekniğini ilgilendiren bir konudur. Bunlara ek olarak ön belleklemede popüler olarak kullanılan bağımlılık(Dependency) gibi tekniklerde bulunmaktadır. Buna görede Cache nesnesinin içeriğinin tazelenmesinin bir sebebe bağımlı hale getirilmesi sağlanabilir. Bu sebep basit bir dosyadaki değişiklik olabilir örneğin.

Dilerseniz hiç vakit kaybetmeden Visual Studio 2010 Ultimate Beta 2 üzerinde basit bir Windows uygulması geliştirerek yolumuza devam edelim. Örneğimizde Product tipinden nesne örnekleri tutan generic bir List<T> koleksiyonunun içeriğinin Absolute ve Sliding olarak ön belleklenmesi işlemlerini göz önüne alıyor olacağız. Tabi ilk etapta System.Runtime.Caching assembly' ının Win Forms uygulamasına referans edilmesi gerekmektedir.

WinForms uygulamamızın basit ekran görüntüsü ise aşağıdaki gibidir.

Gelelim kodlarımıza;

using System;
using System.Collections.Generic;
using System.Runtime.Caching;
using System.Windows.Forms;

namespace UsingObjectCache
{
    public partial class Form1
        : Form
    {
        // Cache nesnelerine erişmek için kullanılan tip tanımlanır
        ObjectCache cachedObject;

        public Form1()
        {
            InitializeComponent();

            // Varsayılan MemoryCache referansı elde edilir
            cachedObject = MemoryCache.Default;
        }

        // Cache üzerinden Products Key değerine sahip içeriği getirmek için kullanılan olay metodudur
        private void btnGet_Click(object sender, EventArgs e)
        {
            // Eğer ProductList Key değerine sahip ön belleklenmiş bir içerik var ise
            if (cachedObject["Products"] != null)
            {
                // Indeksleyici yardımıyla ilgili Cache nesnesi çekilir
                lstProducts.DataSource = cachedObject["Products"] as List<Product>;
                lblInformation.Text = "Ürün listesi içeriği ön bellekten getirildi";
            }
            else
            {
                lblInformation.Text = "Cache içerisinde söz konusu liste yok\n Bilgiler tekrardan üretilecek";
                lstProducts.DataSource = CreateProductList();
            }
        }

        // Cache' lenen veriyi belirli bir süreliğine tutmak için gerekli ayarlamaları yapan olay metodudur.
        private void btnAbsoluteExpiration_Click(object sender, EventArgs e)
        {
            // Zaten Products isimli bir Cache içeriği yoksa, bu Key değerine sahip olan bir Cache içeriği oluşturulur ve o andan itibaren 30 saniye süreyle ön bellekleneceği belirtilir.
            if (cachedObject["Products"] == null)
                if (cachedObject.Add("Products", CreateProductList(), new DateTimeOffset(DateTime.Now.AddSeconds(30))))
                    lblInformation.Text = "Absolute Expiration(30 Seconds)";
        }

        // Kayan süreli Cache' leme için gerekli işlemleri yapan olay metodudur.
        // Buna göre 30 saniyelik süre dolmadan gelen talepler, Cache' te tutulmas süresini o andan itibaren tekrar 30 saniye ileri götürürler
        private void btnSlidingExpiration_Click(object sender, EventArgs e)
        {
            // Absolute Expiration, Sliding Expiration, Dependency gibi Cache çeşitlerini istersek ilke(Policy) olarak tanımlayabiliriz.
            CacheItemPolicy policy = new CacheItemPolicy();
            policy.SlidingExpiration = TimeSpan.FromSeconds(30);

            if (cachedObject["Products"] == null)
                if (cachedObject.Add("Products", CreateProductList(), policy))
                    lblInformation.Text = "Sliding Expiration(30 Seconds)";
        }

        // Maliyetli olduğu varsayılan ve içeriği ön bellekte tutulabilecek olan sembolik bir fonksiyonellik
        private List<Product> CreateProductList()
        {
            return new List<Product>
                {
                    new Product{ ProductId=10, Name="Bardak", ListPrice=1.23M},
                    new Product{ ProductId=11, Name="Tabak", ListPrice=1.25M},
                    new Product{ ProductId=12, Name="Çatal", ListPrice=0.55M}
                };
        }
    }

    // Yardımcı tip
    class Product
    {
        public int ProductId { get; set; }
        public string Name { get; set; }
        public decimal ListPrice { get; set; }

        public override string ToString()
        {
            return String.Format("{0}|{1} {2}", ProductId.ToString(), Name, ListPrice.ToString("C2"));
        }
    }
}

Cache yönetimi için MemoryCache.Default özelliğinin ürettiği ObjectCache nesne referansı kullanılmaktadır. ObjectCache tipinin indeksleyicisinden yararlanılarak ön bellekte tutulan bir Key değerine ve doğal olarak işaret ettiği Object tipinden içeriğe ulaşmak mümkündür. Diğer yandan Add ve Set gibi metodlar yardımıyla ön belleğe veri atılmasıda sağlanabilir. Burada dikkat çekici noktalardan biriside ön bellekleme tipi için ilkelerden(Policy) yararlanılabilmesidir. Örneğimizde Sliding Expiration için CacheItemPolicy nesne örneği tanımlanmıştır. Bu nesne örneği aslında ön bellekleme ilkesini ifade etmektedir. Önbelleğe atılacak nesne içeriğinin bu ilkeye göre tutulacağını belirtmek içinse Add metoduna parametre olarak verilmesi yeterli olmuştur. Aslında Add metodunun aşırı yüklenmiş versiyonlarından birisi de Absolute Expiration tekniğine göre parametre almaktadır ki buda örneğimizde kullanılmıştır.

Gelelim testlerimize. Hatırlarsanız kronometrelerinizi hazır tutmanızı söylemiştim. Bu tabiki işin şakası Wink Nitekim kodun içerisinde de test amaçlı olarak bir kronometre kullanılabilir. Çalışma zamanındaki testlerimizi aşağıdaki adımlarda olduğu gibi geliştirelim.

1 - Uygulama başlatıldıktan sonra Get başlıklı düğmeye basılır ve aşağıdaki ekran görüntüsü ile karşılaşılır.

Get içerisinde yapılan çağrıda ObjectCache içerisinde Products isimli bir Key bulunmadığından ürün listesinin maliyeti yüksek olduğu düşünülen bir metod ile üretilmesi gerçekleştirilir.

2 - 1nci testten sonra Absolute Expiration veya Sliding Expiration düğmelerinden birisi kullanılır. Absolute Expiration başlıklı düğme tıklandığında aşağıdaki ekran görüntüsü ile karşılaşılmalıdır.

Buna göre Product listesi ön bellekte 30 saniye süreyle saklanacaktır. Süre dolmadan Get düğmesine basılırsa aşağıdaki ekran görüntüsü ile karşılaşılmalıdır.

Şimdi 30 saniyelik süre sona erdikten sonra tekrar Get düğmesine basılırsa artık ön bellekte bir veri tutulmadığından ürün listesi üretim işleminin tekrar yapıldığı görülmelidir.

3 - 2nci testin bitmesinden sonra seçiminize göre diğer ön bellekleme tekniğini ele alabilirsiniz. Benim sırama göre şimdi Sliding Expiration testinin yapılması gerekmektedir. Program açık iken Sliding Expiration başlıklı düğmeye asarsanız aşağıdaki ekran görüntüsü ile karşılaşırsınız.

Bu adımdan sonra 30 saniyelik süre dolmadan tekrar Get düğmesine basarsanız içeriğin ön bellekten getirildiğini görebilirsiniz. Ancak önemli olan nokta şudur; Ön bellekte durma süresi, 30 saniyelik süre içerisinde Get düğmesine bastığınız andan itibaren 30 saniye sonrasına uzamasıdır.

4 - Uygulamayı herhangibir Cache tekniği uygulandıktan sonra ilgili süreler aşılmadan kapatıp tekrar açınız ve yine Get düğmesine basınız. Bu durumda içeriğin ön bellekten değil tekrardan üretim ile geldiğini görmelisiniz ki bu son derece doğaldır. Çünkü uygulama sonlandırılmış ve kendisi için ayrılan bellek içeriği bir sonraki uygulama örneği için geçersiz hale gelmiştir. Hımmm...!!! Aslında bu çokda istediğimiz bir vaka olmayabilir.

Tüm bunlar bir yana en iyi test yöntemlerinden biriside uygulmayı Debug ederek incelemeniz olacaktır ki bunu yapmanızı şiddetle tavsiye ederim. Böylece geldik bir yazımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.

UsingObjectCache.rar (42,82 kb)

Asp.Net 4.0 - Özelleştirilmiş Cache Sağlayıcısı(Custom Cache Provider) [Beta 2]

Salı, 17 Kasım 2009 15:10 by bsenyurt

Merhaba Arkdaşlar,

Çok çok uzun zamandır Asp.Net üzerine eğilmiyordum. Hem tasarım yönünden kabiliyetsiz olmam(iki rengi bir araya getir deseler kesin uyumsuz renkler çıkartırım) hemde servis yönelimli mimari dünyasına dalmış olmamın bunda büyük bir rol oynadığını itiraf etmek isterim. Yine de Asp.Net 4.0 ile birlikte gelen yenilikleri okuyunca biraz olsun araştırmak ve edindiğim tecrübeleri sizlere aktarmak istedim. Dikkatimi çeken ilk özellik ön bellekleme(Caching) sisteminin genişletilebilmesi ile alakalıydı. Bilindiği üzere web uygulamalarında performansı arttırmanın en etikili yollarından biriside sunucu tarafındaki yükü azaltarak mümkün olabilmektedir. Bu manada istemci tarafına, sunucu üzerinde ön belleğe atılmış hazır veri çıktılarını göndermek etkili bir yaklaşımdır. Asp.Net tarafında ön bellekleme için farklı teknikler kullanılabilir. Son süre bildirimli(Expire Date), uzatmalı(Sliding), bağımlı(Dependency, SqlCacheDependency, FileDependency gibi) vb...Üstelik ön belleklenecek veri içeriği sayfa bazında, Web User Control bazında vb olabilir. Yine de eksik olan bir şeyler vardır. Herşeyden önemlisi ön bellekleme gerçekten bellek üzerinde yapılmaktadır. Smile Basit bir blog sitesi için ön belleklenecek veri içeriği çok büyük problem teşkil etmeyebilir. Ne varki çok sayıda kullanıcıya hizmet veren portallerde ön belleklenen nesnelerin sayısının, içeriğinin artması, beraberinde belleğinde ölçeklendirilmesi ihtiyacını doğurmaktadır. Buna göre daha çok bellek almak gibi bir maliyet altına girmek gerekebilir. Bu duruma karşın Asp.Net 4.0 ile birlikte ön bellekleme işlemini daha kolay bir şekilde özelleştirebilme şansına sahibiz. Bir başka deyişle kendi Cache Provider tiplerimizi geliştirerek ön bellekleme yerini ve modelini değiştirebilir kendi algoritmalarımızı işin içerisine katabiliriz. Tabi Beta 2 sürümüne göre bu konuyu incelediğimden ve yazıyı hazırladığım tarih itibariyle internet üzerinde çok fazla kaynak bulamadığımdan halen daha pek çok noktada soru işareti vuku bulmuş durumdadır. Bu nedenle zaman içerisinde bu konuda çok daha detaylı bilgiye ulaşabileceğimizi düşünmekteyim.

Dilerseniz hiç vakit kaybetmeden örnek bir senaryo üzerinden hareket edelim. Amacımız web uygulamamızda kullanılan bir Web User Control' ün içeriğini zamana bağlı olarak ön belleklerken, dosya tabanlı bir sistemden yararlanmak. Buna göre ön belleğe atılacak olan nesne içeriğinin fiziki olarak bir dosya içerisine serileştirmeyi(Serialization) planlıyoruz. Çok doğal olarak web uygulaması içerisinde n sayıda sayfa ve n sayıda Web User Control olabilir. Buda fiziki olarak her Web User Control için birden fazla serileştirilmiş veri içeriği tutan dosya anlamına gelmektedir. Diğer yandan sistemimizde Expire eden dosyaların silinmesi işlemlerini de göz önüne almalıyız. Yani bu dosyaların içerikleri gelen yeni bir talep sonrasında yeniden üretilmeli, talep gelmediği ve Duration dolduğunda ise silinmelidir şeklide bir yol tercih ediyoruz.

Peki Asp.Net 4.0 tarafında bu tip özelleştirilmiş bir Cache sağlayıcısı için ne getirilmiştir?

Aslında tahmin etmek oldukça kolaydır. Var olan bir sisteme yeni bir eklenti ilave etmek istiyorsak tercih edilecek yollardan birisi, sistemin bize söylediği kurallara uymaktır. Wink Şimdilik bu kuralı söyleyen OutputCacheProvider isimli abstract bir sınıftır. Söz konusu sınıf Add, Get, Set ve Remove isimli abstract metodlar içermektedir. Buna göre geliştireceğimiz Cache Provider sınıfının bu fonksiyonları mutlaka ve mutlaka ezmesi gerekmektedir. Bir başka deyişle Cache sistemi için gerekli temel CRUD operasyonlarının tarafımızdan uygulanması gerekmektedir. Peki yeterli midir? Elbette değildir. Bir şekilde web uygulaması tarafına, kendi özel Cache sağlayıcımızı kullanabileceğimizi ifade etmemiz gerekecektir ki bunun içinde tahmin edeceğiniz üzere web.config dosyasından yararlanılacaktır. Geliştireceğimiz örnekte özel Cache sağlayıcısını çok kısıtlı olarak kullanabileceğiz. İlk hedefimiz senaryomuzda belirttiğimiz üzere Web User Control içeriklerini ön belleklemektir. Buna göre standart olarak kullandığımız OutputCache direktifinde bir şekilde özel Cache sağlayıcımızı da işaret edebilmeliyiz ki buda oldukça kolaydır. Öyleyse hiç vakit kaybetmeden örneğimizi geliştirmeye başlayalım. İlk olarak DiskCacheProvider isimli OutputCacheProvider tipinden türeyen aşağıdaki sınıfı geliştirmeliyiz.

DiskCacheProvider sınıfına ait diagram;

Custom Provider kodu;

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Timers;
using System.Web.Caching;
using System.Collections.Concurrent;

namespace CustomCaching
{
    public class DiskCacheProvider
        : OutputCacheProvider
    {
        // Expire olan Cache nesnelerini tutan dosyaların silinmesi için bir Timer nesnesi kullanılır
        Timer watcher;
        // Cache nesnelerinin serileştirildiği dosyaların tutulduğu klasör
        string cachePath = "C:\\Cache\\";
        // Dosya adı ve Expire zamanlarını tutan koleksiyon nesnesi. Eş zamanlı çıkartma işlemine destek verebilmek için .Net 4.0 ile gelen Concurrent koleksiyonlardan birisi kullanılmaktadır.
        ConcurrentDictionary<string, DateTime> cacheExpireList;

        // Yapıcı metod içerisinde gerekli nesne başlatma işlemleri yapılır
        public DiskCacheProvider()
        {
            cacheExpireList = new ConcurrentDictionary<string, DateTime>();

            // Timer nesne örneği 3 saniyede bir Elapsed olayını tetikleyecektir
            watcher = new Timer(3000);
            // Elapsed olayı içerisinde Expire olan output cache dosyalarının bulunması sağlanır
            watcher.Elapsed += (o, e) =>
            {
                // Koleksiyonda duran Expire zamanı ile güncel zaman karşılaştırılarak bir sonuca gidilmeye çalışılır
                var discardedList = from cacheItem in cacheExpireList
                                    where cacheItem.Value < DateTime.Now
                                    select cacheItem;
                // Expire olmaya aday olan Cache nesnelerine ait dosyalar için Remove metodu çağırılır.
                // Eğer normal bir Dictionary<T,K> koleksiyonu kullanılırsa çalışma zamanında InvalidOperationException alınabilir. Nitekim discardedList ile gezilirken cacheExpireList' in değişmiş olma ihtimali bulunabilir. Bu nedenle ConcurrentDictionary<T,K> kullanılması tercih edilmiştir
                foreach (var discarded in discardedList)
                {
                    Remove(discarded.Key);
                    // Koleksiyondan çıkartılır
                    DateTime discardedDate;
                    cacheExpireList.TryRemove(discarded.Key, out discardedDate);
                }
            };
            // Timer nesne örneği başlatılır
            watcher.Start();
        }

        // utcExpiry parametresi, Cache için Expire süresini belirtir
        public override object Add(string key, object entry, DateTime utcExpiry)
        {
            FileStream fs = new FileStream(String.Format("{0}{1}.binary", cachePath, key), FileMode.Create, FileAccess.Write);
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(fs, entry);
            fs.Close();
            cacheExpireList.TryAdd(key, utcExpiry.ToLocalTime());
            return entry;
        }

        // Cache' den nesnesi elde etmek için kullanılan metoddur
        // OutputCacheProvider abstract sınıfından gelen ve ezilmesi mecburi olan bir fonksiyondur.
        public override object Get(string key)
        {
            string path = String.Format("{0}{1}.binary", cachePath, key);
            // Örnekteki sistem Cache nesne içeriklerini dosya tabanlı olarak tutmaktadır. Bu noktada dosyanın sistemde var olup olmadığına bakılarak Cache' lenen bir içerik olup olmadığı sonucuna varılabilir
            if (File.Exists(path))
            {
                FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read);
                BinaryFormatter formatter = new BinaryFormatter();
                // Ters serileştirme işlemi ile dosya içeriğinden Cache' lenen nesnenin canlandırılması sağlanır
                object result = formatter.Deserialize(fs);
                fs.Close();
                return result;
            }
            else
                return null;
        }

        // Cache nesnesinin kaldırılması için kullanılan metoddur. OutputCacheProvider abstract sınıfından gelen ve ezilmesi mecburi olan bir fonksiyondur.
        // OutputCacheProvider abstract sınıfından gelen ve ezilmesi mecburi olan bir fonksiyondur.
        public override void Remove(string key)
        {
            string path = String.Format("{0}{1}.binary", cachePath, key);
            if (File.Exists(path)) // Eğer dosya var ise Cache' lenen bir nesne var olduğu sonucuna ulaşabiliriz
            {
                // Dosya silinir
                File.Delete(path);
            }
        }

        // Set metodunda Cache in saklanması işlemleri gerçekleştirilir. Genellikle overwrite mantığına göre çalışır. Yani Cache' lenen nesne varsada üzerine yazılması yoluna gidilir
        // OutputCacheProvider abstract sınıfından gelen ve ezilmesi mecburi olan bir fonksiyondur.
        public override void Set(string key, object entry, DateTime utcExpiry)
        {
            // Cache' lenen içerik varsa overwrite işlemi yapılır.
            string path = String.Format("{0}{1}.binary", cachePath, key);

            FileStream fs = new FileStream(path, FileMode.Create, FileAccess.Write);
            BinaryFormatter formatter = new BinaryFormatter();
            // Cache nesnesi serileştirilerek dosyaya yazdırılır
            formatter.Serialize(fs, entry);
            fs.Close();
            // Cache' lenen nesne serileştirildiği dosyada tutulduğundan expire sürelerini takip edebilmek için ilgili koleksiyonda bilgilendirme yapılır.
            cacheExpireList.TryAdd(key, utcExpiry.ToLocalTime());
        }
    }
}

Şimdi çok kısaca neler yaptığımıza bir bakalım. Cache sağlayıcımız Cache' lenecek olan nesneleri varsayılan olarak C:\Cache isimli bir klasör altında dosyalamaktadır(Aslında bu bilgide ilgili provider' a örneğin yapıcı metod yardımıyla web.config dosyası üzerinden geçirilebilir). Söz konusu nesne içerikleri ilgili dosyalara binary formatta serileştirilmektedir. Bu amaçla BinaryFormatter tipinden yararlanılmaktadır. Özellikle Cache' e nesne atma işlemi sırasında devreye giren Set metodu içerisinde, dosya adı için key parametresininin kullanıldığına dikkat edilmelidir. Hatta asıl dikkat edilmesi gereken nokta her cache için değişik bir key değerinin üretildiğidir(Ancak ispatını yapamadım henüz bunu belirteyim). Diğer yandan Expire süreleride ilgili metodlara(örneğin Set) parametre olarak gelmektedir. Bu süre bilgilerinden yararlanılarak ön bellekten düşürme işlemleri yapılmalıdır. Örneği geliştirirken beklentilerimi boşa çıkaran noktalardan biriside aslında Expire süreleri ile ilişkilidir. Şöyleki; örneği geliştirirken Remove metodunun belirtilen Duration süresi sonlandığında otomatik olarak devreye gireceğini tahmin etmiştim. Ancak bu şekilde olmadı Undecided Bu nedenle Cache' lenen dosyaların zamanı geldiğinde silinmesi için bir mekanizmanın geliştirilmesi gerekiyordu. Bu amaçla Timer nesnesinden yararlanarak Elapsed olayı içerisinde gerekli silme işlemlerini gerçekleştirmeyi tercih ettim.  Çok doğal olarak hangi dosyaların silinmesi gerektiğini anlayabilmek içinde basit bir koleksiyon tabanlı yapıyı tercih ettim. Tabiki Thread Safe bir yapı söz konusu değil. Hatta eş zamanlı gerçekleşebiliecek çıkarma işlemlerine karşın kolaya kaçıp ConcurrentCollection<T,K> kullandığımı ifade edebilirim. Aslına bakarsanız şu an için tek derdim gerçekten özelleştirilmiş bu Cache sağlayıcısının çalışıp çalışmadığını görebilmek. Cool

Şimdi bu Cache Provider tipini web uygulamamızda nasıl kullanacağımızı belirtmemiz gerekiyor. Bu amaçla Web.config dosyası içerisinde aşağıdaki eklemeleri yapmamız yeterli olacaktır.

web.config içeriği;

<?xml version="1.0"?>
<configuration>

    <system.web>
        <compilation debug="true" targetFramework="4.0" />
      <caching>
        <outputCache defaultProvider="AspNetInternalProvider">
          <providers>
            <add name="DiskBasedCacheProvider" type="CustomCaching.DiskCacheProvider,CustomCaching"/>
          </providers>
        </outputCache>
      </caching>
    </system.web>
    <system.webServer>
      <modules runAllManagedModulesForAllRequests="true"/>
    </system.webServer>

</configuration>

Dikkat edileceği üzere providers elementi içerisinde DiskBasedCacheProvider isimli bir bildirim yer almakta ve geliştirdiğimiz DiskCacheProvider tipini işaret etmektedir. Bununla birlikte outputCache elementinin defaultProvider niteliğinde yer alan AspNetInternalProvider değeri, standart Asp.Net ön bellekleme sisteminin kullanılacağını ifade etmektedir. Örneğimize aşağıdaki basit Web User Control' ü ekleyerek devam edebiliriz.

Web User Control içeriği;

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="CurrentDateTime.ascx.cs" Inherits="CustomCaching.CurrentDateTime" %>

<%@ OutputCache Duration="20" VaryByParam="none" ProviderName="DiskBasedCacheProvider" %>
<style type="text/css">
    .style1
    {
        color: #FFCC00;
    }
</style>
<div style="background-color:Gray">
    <strong><span class="style1">Güncel Zaman :
</span>
<asp:Label ID="Label1" runat="server" Text="Label" CssClass="style1"></asp:Label>
    </strong>
</div>

OutputCache direktifi içerisindeki ProviderName niteliği mutlaka dikkatinizi çekmiştir. Burada DiskBasedCacheProvider isimli bir değer kullanılmaktadır. Buda bilindiği üzere web.config dosyasında yer alan özel Cache sağlayıcı tipini işaret etmektedir. Duration değeri 20 olduğu için söz konusu Web User Control içeriğinin 20 saniye süreyle tutulması söz konusudur.

Web User Control kodu;

using System;

namespace CustomCaching
{
    public partial class CurrentDateTime : System.Web.UI.UserControl
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Label1.Text = DateTime.Now.ToLongTimeString();
        }
    }
}

Ve hemen arkasından basit bir Web sayfası...

Default.aspx içeriği;

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CustomCaching.Default" %>

<%@ Register src="CurrentDateTime.ascx" tagname="CurrentDateTime" tagprefix="uc1" %>

<!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 runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
   
        Default Sayfası için Güncel Zaman :
        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>
   
    </div>
    <uc1:CurrentDateTime ID="CurrentDateTime1" runat="server" />
    <p>
        İkinci Web User Control</p>
    <uc1:CurrentDateTime ID="CurrentDateTime2" runat="server" />
    </form>
</body>
</html>

Default.aspx kodu

using System;

namespace CustomCaching
{
    public partial class Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            Label1.Text = DateTime.Now.ToLongTimeString();
        }
    }
}

Tahmin edileceği üzere o anki zaman bilgisini hem aspx sayfası hemde Web User Control içerisinden göstererek bir karşılaştırma yapmaya çalışacağız. Öyleki; eğer Cache sağlayıcımız devreye girerse, Web User Control içeriği 20 saniyeliğine fiziki olarak tutulan bir dosyadan karşılanacak ve bu sebeple sayfanın zaman bilgisi değişirken kendisinin ki sabit olarak kalacak. Tabiki belirtilen Duration süresi kadar. Artık testlere başlayabiliriz. Size önerim indirdiğiniz örneği mutlaka Debug ederek incelemenizdir. Özellikle Set ve Get metodları ile Elpased olayında durmanızı tavsiye ederim. Ben ilk olarak aynı web sayfasına iki farklı talep gönderip aşağıdaki çıktıları elde ettim.

Görüldüğü üzere iki farklı talep gönderilmiş ve özellikle ikince talepte Web User Control içerisindeki zamanın değişmediği görülmüştür. Çünkü bu içerik ilk talep ile birlikte 20 saniyeliğini fiziki olarak bir dosyaya serileştirilmiş ve bu zaman dilim içerisinde sürekli olarak ilgili dosyadan ters serileşerek(DeSerialization) getirilmiş bir HTML çıktısıdır. 20 saniyelik süre sona erdikten sonra sayfalarda herhangibir yeni talep oluşturmassak, ilgili fiziki dosyalarında Cache klasöründen silindiği gözlemlenir. Geliştirdiğimiz örnekte sadece Set, Get ve Remove metodları işlevsel durumdadır. Yani söz konusu vakaya göre Add metodu herhangibir sebeple çalışmamıştır.

Hemen bir noktayı aydınlığa kavuşturalım. Geliştirdiğimiz örnekte Web User Control' ün üretimi özel Cache sağlayıcısı içerisindeki serileştirme ve ters serileştirme gibi işlemlerin çıkarttığı maliyetten daha az olabilir. Yani aslında bu örneğe göre bir Cache sistemi kullanılmasına gerek duyulmaz. Bizim amacımız sadece özel bir Cache sağlayıcısının nasıl yazılabileceğini Asp.Net 4.0 Beta 2 cephesinden incelemektir. Tabiki genişletilebilir Cache sisteminin daha esnek imkanlar sunacağıda belirtilmektedir. Aslında bu konu ile ilişkili özet bir bilgiyi asp.net sitesinden indireceğiniz dökümanda bulabilirsiniz. Böylece geldik bir yazımızın daha sonuna. Tabiki buradaki eksikleri ve gereksinimleri en iyi değerlendirecek kişi sevgili arkadaşım Uğur Umutluoğlu' dur(Asp.Net MVP). Tekrardan görüşünceye dek hepinze mutlu günler dilerim.

CustomCaching.rar (26,21 kb)