Entity Framework ve ORM araçlarının Lazy Loading mantığı

21 Tem

Entity Framework veya NHibernate veya benzeri ORM araçlarının Lazy Loading özelliği ilişkili tablolardan veri çekerken geliştiricilere büyük kolaylık sağlar. Her kolaylığın bir maliyeti vardır ve ilişkili tablolardan veri çekme işlemi sırasında oluşan maliyetin faturası veri tabanlarına yansıtılmaktadır. Bu yazıda veritabanlarından ilişkili veri çekimi sırasında ORM araçlarının nasıl sorgular oluşturduğunu incelemeye çalışacağız.

İlişkili Tablolar
İlişkili Tablolar

Yukarıdaki şema Blog tablosuna bağlı birçok Article olabileceği gibi, Article tablosuna bağlı birçok Tag olabileceğini göstermektedir. (Veritabanı MsSQL Express’dir.)

Bu yapıya göre bir Blog altındaki Article listesini görmek istediğimizde ve her Article için kaç Tag olduğunu listelemek istediğimizde nasıl bir yol izlemeliyiz? Bunu  Entity Framework kullanarak  incelemeye çalışalım.


public class Blog
{
     public int Id { get; set; }
     public string Name { get; set; }
     public virtual ICollection Articles { get; set; }

     public override string ToString()
     {
         return string.Format("{0} ({1})",Name, Articles.Count);
     }
}

public class Article
{
     public int Id { get; set; }
     public int BlogId { get; set; }
     public string Title { get; set; }
     public string Content { get; set; }

     public virtual Blog Blog { get; set; }
     public virtual ICollection Tags { get; set; }

     public override string ToString()
     {
        return string.Format("{0} ({1} Tags)", Title, Tags.Count);
     }
}

public class Tag
{
     public int Id { get; set; }
     public int ArticleId { get; set; }
     public string Name { get; set; }
     public virtual Article Article { get; set; }
}

public class BloggingContext:DbContext
{
     public DbSet Blogs { get; set; }
     public DbSet Articles { get; set; }
     public DbSet Tags { get; set; }
}

Bu kısımda Entity Framework için veritabanı tablolarını temsil eden tipler oluşturulmuştur. App.config veya Web.config dosyaları içerisinde connectionString ayarlamalarını yaptıktan sonra BloggingContext nesnesi ile veritabanında işlemler gerçekleştirilebilmektedir. Bu işlem aşağıdaki gibi yapılmaktadır.

 static void Main(string[] args)
 {
    using (var context = new BloggingContext())
    {
       foreach (var blog in context.Blogs.ToList())
       {
          Console.WriteLine(blog);
          foreach (var article in blog.Articles)
          {
              Console.Write("\t");
              Console.Write(article);
              Console.Write("\n");
          }
       }
    }
 }
Run
Run

Öncelikle Blog’lar listeleniyor ve parantez içinde her Blog türüne bağlı Article sayısı yazdırılıyor. Article satırına da her Article türüne bağlı Tag sayısı yazdırılıyor.

Bu işlem gerçekleşene kadar acaba veritabanında neler olup bitiyor? Bunu anlamak için Express Profiler aracından faydalanabilirsiniz. Express Profiler aracı veritabanına giden tüm sorguları yakalayabilen bir özelliğe sahiptir.

Kodumuzu çalıştırdığımızda Express Profiler penceresinde bir dizi işlemler raporlanacaktır.

Express Profiler
Express Profiler

Entity Framework öncelikle veritabanının var olup olmadığını sorgular. Daha sonra ilgili tabloların olup olmadığını sorgular. Daha sonra MigrationHistory tablolarını sorgular. Derken bizi ilgilendiren kısım olan Blog listesini çeken sorguyu oluşturulur.

Adsız

Bu sorgu sonrasında Blog listesindeki ilk Blog’a bağlı Article listesi seçilir.

Adsız

Article listesi geldikten sonra ilgili Article için Tag listesi çekilir.

Adsız

 

Bu işlem döngü şeklinde devam eder gider.

