1 Şubat 2015 Pazar

Entity Framework Notları : Part 1 (Modelleme Yöntemleri)

Bu yazımda, Microsoft'un bizimle altı sene önce tanıştırdığı Entity Framework (EF) ile ilgili, şu an üzerinde çalıştığım bir proje çerçevesinde aldığım notları size aktarmaya çalışacağım.

Makale boyunca, birçok konuya yönelik yaptığım araştırmaları göz önünde bulundurarak, bir yazılım oluştururken, EF gibi bir ORM(Object Relational Mapping) Framework ile ilgili dikkat edilmesi gereken konulara değineceğim.


Hangi model oluşturma yöntemi tercih edilmeli ?


Öncelikle, bu sorunun kesin bir cevabı olmadığını belirteyim. Demek istediğim, bu sorunun cevabı biraz da kişisel veya takım olarak size kalmış. EF kullanarak ilerleyebileceğiniz, şu an 3 çeşit yöntem var :


  1. Code-First
  2. Model-First
  3. Database-First
Öncelikle, yeni başlayanlar için her birini kısaca özetleyecek olursak ; 

  1. Code-First : Hardcore programming dediğimiz salt programlama ile model oluşturulan yöntem. POCO sınıfları yazarak modelinizi kendi yazdığınız kodlarla oluşturuyorsunuz. Nasıl mı ?
        class Sınıf
        {
            public int ID { get; set; }
            public string Adı { get; set; }
            public virtual ICollection<Öğrenci> Öğrenciler { get; set; } 
        }

        class Öğrenci
        {
            public int ID { get; set; }
            public string Adı { get; set; }
            public virtual Sınıf Sınıfı { get; set; }
        }

     Gördüğünüz gibi, iki sınıfımız var. Birincisi "Sınıf" sınıfımız, diğeri de "Öğrenci" sınıfımız.  Bu ikisinin ilişkisini nasıl tanımladık peki ?   "Navigation Properties (Navigasyon Özellikleri)" sayesinde. Sınıf ile Öğrenci sınıfları arasında bir one-to-many ilişkisi kurduk. Geri kalan tek şey, EF kurulumunu yapmak. Onun da örneği şöyle ki :

        class DenemeContext : DbContext
        {
            public DbSet<Sınıf> Sınıflar { get; set; }   // Bu DbSet ler EF tarafından veritabanına Tablo olarak işlenirler.
            public DbSet<Öğrenci> Öğrenciler { get; set; }

            public DenemeContext()
            {
             
            }

            protected override void OnModelCreating(DbModelBuilder modelBuilder)
            {
                 //  Bu override methodun önemi büyük. Daha sonra değineceğiz.
                base.OnModelCreating(modelBuilder);
            }
        }

    DbContext'ten miras almış olan DenemeContext sınıfı bizim işlemlerimizi gerçekleştirmeye yarayan sınıfımız. Veri işlemlerini yaparken bu sınıfın kopyasını almış bir değişken üzerinden yapıyoruz.

    DenemeContext con = new DenemeContext();
    con.Sınıflar.FirstOrDefault(); // bize Sınıflar tablosundaki ilk veriyi verir.


  2. Model First : Bu yöntem yeni başlayanlar için ve T-Sql bilmeyenler için ideal bir yöntem. Bu yöntemde, Visual Studio'nun size sağladığı bir arayüz sayesinde tüm modelinizi oluşturabiliyorsunuz.
    Model-First örneği
    Tüm entity(varlık/tablo) lerinizi oluşturduktan sonra, gerisini her zamanki gibi EF halledecek ve eğer yoksa veritabınını oluşturacak ve bağlantıları yapacaktır.
  3. Database(DB) First : Açıkçası Code-First ten sonra en çok tercih edeceğim modelleme yöntemidir. Çünkü bir geliştirici olarak, kontrolün elimde olmasını severim ve Code-First'ten sonra gelen ve kontrolü çoğunlukla elinizde tutabileceğiniz yöntemlerden biri de DB-First'tür.
    DB-First modelleme de adından da anlaşılacağı gibi ilk olarak database(veritabanı) modellemesi yapılır. MSSQL kullandığınızı varsayacak olursak DB-First örneğimiz de şu şekilde olacaktır :

    CREATE TABLE Sınıflar (ID int PRIMARY KEY IDENTITY(1,1), Adı nvarchar(255))

    CREATE TABLE Öğrenciler (ID int PRIMARY KEY IDENTITY(1,1), Adı nvarchar(255), Sınıfı_ID int, CONSTRAINT [FK_Sınıflar_Sınıfı_ID] FOREIGN KEY REFERENCES Sınıflar(ID) )

    Bu yöntemi kullanarak bir ObjectContext(veri işlemleri yapmanızı sağlayan nesne) oluşturmak, yaklaşık olarak model-first ile aynı yollardan geçer. Her ikisinde de, projenizde bir EDMX nesnesi oluşacaktır. Ayrıca veritabanındaki model üzerinde yapacağınız her değişiklik ile modelinizi güncellemeniz gerekir.

