İlkay İlknur

just a developer...

Entity Framework–Compiled Queries ve Performans Karşılaştırmaları

Merhaba Arkadaşlar,

Günümüzde, özellikle yeni geliştirdiğimiz uygulamaların pek çoğunda, database erişim katmanında Entity Framework gibi ORM çözümlerini kullanmaktayız. Böylece hem database tarafına gidip SP yazma zahmetinden kurtuluyoruz ( Entity Framework içerisinde SP çağırma desteği de bulunmaktadır. ) hem de uygulamaları çok daha kısa sürede geliştirip daha dekleratif ( Bir işi nasıl yapacağımızdan çok ne yapacağımıza odaklanmak. Less about how, more about what ) bir yaklaşım uyguluyoruz. Ancak bu teknolojiler her ne kadar işimizi kolaylaştırsa da özellikle performans kritik uygulamalar geliştiriyorsanız yapacağınız testler eğer teknolojiyi kullanırken belirli noktalara dikkat etmediyseniz sizleri oldukça hayal kırıklığna uğratacaktır. :)

Bu yazımızda da Entity Framework içerisinde sizleri performans açısında oldukça rahatlatacak olan Compiled Query kavramını inceliyor olacağız.

Uygulamalarımız içerisinde database’den belirli sabit kriterlere göre veri getiren linq sorgularımız mutlaka bulunmaktadır. Bu sorgular uygulama içerisinde sürekli olarak farklı parametreler alarak çalışırlar ve database’e giderek veriyi uygulama tarafına getirirler. Ancak her çalışmada bu sorguların SQL’e çevrimi vs.. gibi işlemler hep baştan yapılır. Aslında baktığımızda da bir linq sorgusunun çalışması sırasında en büyük zaman kaybı da bir linq sorgusunun SQL’e çevirilip veritabanına gönderilmesi sırasında yaşanmaktadır. İşte bu noktadaki problemi çözmek üzere Compiled Query’ler devreye girmekte ve çalıştırdığımız bu sorguların sadece ilk çalıştırma sırasında derlenmesi ve cache’lenmesi gerçekleştirilmekte, daha sonra ise bu derlenmiş olan sorgunun pek çok kez yeniden kullanımı sağlanmaktadır. Şimdi isterseniz uygulama tarafına geçelim Compiled Query kullanımını ve bize sağladığı avantajları gözlemleyelim.

Compiled Query’nin Kullanılmadığı Durumlar

Örnek olarak BlogEngine database’i içerisinde bulunan Settings tablosunu kullanıyor olacağız. Tabloda basit olarak key-value şeklinde ayarlar tutulmakta. Siz de kendiniz test amacıyla bu şekilde basit bir tablo oluşturabilirsiniz.

Biz örneğimizde basit olarak bu tablodan email bilgisini Linq to Entities sorgusuyla çekiyor olacağız. Hemen hızlıca linq sorgumuzu da yazalım.

public class ConfigurationOperations     
{         
 public static string GetEmailAddress()         
 {
 using (BlogEngineEntities entities = new BlogEngineEntities())
 {                 
 string email = (from inc in entities.be_Settings                                 
 where inc.SettingName == "email"                                 
 select inc.SettingValue).SingleOrDefault();
return email;             
}         
}     
}

Şimdi ise sorgunun çalışma süresini ölçecek olan kodumuzu yazalım. Aynı sorguyu 10 kere arka arkaya çalıştıracağız ve çalışma sürelerini inceleyeceğiz.

static void Main(string[] args) { 

 Stopwatch stopwatch = new Stopwatch();     
 for (int i = 0; i < 10; i++)     
 {         
 stopwatch.Restart();         
 ConfigurationOperations.GetEmailAddress();         
 stopwatch.Stop();         
 Console.WriteLine("{0}. iterasyon {1} milisaniye sürdü.",
 i+1,stopwatch.Elapsed.TotalMilliseconds);      
 }
}

Uygulamayı çalıştıralım ve sorguların toplam çalışma zamanlarını inceleyelim.

Bir de Compiled Query Deneyelim :)

Şimdi sıra geldi Compiled Query kullanımına :) Linq to Entities sorgularının derlenmesi için CompiledQuery isimli tipin static Compile metodunu kullanmaktayız.