Şu anda listelediğim örnek tablolarda toplamda 10 veya 15 veri vardır. Bu işlem yüz binlerce kayıt barındıran tablolarda yapılmaya çalışıldığında epeyce maliyetli olabilir. Maliyeti düşürmek adına JOIN ile oluşturulabilecek ham SQL sorguları kullanmak daha ucuz bir yöntem olacaktır.

SELECT B.Name, COUNT(A.Id)
FROM Blogs as B
LEFT JOIN Articles as A
ON A.BlogId=B.Id
GROUP BY B.Name

ORM araçlarına savaş açmak yerine uygun ve yerinde çözümler üretmek hayat kurtarıcı olabilir.

Güncelleme: 25.07.2015, SQL JOIN sorgusu eklendi.

Javascript Array.prototype.slice.apply(arguments)

2 Tem

Geleneksel programlama dillerine göre bir çok yönden farklılık gösteren Javascript dilinde nesne oluşturma fonksiyonlar yardımıyla yapılır. Javascript dilinde, function tipleri normal programlama dillerindeki class benzeri işlev görür. Bir function tipinden yeni bir nesne oluşturmak için new anahtar sözcüğü kullanılır. function tipine dışarıdan gönderilen argümanlar arguments isimli bir iç değişkende tutulur. Aşağıdaki örnek, bir fonksiyona gönderilen argümanları listeleme işlemini göstermektedir.


function f(){
   return arguments;
}

console.log(new f());      // Ekrana [] yazar.
console.log(new f(1,2));   // Ekrana [1,2] yazar.
console.log(f(1,2));       // Ekrana [1,2] yazar.

Ekrana yazım şekilleri her ne kadar Array tipinde görünse bu diziler Array nesnesinin özelliklerini taşımazlar. Yani:


function f(){
   return arguments;
}

console.log(f(1,2) instanceof Array);    // Ekrana false yazar.

Çünkü oluşturulan nesneler Array tipinin push, pop, slice benzeri prototype özelliklerini taşımazlar. Bir fonksiyonun arguments üyesini Array tipine dönüştürmek için Array.prototype.slice.apply(arguments) şeklinde kullanmak gerekmektedir. Bu şekilde kullandığımızda, nesne içerisindeki argümanlar Array tipine dönüştürülmüş olur. Yani bu yöntem ile Array olmayan fakat ona benzeyen nesneleri Array tipine dönüştürür.


function f(){
   return arguments;
}

console.log(Array.prototype.slice.apply(f(1,2)) instanceof Array)
// Ekrana true yazar.

Bu özellikler Javascript dilinde ileri seviye işlemlerde önem arz etmektedir. Örneğin kütüphaneler oluştururken bizim sınıflarımızı kullanan kullanıcıların gönderdiği parametreleri bu şeklide alıp değerlendirmek durumunda kalabiliriz. Açık kaynak javascript kütüphanelerini inceleyerek dilin daha fazla özelliğini kavramak mümkündür.

Bir sonraki yazıda görüşmek dileğiyle.

 

Javascript Class ve Nesne Oluşturma

1 Tem

Javascript programlama dili, geleneksel programlama dillerindeki gibi gelişmiş özelliklere sahip olmasa da nesneye yönelik programlama açısından son derece esnek bir yapıya sahiptir. Bu yazıda nesneye yönelik programlamada önemli bir yer tutan class yapısını incelemeye çalışacağız.

Javascript dilinde class benzeri tanımlamalar fonksiyonlar yardımıyla yapılmaktadır. Class benzeri diyorum çünkü geleneksel programlama dillerindeki gibi class tanımlamak için özel bir yöntem yoktur. Her fonksiyon bir class olarak değerlendirilebilir ve her fonksiyonun new anahtar sözcüğü ile bir nesnesi oluşturulabilir.

1-Fonksiyon Tanımlamak

Normal bir Javascript fonksiyonu tanımlayarak aşağıdaki gibi bir class oluşturabiliriz.