Konumuz bu olmadığı için, bu yöntemlerin ayrıntılı olarak nasıl kullanılacağını yazmıyorum. Daha çok, performans iyileştirmeleri, model oluşturma teorileri üzerinde yazacağım.

Avantajlar-Dezavantajlar


Bu yöntemlerin her birinin teorik ve pratik olarak birbirlerine göre avantaj ve dezavantajları var.  Bunun için bir tablo oluşturmak isterdim ancak bu tablonun çok doğru olacağına inanmıyorum. Daha çok size benim naçizane tecrübelerime dayanan görüşlerimden bahsedeceğim.

Öncelikle Model-First'ü aradan çıkaralım : 

Daha önce de söylediğim gibi yeni başlayanlar ve t-sql üzerinde çok fazla bilgisi olmayanlar için ideal bir yöntem. Tamamıyla Visual Studio üzerinde built-in olarak gelen designer sayesinde, sadece bir diagram çizmeniz yeterli.Korkmayın, sırf model-first kullanıyorsunuz diye herhangi bir EF özelliğinden geri kalmıyorsunuz ancak tek sorun birçok şey EF'nin kontrolünde oluyor ve sizin bu konu üzerinde pek fazla müdaheleniz olamıyor.

Peki bu neden kötü birşey ?  Şöyle ki, bir geliştirici her zaman sadece yazılımı oluşturmaktan kaçınarak, o yazılımın performansını, tutarlılığını da göz önünde bulundurmalı. Bu gibi konuları göz önüne alırsak, aslında Code-First veya DB-First ile yapabileceğiniz birçok manüel değişiklikten mahrum kalıyorsunuz. (Aslında kalmıyorsunuz ama karmaşık EDMX dosyalarının model oluştururken kullandığı  T4 Template lerinin içerisinde gerçekten rahatlıkla kaybolabilirsiniz )


DB-First


DB-First model örneği
Eğer SQL seviyorsanız sizin için mükemmel. Kontrolün yine çoğu sizin elinizde. Ancak işinizi ikiye bölmek zorunda kalıyorsunuz. Bir taraftan kod yazmak için IDE'ye döneceksiniz, diğer taraftan modeli yazmak/güncellemek için SQL kodlarına. İkisi arasındaki bağlantıyı çok iyi kurmanız gerekir, özellikle de bir takım olarak çalışıyorsanız. Ayrıca, tam olarak sql kodlarını yazdığınız platforma bağlı kalacaksınız. Bunu MySql, MsSql farklılığı olarak düşünmeyin. Örneğin bir ara başıma gelen, MSSQL 2008 R2 - 2005 farklılığı gibi. 2008 R2 "date" türünü desteklerken 2005 desteklemiyor. Evet, küçük bir sorun ama yine de bir sorun... O yüzden bu metodu kullanacaksanız gerçekten buna emin olmanız gerekiyor...

