Entity Framework (Varlık Altyapısı)

Eyl 06, 2013
Linq(Language Integrated Query - Dil ile Tümleşik Sorgu) ile kullanımı yaygın olan veritabanı şemalarını nesnelere dönüştürmekte kullanılan Varlık Altyapısı, ".Net 3.5" platformunun eklentilerinden biridir. Linq to SQL Classes(Linq'den SQL Sınıflarına) yerine ADO .NET Entity Data Model(ADO .NET Varlık Veri Modeli) kullanılmaya başlanmıştır. Kullanılabilmesi için ".NET Framework 3.5 SP1" ve bunun yanında "Visual Studio 2008 SP1" kurulu olması gerekmektedir.

Varlık Altyapısı Kullanım Nedeni

Varlık Altyapısı ile tabloların sınıflara, satırların nesnelere, kolonların değişkenlere dönüştürüldüğü bir yapı olan ORM(Object Relational Mapping - Nesne İlişkili Eşleme)’ye giriş yapılmıştır. Nesne İlişkili Eşlemede kullanılan mantığa göre veritabanındaki her bir tablo entity(varlık) olarak düşünülürse tablolara veri eklenirken, bu durum tabloya doğrudan bir nesne eklendiği anlamına da gelmektedir. Tablo sayısının arttığı durumlarda bu yöntem  verimsiz bir hal alacağı için bu yöntemin otomatik çalışan bir hal alması gereksinimi doğmuştur. Bu nedenle Microsoft tarafından "ADO.NET Varlık Altyapısı" oluşturulmuştur.

Visual Studio İle ADO.NET "Entity" Modeli Oluşturulması

Varlık Altyapısı herhangi bir tipte uygulama açılarak kullanılabilmektedir.
  1. Proje ismine sağ tıklayarak Add New Item(Yeni Öğe Ekle) tıklanır.



  2. Data(Veri) kısmı tıklanarak sağ bölümde görünen "ADO.NET Entity Data Model" seçilir ve Add(Ekle) tıklanır.



  3. Boş Model(Empty Model) oluşturulabilir ya da model tanımlanabilir. Model tanımlamak için Generate from database(Veritabanından Üret) seçilerekNext(İleri) tıklanır.



  4. Yeni bir bağlantı yaratmak için New Connection(Yeni Bağlantı) tıklanır.



  5. Server name(Sunucu adı) kısmına bağlanılmak istenen sunucunun adı yazılır. Sunucu yerelse Use Windows Authentication(Windows Kimlik Denetimini Kullan) seçilir. Sunucu yerel değilse Use SQL Server Authentication(SQL Sunucusunun Kimlik Denetimini Kullan) seçilerek User name (Kullanıcı adı) vePassword(Şifre) girilir. Select or enter a database name(Veritabanı ismi seç ya da ekle) kısmından veritabanı seçilir ya da yeni bir veritabanı ismi girilir. Test Connection(Bağlantıyı Test Et) tıklanır.



  6. Test başarı ile gerçekleşmiştir. OK(TAMAM) tıklanır.



  7. "webConfig" dosyasına yazılması istenen bağlantı ismi verilir. 



  8. Kullanılacak olan Tablolar, Görünümler, Saklı Yordamlar seçilir. Model NameSpace(Model İsim Alanı) kısmına istenilen isim verilir. Finish(Bitti) tıklanır.

Örnek olarak kullanılan Northwind Veritabanı Modelinde Product(Ürün) ve Category(Kategoriler) örnek tabloları seçilmekte ve ".edmx" uzantılı dosya açıldığında şekildeki diyagramlarla karşılaşılmaktadır.



Aşağıdaki kod parçaları kullanılarak test işlemleri yürütülür.

protected void Page_Load(object sender, EventArgs e)
{
   NorthwindEntities nt = new NorthwindEntities();
   foreach (var item in nt.Category)
   {
      Response.Write("Kategori İsmi : " + item.CategoryName + "</br>");
      Response.Write("Ürün Sayısı : "   + item.ProductList.Count 
                                        + "</br></br>");
   }
}

Bu koda ilişkin ekran çıktısı aşağıdaki gibi olmaktadır.

Çıktıya göre ürün sayılarının "0" olması hatalı gibi görünse de bu durum ile SQL tarafından fazla sorgunun çalışması ile meydana gelen Lazy Loading(Ağır yükleme)durumunun önüne geçilmektedir. SQL Profiler(SQL Veri İzleyicisi) performans durumlarını gösteren bir araç olduğu için bahsedilen performans kazancı bu araç ile gözlenebilmektedir.

SQL Veri İzleyicisinde veri izleme işlemi aşağıda belirtilen biçimde yapılmaktadır.

  1. New Trace(Yeni Takip) tıklanır.



  2. Connect(Bağlan) tıklanarak istenilen veri tabanına bağlanılır.



  3. Run(Çalıştır) tıklanır. 

Visual Studio'da derlenmiş, yukarıda kodları verilmiş olan program çalıştırıldığında izleyicilerde aşağıdaki durum gözlenmektedir. SQL Veri İzleyicisinde gözlendiği üzere ürün sayılarının bulunmasına dair bir sorguda bulunulmamış sadece kategoriler sorgulanmıştır. Sorgulama "CategoryID" için SQL:BatchStarting (Toplu Başlangış) komutu ile başlamış ve SQL:BatchCompleted(Toplu Bitiş) komutu ile sonlanmıştır. Sorgulama sadece kategori için yapıldığından ürün sayıları yukarıdaki ekran çıktısında olduğu gibi "0"dır. Henüz yüklenmemiştir.

 

Varlık Altyapısında ağır yükleme işleminin amacı kullanıcının bilinçli bir şekilde yükleme işlemini yapabilmesini sağlamaktadır. Böylelikle yüklenme gerekmeyen kısımlarda performans artırılmış olunur. Gereksiz sorgunun önüne geçilir.
Load(Yükle) metodunu çağırarak ağır yükleme işlemi gerçekleştirilebilmektedir. Aşağıda görülen kod parçası kategori tipine bağlı ürün içeriğinin de çağrılmasını sağlamaktadır. Bu işlem için kod kısmında aşağıdaki değişiklik yapılabilmektedir.

NorthwindEntities nt = new NorthwindEntities();
foreach (var item in nt.Category)
{
   if (!item.ProductList.IsLoaded)
       item.ProductList.Load();
   Response.Write("Kategori İsmi : " + item.CategoryName 
                                     + "</br>");
   Response.Write("Ürün Sayısı : " + item.ProductList.Count 
                                   + "</br></br>");

"RPC:Completed" kısmında "Ürünler" tablosundan "CategoryID"nin eşit olduğu kısımdaki tüm özellikleri çekilmektedir. "@EntityKeyValue1=1"in anlamı "CategoryID"si 1'e eşit olan "Ürünler" tablosunun tüm özelliklerini almayı sağlamaktadır. Bu işlem "@EntityKeyValue1=1" den @EntityKeyValue1=8'e kadar 8 kez yapılmaktadır. ilk olarak 5 ürün "SQL:BatchStarting" komutu ile sorguya çekilmiş daha sonra geriye kalan 3 kategori için de ürün sorgusu gönderilmiştir.




Bir başka yöntem ise "Eager Loading" işlemidir. Include(İçerir) metodu kullanılarak "Eager Loading" işlemi gerçekleştirilebilmektedir. "Include" metodunun içine Navigation Property(Dolaşım Özelliği) ismi yazılır.

NorthwindEntities nt = new NorthwindEntities();
foreach (var item in nt.CategorySet.Include("ProductList"))
{
   Response.Write("Kategori İsmi : " + item.CategoryName + "</br>");
   Response.Write("Ürün Sayısı : " + item.ProductList.Count + "</br></br>");
}

Sql  da görüldüğü gibi kategori tablosunun özellikleri ve alt sorgu ile de "Ürünler" tablosunun özellikleri çekilmektedir. Tek sorguyla işlem tamamlanmıştır.



Bu kodlara bağlı ekran çıktısı aşağıdaki gibidir. Her ikisinde de sonuç aynıdır.



Varlık Altyapısının Linq'e Göre Farkları :

Yukarıda gösterilen uygulamanın aynısı linq ile yapılıp elde edilen sonuçlar karşılaştırılarak aralarındaki farklar görülebilmektedir.

Northwind Veritabanı Modelinden Products(Ürünler) ve Category(Kategori) örnek tabloları alınmaktadır.

  • Varlık Altyapısından farklı olarak Linq'de Navigation Property(Dolaşım Özelliği)'ler bulunmamaktadır. Diyagramda da görüldüğü gibi tablolar birbirine "CategoryID" ile bağlanmıştır.


  • Sağlayıcı olarak Entities(Varlıklar) yerine DataContext(Veri Bağlamı) kullanılmaktadır.

    DataClassesDataContext dataContext = new DataClassesDataContext();
    foreach (var i in dataContext.Categories)
    {
        Response.Write("Kategori İsmi: " + i.CategoryName + "</br>");
        Response.Write("Ürün Sayısı: " + i.Products.Count + "</br></br>");
    }

  • Bu koda ilişkin ekran çıktısı aşağıdaki gibidir. Varlık Altyapısı kullanılarak hazırlanan ikinci uygulamadan elde edilen ekran çıktısının aynısıdır. Yani Varlık Altyapısında sonradan yükleme işlemi Linq'de yoktur.



  • Bu durum Sql Veri İzleyicisi ile aşağıdaki gibi gözlenebilmektedir. RPC:Completed kısmı 8 tane bulunmaktadır. Veri tabanından çekilen veri 8 tane olduğu için 8 kere sorgu gönderilmiştir, bu yüzden 8 tane "RPC:Completed" yer almaktadır. "RPC:Completed" kısmında "Ürünler" tablosundan "CategoryID"nin eşit olduğu kısımdaki tüm özellikleri çekilmektedir. @p0=8 in anlamı "CategoryID"si 8'e eşit olan "Ürünler" tablosunun tüm özelliklerini almayı sağlamaktadır. Bu işlem @p0=1 den @p0=8'e kadar 8 kez yapılmaktadır. 



    Varlık Altyapısında olduğu gibi Linq'de ağır yükleme işlemi bilinçli olarak yapılamaz. Veri tabanına yapılan her bağlantıda yükleme işlemi yapılmaktadır. Bu da performans kaybına neden olmaktadır.