function Map(name){
    this.name = name;
    this.version = "1.0.0";
    this.zoomLevel = 0;
    this.zoomin = zoomIn;
}

function zoomIn(){
    return this.zoomLevel + 1;
}

Yukarıda tanımlanan bir constructor fonksiyondur. Aynı zamanda class olarak değerlendirilir.  Bu class tipinde bir nesne aşağıdaki gibi oluşturulabilir.


var map = new Map("myMap");
map.version = 1.1.1;
map.zoomin();

2-Anti Pattern

Yukarıda tanımlama da zoomIn metodu Map calss dışında ayrı bir şekilde tanımlandığından  bir anti pattern oluşmaktadır. Kodun çalışması açısından hiç bir sorun yoktur. Ancak dışarıda tanımlanan her metod, global namespace içerisinde yer alacağından başka bir amaçla aynı isimde (zoomIn) bir metod tanımlandığında bu durum bir isim karışıklığına sebep olacaktır. Bu tür kirliliklerin önüne geçmek adına ihtiyaç duyulan metodlar constructor fonksiyonların içerisine tanımlanmalıdır.

function Map(name){
    this.name = name;
    this.version = "1.0.0";
    this.zoomLevel = 0;
    this.zoomin = function(){
           return this.zoomLevel + 1;
    }
}

Bu şekilde bir tanımlama kodda her hangi bir değişikliğe sebep olmayacaktır.

3-Metodların Prototype Şeklinde Tanımlanaması

2. maddede olduğu gibi metodları constructor fonksiyon içerisine tanımlayarak anti pattern oluşumundan kurtulmak mümkündür. Ancak bu durumun karşımıza çıkardığı bir dezavantaj vardır. Her nesne oluşturulduğunda zoomin() metodu yeniden oluşturulacaktır. Bu dezavantajı ortadan kaldırmak için zoomin() metodu prototype olarak tanımlanmalıdır.

function Map(name){
    this.name = name;
    this.version = "1.0.0";
    this.zoomLevel = 0;
}

Map.prototype.zoomin = function(){
    return this.zoomLevel + 1;
}

4-Doğrudan Nesne Tanımlamak

Şu ana kadar olan bölümlerde constructor fonksiyonlar yardımıyla sınıf simülasyonları olşuturduk ve new anahtar sözcüğü ile bu constructor fonksiyonlardan nesneler oluşturduk. Bu bölümde doğrudan nesneler tanımlamayı inceleyeceğiz.

Javascript dilinde doğrudan nesne tanımlamak object literal olarak geçer. Doğrudan oluşturulan  nesnelerden new anahtar sözcüğü ile tekrardan nesneler oluşturulamaz. Doğrudan bir nesne oluşturmak:

  var obj = {};

veya

  var obj = new Object();

şeklinde yapılabilmektedir.

Dizler için doğrudan nesne oluşturmak:

  var arr = [];

veya

  var arr = new Array();

şeklinde yapılır.

Herhangi bir sınıf tanımlamadan Map nesnesini doğrudan oluşturmak mümkündür.

var map = {
    this.name = name,
    this.version: "1.0.0",
    this.zoomLevel: 0,
    this.zoomin: function(){
        return this.zoomLevel + 1;
    }
}

Artık bu nesneyi doğrudan kullanmak mümkündür.

   map.version = 1.1.1;
   map.zoomin();

Bu tür tanımlamalar singleton olarak da geçer.

5-Fonksiyonlar Yardımıyla Singleton Nesne Tanımlamak

var map = new function(name){
    this.name = name;
    this.version = "1.0.0";
    this.zoomLevel = 0;
    this.zoomin = function(){
           return this.zoomLevel + 1;
    }
}

Burada tanımlanan constructor fonksiyonunun önünde new anahtar sözcüğü kullanıldığında doğrudan bir nesne oluşturulmuştur.

Böylece Javascript dilinde class ve doğrudan nesne kavramını ve bu türlerin nasıl tanımlandığını ve kullanıldığını incelemiş olduk. Bir sonraki makalede görüşmek üzere.