Code-First

Bu üç methodun arasında Code-First benim favorim. Kontrolün tamamı geliştiricinin elinde, EF'nin karışmamasını istediğiniz herşeyi engeleyebilir, farklı bir yorum katmak istediğiniz yerlerde, bunu gerçekleştirebilirsiniz. Platform spesifik olarak çalışmaz, karmaşa söz konusu değildir (tabi bana göre).

Ancak Code-First'ün kendi dezavantajları yok mu ? Tabi ki var... En önemlisi de, "Model Pipeline" nın sebep olduğu performans sıkıntıları ...

Nedir bu "Model Pipeline" ?
Model Pipeline, kodladığınız modelin EF tarafından her programı çalıştırdığınızda belleğe alınan, ilişki şemasının bir görüntüsüdür diyebiliriz. Yani neyin nereye bağlı olduğunu, neyin ne olduğunu bu pipeline belirler. Pipeline data(veri) tutmaz. Sadece modelin ilişkilerinin şemasıdır.

Bu model view(görüntü) larının oluşması, ilk kez programınızı çalıştırıyorsanız (modelinizin de büyüklüğüne bağlı olarak) gerçekten çok uzun sürebilir. (Benim yazdığım bir yazılımda bu 3 dakika kadar sürmüştü) Dikkatinizi çekmek istediğim bir nokta, programınızı ilk kez çalıştırdınız ve kapattığınızda EF bu viewları herhangi bir yere kaydetmez. Yani her programınızı açışınız aslında EF için ilk açışınızdır ve her seferinde bu viewları oluşturmaya çalışacaktır.

Bunun bir çaresi yok mu ? Olmasaydı, kimse Code-First kullanmıyor olurdur herhalde. Çünkü gerçekten can sıkıcı bir durum....

Ne zaman çıktığını bilmiyorum ama, geçenlerde okuduğuma göre (bulamadım makaleyi) VS2015 üzerinde built-in olarak gelecek, Entity Framework Power Tools ile (veya başka 3. parti eklentiler ile) bu durumdan kurtulmak mümkün.

Bu araç sizin için, compile-time sırasında Code-first view oluşturarak, DbContext'inizin ismiyle birlikte kaydedecektir. Örneğin :

DenemeContext.cs
DenemeContext.Views.cs

Böylelikle, programınız her çalıştığında pipeline ı tekrar tekrar oluşturmaya kalkmayacaktır, siz de zamandan kazanacaksınız. Tabi, burada da ufak bir noktadan bahsedeyim, her model değişikliğinizin ardından bu viewları güncellemeniz gerekir.

Hardcore Programming 

Code-First kullanırken, en sevdiğim noktalardan biri de herşeyin "Hardcore Programming" dediğimiz (benim "Salt Programlama" diye tabir ettiğim) yöntemin kullanılması. Yukarıda ki örneğimizden de görebileceğiniz gibi herşey POCO sınıflar ile halledilebiliyor. Peki bunun neresi güzel ?

Programlama dilinin her tarafından yararlanabiliyorsunuz... İster Interface kullanın, isterseniz bu sınıflara methodlar ekleyin. Herşey serbest. Çünkü EF sadece sınıfa ait olan Property leri görecektir.
Methodlar, fieldlar herhangi bir şekilde EF nin aklını karıştırmayacaktır. Tabi, auto-property leri kullandığımızı varsayarsak...

Code-First ün bu konudaki tek eksiği, "Concrete-Class" dediğimiz salt sınıflardan başka birşey görmüyor olması.

Nedir bu Concrete-Class ? 

