CompiledQuery (Derlenmiş Sorgu)

Eyl 06, 2013
LINQ (Language Integrated Query), .NET nesnelerini aynı SQL Server veritabanında olduğu gibi (select, where, join, aggregate işlemleri vb) sorgulanmasını sağlayan mimaridir. Sadece veritabanı için değil aynı zamanda daha birçok farklı şekilde kullanılabilmektedir. Fakat en çok veri tabanı ile ilgili kullanıldığı için akla bu şekilde gelmektedir.


Linq ile yazılmış olan herhangi bir veritabanı sorgusu çalıştırılmak istendiğinde her zaman syntax(sözdizimi) kontrolünden geçtikten sonra veritabanına özel bir sql sorgusuna dönüşmektedir. CompiledQuery(Derlenmiş Sorgu) sınıfının yazılmasının en büyük sebebi, bu her zaman gerçekleşen çevirme işlemini azami düzeye indirip, performans artışı sağlamaktır. Konuyu daha net anlamak için aşağıdaki resim incelenebilir.




Yukarıdaki aşamalara bakıldığında linq to sql sorgusuna müdahale edilemeyeceği açıktır. Çünkü sorgular istenildiği gibi kullanılabilir. Bu yazılan sorguların çevriminde ortaya çıkacak olan sonuca da müdahale etmek olamayacağına göre bu her zaman gerçekleşen çevrim olayını global(genel) bir alanda saklanabilirse (cashing-önbellekleme) gereken zamanlarda o alandan alınıp kullanıldığı zaman, aradaki aşamalar yapılmayacağı için performans artışı sağlanır. 

Linq to sql sorgusu ilk çalıştırıldığı anda 1 numaralı okları takip ederek veritabanına ulaşır. Çağrılan sorguya benzer bir sorgulama tekrar yapıldığında ise 2 numaralı oklar takip edilerek veritabanına ulaşılır. Sorgu önbellekte tutulan sonuçtan faydalanır. Böylece arada atlanan aşamalar sayesinde bir performans artışı sağlanır. 

Bu işlemleri gerçekleştiren CompiledQuery isimli sealed sınıf System.Data.Linq namespace(isimalanı) altında bulunmaktadır. Bu sınıf sayesinde sorgulara önbellekleme işlemi yapılabilir. Sınıfın içinde Compile isimli static ve üç farklı sürümü bulunan geri dönüş değeri generic(şablon)  bir delegate(temsilci) olan bir metod bulunur. Metodun geri dönüş değerinin bir temsilci olması istenilen kriterlere göre hazırlanan metodun çağrılmasına olanak sağlamaktadır. 

public static Func<TArg0, TArg1, TResult> Compile<TArg0, TArg1, TResult>(Expression<FuncTArg0, TArg1, TResult>> query) where TArg0 : DataContext;

TArg0: DataContext(Veri İçerikleri) yani tabloların bulunduğu sınıftır.
TArg1: Seçici olarak kullanılacak parametreyi belirtir.
TResult: İşlem sonucunda geriye dönecek verinin türünü belirtir.
query: Linq To Sql sorgusunu temsil etmektedir.

Örnek Uygulama:

namespace CacheTest
{
    class Program
    {
        static void Main(string[] args)
        {    
        NorthwindDataContext nt = new NorthwindDataContext();
        int donguSayisi = 1;
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < donguSayisi; i++)
            {        
            foreach (var p in Deneme.Sorgula(nt, "London"))
            { }
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedMilliseconds.ToString()); 
        }
    }
    public class Deneme
    {
        public static Func<NorthwindDataContext, string, 
        IQueryable<Customer>> Sorgula = CompiledQuery.Compile 
        ((NorthwindDataContext nt, string sehir) =>
            from s in nt.Customers
            where s.City == sehir
            select s);
    }
}

Açıklama:
  1.  Öncelikle projenin veritabanı bağlantısının ayarlanması gerekir. Bu bağlantı ayarlarını yapmak için Project (Proje) -> Add New Item(Yeni Öğe Ekle) tıklanıpLINQ to SQL Classes seçilip isminin veritabanına uygun yazılması gerekir. Add(Ekle)'ye tıklandıktan sonra tabloların bulunacağı sınıf eklenmiş olur.



    Örnek uygulamada Northwind adında bir veri içerikleri oluşturulmuştur. Veri içerikleri yukarıda oluşturulan linq to sql sınıfıdır. Bu veri içeriklerinin içine veritabanından istenilen tablo sürüklenip bırakıldıktan sonra projenin veri tabanıyla bağlantısı sağlanmış olur. 



  2. Bu veri içerikleri de projede kullanılacak olan tablolar bulunmaktadır. Bu uygulama için örnek olarak Nortwind veri tabanında bulunan Customers tablosu kullanılmıştır. Dongusayısı değişkeni ile linq sorgusunun kaç kere çalışacağı belirtilir. Stopwatch sınıfı kullanılarakta işlemin ne kadar zamanda yapıldığı bulunur. Stopwatch sınıfını kullanmak için System.Diagnostics isimalanını eklemek gerekmektedir. For döngüsünün içerisinde Deneme sınıfında tanımlanmış olan Sorgula() metodu çağrılır. Bu metod sayesinde Customers tablosunun içindeki City sütununda "London" katarı bulunan satırlar belirlenir. Son olaraktaMain içerisinde işlemin gerçekleştirilme süresi gösterilir.

  3.  Aynı işlem normal linq sorgusu ile yapılmak istendiğinde Sorgula() metodu silinip aşağıdaki kod yazılmalıdır.

    foreach (var p in from f in nt.Customers where f.City  
             == "London" select f)
             { }

  4.  Döngüsayısı değiştirilerek Compile metodunun kazandırmış olduğu avantaj test edilebilir.

    Döngü Sayısı  CompiledQuery    Normal Linq Sorgusu
     1  71 ms 65 ms 
     10  140 ms 452 ms
     100  250 ms 956 ms 
     1000  463 ms 1749 ms


    Görüleceği üzere döngü sayısı artıkça, compiledQuery kullanılarak hazırlanan linq sorguları daha hızlı çalışmaktadır. Bu da linq to sql sorgularının çok kullanıldığı sistemlere büyük avantaj sağlar.

  5. Yukarıdaki örnekte bir tane parametre alarak sorgulama işlemi yapılmaktadır. Fakat istenirse bu sorgulama işlemi için bir sınıf yazılarak istenilen sayıda parametre gönderilip, filitreleme işlemi daha etkili bir şekilde yapılabilir.

    Parametrelerin tutulup sorguya gönderilecek olan sınıf:

        public class cokluSecim
        {
            public string sirketAdi { set; get; }
            public string ulke { set; get; }
        }

    cokluSecim sınıfını alıp, sorugulama işlemini gerçekleştirecek olan metodun yazıldığı sınıf, yapı olarak önceki metod ile aynıdır.

        public class cokluSecme
        {
            public static Func<NorthwindDataContext, cokluSecim,
            IQueryable<Customer>> cokluSecici =
            CompiledQuery.Compile((NorthwindDataContext nt, 
            cokluSecim filtre) =>
                 from p in nt.Customers
                 where p.CompanyName == filtre.sirketAdi && 
                 p.Country == filtre.ulke
                 select p);
        }

  6. Bu cokluSecici metodunu kullanmak için yine Main içerisindeki Sorgula() metodu silinip aşağıdaki kod yazılmalıdır.

    foreach (var item in cokluSecme.cokluSecici(nt,new cokluSecim()
            {sirketAdi="Queen Cozinha",ulke="Brazil"})) 
            { }