Yukarıda da gördüğünüz üzere generic Compile metodunun 16 farklı versiyonu bulunmakta. :) Ancak korkmaya gerek yok hepsinin kullanım prensibi aynı :) Compile metodu derleyeceğimiz linq sorgusunun içerisinde kullandığı parametrelerin tipini ve dönüş tipini generic parametre olarak belirtmemizi gerektirmekte. Buradaki tek kısıt ise şu : ilk olarak vereceğimiz parametre mutlaka ObjectContext’i inherit etmeli. Yani bizim modelimizi temsil eden context olmalı. Son generic parametre ise bizim çalışacak linq sorgusunun dönüş tipini belirtmeli. İlk ve son parametre arasındaki tipler ise linq sorgusu içerisinde kullanıdığınız parametrelerin tipi olmalıdır. :) İlk bakışta biraz karışık olsa da kullandıkça aslında çokta zor olmadığını göreceksiniz. CompiledQuery tipinin Compile metodunun dönüş tipi ise Func tipinden bir delegate. Bu delegate’i static olarak sınıfımız içerisinde tanımlarsak Query bir kez derlendikten sonra pek çok kez doğrudan derlenmiş sorguyu kullanabiliriz.

Şimdi örneğimizi Compiled Query kullanarak geliştirelim.

public class ConfigurationOperations     
{         
 static readonly Func<BlogEngineEntities, string> GetEmailAddressQuery =     
 CompiledQuery.Compile<BlogEngineEntities, string>(             
 (entities) => (from inc in entities.be_Settings                                   
 where inc.SettingName == "email"                                   
 select inc.SettingValue).SingleOrDefault());         

 public static string GetEmailAddress()         
 {             
 using (BlogEngineEntities entities = new BlogEngineEntities())             
 {                 
 string email = GetEmailAddressQuery.Invoke(entities);                
 return email;             
 }         
 }     
}

Gördüğünüz gibi CompiledQuery tipinin static Compile metodunu kullanarak Linq sorgunumuzu parametre olarak geçirip sorgunun derlemesini sağladık. Daha sonra ise derlenmiş olan sorguyu static readonly Func delegate ile saklayıp metotlar içerisinden de sorguyu bu delegate üzerinden çalıştırdık.

Şimdi gelin performans ölçümlerine bakalım.

Gördüğünüz üzere sorguların çalışma süreleri arasında büyük farklar bulunmakta. CompiledQuery kullanımı olan senaryomuzda sadece ilk sorgunun çalışma zamanı uzun sürerken diğer çağrımlar yaklaşık olarak 0,3 milisaniye sürüyor. Bunun nedeni de sadece 1 kez derlenen yani SQL’i oluşturulan sorgunun farklı parametrelerle tekrar tekrar çalıştırılması. Tekrar tekrar çalıştırdığımız linq sorgularında mutlaka CompiledQuery kullanmamız gerektiğini bu yazımızın ana fikri olarak söyleyebiliriz :) Özellikle sorgunuzun daha karmaşıklaştığı durumlarda CompiledQuery’lerdeki performans kazancını daha net hissedeceksiniz. ;)

Compiled Query’ler İle İlgili Bilinmesi Gerekenler

Yazımızı sonlandırmadan önce Compiled Query’ler ilgili belirtmek istediğim bir kaç nokta bulunmakta.
  • Compiled Query’ler bir kez derlendikten sonra oluşturulan SQL artık sabittir. Oluşturulan SQL’i değiştirecek çağrılarda bulunmak mümkün değildir.
  • Sorgunun 1 kez derlenmesi sırasında belirtilen Merge değerlerinin değiştirilmesi ilerleyen çağrılarda mümkün değildir.
  • CompiledQuery tipinin içerisindeki Compile metodunun 16 farklı versiyonu vardır. Eğer linq sorgunuzun parametre sayısı 16 ‘dan fazla ise parametrelerinizi bir struct içerisinde toplamayı deneyin ve Linq sorgusuna ilgili struct’ı parametre olarak geçirin ;)
Görüşmek Üzere,


Yorum Gönder