Concrete-Class (Sade, salt sınıflar) , bildiğimiz normal sınıflardır. Abstract sınıflar veya interface ler gibi birer kopyası alınamayan sınıflar değillerdir. Örneğin  ::

    class Sınıf
    {
        public int ID { get; set; }
        public string Adı { get; set; }
        public virtual ICollection<Öğrenci> Öğrenciler { get; set; }
    }

bir concrete-class örneği olarak verilebilirken,

abstract class SınıfBase
    {
        public int ID { get; set; }
        public string Adı { get; set; }
    }

veya

interface ISınıf
    {
        int ID { get; set; }
        string Adı { get; set; }
    }

birer concrete-class değillerdir ve bunların birer kopyası alınamaz.

Code-First modelleme yöntemi, bu tarz nesneleri veritabanı oluştururken önemsemez. Örneğin,

    class Sınıf
    {
        public int ID { get; set; }
        public string Adı { get; set; }
        public virtual ICollection<Öğrenci> Öğrenciler { get; set; }
        public IÖğretmen Öğretmen { get; set; }
    }


    interface IÖğretmen
    {
        int ID { get; set; }
        int Adı { get; set; }
        Sınıf Sınıfı { get; set; }
    }


"Sınıf" sınıfımızdaki "Öğretmen" property si, EF tarafından ignore(göz ardı) edilecektir. Çünkü bu property tipi bir concrete-class değildir ve EF bunu veritabanına yansıtmaz.

Bunun neresi kötü peki ? OOP açısından bu birçok noktada önünüze engel koyar. Ancak tabi şundan da bahsedeyim, DB-First veya Model-First yöntemlerinde zaten interface veya abstract düşüncesi hiç yok. Code-First üzerinde en azından interface veya abstract sınıfları birer yol gösterici olarak kullanabilirsiniz. Örneğin :



    interface ISınıf
    {
        int ID { get; set; }
        string Adı { get; set; }
        ICollection<Öğrenci> Öğrenciler { get; set; }
    }

    class Sınıf : ISınıf
    {
        public int ID { get; set; }
        public string Adı { get; set; }
        public virtual ICollection<Öğrenci> Öğrenciler { get; set; }
    }


    class Öğrenci
    {
        public int ID { get; set; }
        public string Adı { get; set; }

        public Sınıf Sınıfı { get; set; }
    }

Her ne kadar, Öğrenci sınıfında "Sınıfı" property sini "Sınıf" türünde kullanıp, "ISınıf" türünde kullanamıyor olsakta, en azından bir interface sayesinde OOP mantığı uygulayabiliriz.
(Bu arada şunu da belirteyim, EF'nin şu an için böyle bir özelliği yok, ama NHibernate'in var. )


Peki neden OOP kullanmak isteyesiniz ? Ya da neden bunu EF 'nin sınıfları üzerinde isteyesiniz ?

Şöyle ki, atıyorum WPF ile çalışıyorsunuz ve sürekli binding(veri bağlama) yapıyorsunuz. Eğer daha önce WPF kullandıysanız, INotifyPropertyChanged interface'inin ne kadar önemli olduğunu biliyorsunuzdur. Tabi bunu bir ViewModel üzerinde de kullanabilirsiniz, ancak direk olarak entity sınıfı üzerinde kullanabileceğinizi bilmek te sizi rahatlatabilir.

Ya da ikinci senaryo, ortak noktası bulunan birkaç sınıfın tümünün bir metodu barındırmasını istiyorsunuz. Bunun için neye ihtiyacınız var ? Bir interface veya abstract class a ...  (Yukarıdaki örnek gibi). Gönül rahatlığıyla kullanabilirsiniz.



Part-1 üzerinde anlatacaklarım bu kadar. Part-2 ye geçtiğimizde biraz da size MVVM biraz da Association(ilişkilendirme) mantıkları hakkında karşılaştığım durumları aktarmaya çalışacağım.

Eğer yanlış bildiğim bir nokta , eklemek istediğiniz bir şeyler veya bir sorunuz varsa lütfen çekinmeden yorumunuzu yapın...