Entity Framework alt yapsnn sunduu nemli yaklamlardan birisi de Code-First modelidir. Bu modele gre, gelitiriciler nce snflar basit POCO(Plain Old CLR Objects) tipler eklinde tasarlarlar. Bylece Conceptual(Domain) Model oluturulur. POCO tiplerinin tek bana tasarlanmas elbette yeterli deildir. DbContext trevli bir snfnda, model de kullanlmas dnlen POCO tiplerini ieriyor olmas gerekmektedir. Bu noktada DbSet tipinden zellikler kullanlr.

Sonrasnda ise Entity Framework' n ilgili Context tipi alma zamannda gerekli veritaban retimini icra eder. Peki hi u soru aklnza geldi mi; Bu retim ilemi srasnda tablolar hangi kuralar gre oluur, hangi alan Primary Key kabul edilir, tablolar arasndaki ilikiler(Relations) nasl belirlenir vb. Bu yazmzda bu soruya biraz daha aklk getirmeye alyor olacaz.

Code First yaklamnda, veritaban tarafnn retilmesi aamasnda devreye girmekte olan bir takm kurallar btn bulunmaktadr. Convetion olarak adlandrlan bu kurallar btn aslnda System.Data.Entity.ModelConfiguration.Conventions isim alan altnda yer alan baz tipler yardmyla ifade edilmektedir. Entity Framework 5.0 srm iin ilgili isim alannda(namespace) yer alan tipleri bu adresten inceleyebilirsiniz(http://msdn.microsoft.com/en-us/library/system.data.entity.modelconfiguration.conventions(v=vs.103).aspx)

Burada pek ok Convetion tipi yer almaktadr. lk olarak bu basit Convetion tiplerinden bazlarn kavramsal olarak incelemeye alalm. Bu amala aadaki basit ierie sahip olduumuz bir rnek zerinden ilerleyebiliriz.

efcon_2.png

using System;
using System.Collections.Generic;
using System.Data.Entity;

namespace HowTo_EFCodeFirstConvetions
{
    public class AzonBookShop
        :DbContext
    {
        public DbSet<Book> Books { get; set; }
        public DbSet<Category> Categories { get; set; }

		static AzonBookShop()
        {
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<AzonBookShop>());
        }
    }

    public class Category
    {
        public Guid ID { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Book> Books { get; set; }
    }

    public class Book
    {
        public int BookID { get; set; }
        public string Title { get; set; }
        public decimal ListPrice { get; set; }
        public virtual Category Category { get; set; }
        public int CategoryId { get; set; }
    }
}

"rnek zerinde ilerlerken modeli ska deitirebileceimizden, static yapc metod(Constructor) ierisinde, Initialize srasnda eer model de bir deiiklik olmusa Drop ilemlerinin uygulanmas gerektiini belirtmekteyiz"

imdi bu modele gre retilen veritaban emasn yle bir inceleyelim dilerseniz.

efcon_1.png

Dikkat edilecei zere Categories ve Books isimli iki tablo retilmitir. Her iki tabloda birer Primary Key alan bulunmaktadr. te burada Primary Key Convention kmesi devreye girmektedir. Bu kural setine gre, Guid veya int tipinden olup ad ID veya [SnfAd][Id] olan zellikler, veritaban emasnda birer Identity alan olarak retilecek ve hatta Primary Key olarak iaretleneceklerdir.

Senaryomuza Class seviyesinde baktmzda bir kategorinin altnda birden fazla kitabn yer alabilecei grlmektedir. Bu association'  salamak adna Category snf ierisinde ICollection<Book> tipinden bir zellik konumlandrlmtr. Bunun karl olarak veritaban emasnda grld zere iki tablo arasnda bir relation kurulmutur. Bu Relation, Categories tablosundan Books tablosuna doru one-to-many olacak ekildedir. Burada Relation Convention kurallar devreye girmektedir.

rnekte kastl olarak Book snf ierisidne CategoryId isimli ayr bir zellik tanmlanmtr. Mantksal olarak bu zellik bir kitabn bal olduu Category tipini iaret etmek zere planlanmtr. Ancak Relation Convention kurallarna gre isimlendirme de bir sorun vardr. Category tablosunun Identity eklindeki Primary Key alan ID olarak belirlenmitir. Bu sebepten tablo emasna bakldnda Category_ID isimli bir alann daha eklendii ve iki tablo arasndaki Relation' n bu alan zerinden saland grlmektedir.

efcon_3.png

USE [AzonBookShop]
GO

ALTER TABLE [dbo].[Books]  WITH CHECK ADD  CONSTRAINT [FK_dbo.Books_dbo.Categories_Category_ID] FOREIGN KEY([Category_ID])
REFERENCES [dbo].[Categories] ([ID])
GO

ALTER TABLE [dbo].[Books] CHECK CONSTRAINT [FK_dbo.Books_dbo.Categories_Category_ID]
GO

Relation Script' inde grld zere Books tablosunda yer alan Category_ID ForeignKey olarak belirlenmi ve Books tablosundaki ID alanna balanmtr.

imdi rneimizi biraz daha geniletelim. Aadaki POCO tipini eklediimizi dnelim.

efcon_5.png

public class Book
{
    public int BookID { get; set; }
    public string Title { get; set; }
    public decimal ListPrice { get; set; }
    public virtual Category Category { get; set; }
    //public int CategoryId { get; set; }
    public Detail BookDetail { get; set; }
}

public class Detail
{
    public int PageSize { get; set; }
    public double Weight { get; set; }
    public bool HardCover { get; set; }
    public string Language { get; set; }
}

Book snfna Detail tipinden BookDetail isimli bir zellik eklenmitir. Detail snfnn dikkat ekici zellii ise Primary Key Convention kuralna uygun bir zellik iermiyor olmasdr. Bu durumuda Complex Type Convention kural seti devreye girecektir ve veritaban tarafnda aadaki sonularn olumasna neden olacaktr.

efcon_4.png

Grld zere Detail snfnn zellikler, Book tablosu ierisinde birer alan haline getirilmitir. 

rnekte, makinede ykl olan SQL 2008 sunucusu kullanlmtr. Bu nedenle app.config dosyas ierisinde aadakine benzer bir ConnectionString bilgisi yer almaktadr. name niteliinin deerinin Context snf ad ile ayn olmas nemlidir.

<connectionStrings>
	<add name="AzonBookShop" connectionString="data source=.;database=AzonBookShop;integrated security=SSPI" providerName="System.Data.SqlClient"/> 
</connectionStrings>

Bu noktada aslnda Connection String Convention kural seti devreye girmektedir. Bu kural setinin veritaban emasn olutururken bakt yerlerden birisi config dosyasndaki connectionStrings elementi ieriidir. Eer name zelliinin deeri ile DbContext trevli Context tipinin elenii bulunursa, o elemente ait Connection bilgisi ele alnarak bir ema retimi gerekletirilecektir.

Buraya kadar anlatmaya altmz Convention kural setlerinin daha pek ok zellii bulunmaktadr. Data Annotations ve Fluent API kullanm gibi durumlarda ilgili Convention kural kmelerinin ezilmesi vb mmkndr. stenirse bir Convention kural seti devre d braklabilir de. Bunun iin DbContext zerinden gelen OnModelCreating metodunun ezilmesi ve ierisinde aadakin benzer bir kodun kullanlmas yeterlidir.

using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace HowTo_EFCodeFirstConvetions
{
    public class AzonBookShop
        :DbContext
    {
        public DbSet<Book> Books { get; set; }
        public DbSet<Category> Categories { get; set; }

		static AzonBookShop()
        {
            Database.SetInitializer(new DropCreateDatabaseIfModelChanges<AzonBookShop>());
        }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }
	...

modelBuilder tipi zerinden Conventions zellii ile Remove metoduna eriilmekte ve PluralizingTableNameConvention snf generic parametre olarak belirtilmektedir. rneimizin nceki ksmlarnda veritaban tarafnda retilen tablo adlar mutlaka dikkatinizi ekmitir. oul isim olarak retilmilerdir. Ancak OnModelCreating ierisine bu kural setini kaldrmamz tablo adlarnn snf adlar olarak tanmlanmasn salamtr.

efcon_6.png

Peki Convention kurallar ile ilikili olarak daha baka neler yapabiliriz? Convetion kurallarn manuel olarak ele almann bir ka yolu bulunmaktadr. Bunlardan birisi Lightweight modelidir. Bu modelde nce bir filtreleme ilemi yaplr ve ilem sonucuna gre konfigurasyonun deitirilerek uygulanmas salanr. rnein modelimizde yer alan Category snfnn ieriini aadaki gibi deitirdiimiz dnelim.

    public class Category
    {
        public Guid Signature { get; set; }
        public string Name { get; set; }
        public virtual ICollection<Book> Books { get; set; }
    }

Burada ID isimli zelliin Signature olarak deitirildii grlmektedir. Bu deiiklik nedeniyle pek tabi Primary Key Convention kural seti uygulanamayacaktr. Daha da kts senaryomuz gerei Category ile Book arasnda bir relation tesis edilebilmesi iin gerekli Foreign Key bulunamayacak ve alma zamannda aadaki Exception ile karlalacaktr.

efcon_7.png

te bu noktada Lightweight Convention teknii ile durum zmlenebilir. Bunun iin yine OnModelCreating ierisinde baz ilemler yaplmas gerekmektedir. Aynen aada grld gibi.

"Sz konusu kodda yer alan Properties zellii EF 6.0 Alpha 2 srmnde duyurulmutur. Dolaysyla bundan sonraki kodlar iin gncel PreRelease srmn kurarak devam etmelisiniz. Kurulum iin http://msdn.microsoft.com/en-us/data/ee712906 uradaki adresten yararlanabilirsiniz"

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    modelBuilder
        .Properties()
        .Where(p => p.Name.Contains("Signature"))
        .Configure(p => p.IsKey());
}

modelBuilder zerinden Properties metodu kullanllarak, Entity' deki zellikler arasnda Signature kelimesini ieren bir tane olup olmadna baklmakta ve eer var ise IsKey metodu ars ile bunun bir Identity alan olmas gerektii(bir baka deyile Primary Key Convention kurallarnn uygulanmas gerektii) vurgulanmaktadr. Buna gre alma zaman sonucunda veritaban tarafnda aadaki emann retildii gzlemlenecektir.

efcon_8.png

Convention eitlerinden bir dieri de Model-Based olan versiyondur. Bu teknikte dorudan model ile alma ve ayarlama ansna sahip oluruz. lgili teknii uygulayabilmek iin IEdmConvention, IDbConvention ve IDbMappingConvention arayzlerini(interface) implemente eden snflardan yararlanrz. Aadaki basit rnei gz nne alalm.

efcon_10.png

using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.ModelConfiguration.Conventions;

namespace HowTo_EFCodeFirstConvetions
{
    public class StringLengthConversion
        : IEdmConvention<EdmProperty>
    {
        public void Apply(EdmProperty edmDataModelItem, EdmModel model)
        {
            if (edmDataModelItem.PrimitiveType.PrimitiveTypeKind == PrimitiveTypeKind.String)
                edmDataModelItem.MaxLength = 200;
        }
    }
}

IEdmConvention<EdmProperty> interface' ini implemente eden StringLengthConversion snf Apply metodunu uygulamaktadr. Bu metodun ierisinde, edmDataModelItem isimli deikenin String tipi olup olmadna baklmakta ve eer yleyse Max Length deeri 200 karakter ile snrlandrlmaktadr. Bu ilem pek tabi Model ierisinde yer alan ne kadar String tipte e var ise geerli olacktr. Tabi sz konusu snfn devreye girebilmesi iin yine OnModelCreating ierisine mdahale edilmelidir. Aadaki kod parasnda grld gibi.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    modelBuilder
        .Properties()
        .Where(p => p.Name.Contains("Signature"))
        .Configure(p => p.IsKey());
    modelBuilder.Conventions.AddBefore<StringLengthAttributeConvention>(new StringLengthConversion());
}

Bu ilem sonucunda veritaban emasnda aadaki sonularn olutuu gzlemlenecektir.

efcon_9.png

Bir baka Convention modeli ise Configuration tabanl olandr. Bu teknikten IConfigurationConvention arayznn(intercace) implenente edildii bir snfn devreye girerek Convention kurallarna mdahale etmesi sz konusudur. Aadaki rnek snf gz nne alalm.

efcon_11.png

using System;
using System.Data.Entity.ModelConfiguration.Configuration.Properties.Primitive;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Reflection;

namespace HowTo_EFCodeFirstConvetions
{
    public class StringColumnTypeConvention
        :IConfigurationConvention<PropertyInfo,StringPropertyConfiguration>
    {
        public void Apply(PropertyInfo memberInfo
            , Func<StringPropertyConfiguration> configuration)
        {
            if (configuration().ColumnType == null)
            {
                configuration().ColumnType = "nvarchar";
                configuration().IsNullable = false;
                configuration().MaxLength = 50;
            }
        }
    }
}

Pek tabi sz konusu snfn String tipteki zelliklerin, tablo alan haline getirilmesi srasnda devreye girebilmesi iin OnModelCreating metoduna bir mdahale de bulunmak gerekmektedir. Aadaki kod parasnda bu durumu grebilirsiniz.

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    modelBuilder
        .Properties()
        .Where(p => p.Name.Contains("Signature"))
        .Configure(p => p.IsKey());

    modelBuilder.Conventions.Add<StringColumnTypeConvention>();
}

Bu ilem sonrasnda veri taban emasnn aadaki gibi retildii grlecektir.

efcon_12.png

Dikkat edilecei zere String zelliklerin karl olarak nvarchar tipinde olan, null deer ieremeyen ve maksimum 50 karakter uzunluunda ierik tutabilen alanlar retilmitir.

