23 Şubat 2011 Çarşamba

Telerik MVC Extensions

Yazılım geliştiricilerin en çok ihtiyaç duyduğu bileşenlerden biride yetenekli ön yüz (özellikle grid) bileşenleridir. MVC uygulamalarınızda kullanabileceğiniz open source bir çözüm var: Telerik MVC Extensions

Telerik MVC 3 için mevcut kontrol kütüphanesine extension yazıdı ve bunu open source olarak paylaştı. TelerikMVCExtensions projesini NuGet ile kendi MVC projenizde kullanabilirsiniz. (NuGet ile bir çok open source uygulamayı kendi projenize ekleyebilirsiniz. NuGet’i nasıl kurucağınızı ve kullanacağınızı buradan öğrenebilirsiniz.)

Telerik MVC Extensions paket referansını kendi projenize ekledikten sonra gelişmiş grid özelliklerini inceleyelim. Ajax çağrıları ile grid üzerinde CRUD işlemleri nasıl yapılır?

Öncelikle gridi sayfamıza ekleyelim.

[cs]

@using  Telerik.Web.Mvc.UI  
@(  Html.Telerik().Grid()         
 .Name("Grid" )         
 .DataKeys(keys =>         
 {             
  keys.Add(p => p.LinkCategoryId);         
 })         
 .ToolBar(commands =>             
  commands.Insert().ButtonType(GridButtonType .ImageAndText)                 
   .ImageHtmlAttributes(new  { style = "margin-left:0"  }))         
 .DataBinding(dataBinding =>{             
  dataBinding.Ajax()                 
   .Select("_SelectAjaxEditing","LinkCategory")                 
   .Insert("_InsertAjaxEditing","LinkCategory" )                 
   .Update("_SaveAjaxEditing" , "LinkCategory" )                 
   .Delete("_DeleteAjaxEditing" , "LinkCategory" );         
 })         
 .Columns(columns =>{             
  columns.Bound(p => p.Title).Width(210);             
  columns.Bound(p => p.Url).Width(210);             
  columns.Bound(p => p.Description).Width(130);             
  columns.Command(commands =>{                 
   commands.Edit().ButtonType(GridButtonType .ImageAndText);                 
   commands.Delete().ButtonType(GridButtonType .ImageAndText);             
  }).Width(180).Title("Commands" );         
 })         
 .Editable(editing => editing.Mode(GridEditMode .PopUp))         
 .Pageable()         
 .Scrollable()         
 .Sortable())

Yukarıdaki kod bloğunda bir Telerik MVC gridin kullanıldığını görüyoruz. Satır satır inceleyerek kodun ne yaptığına bakalım

  • Gridin öncelikle bir veri tipi ile birlikte oluşturuyoruz:  @Html.Telerik().Grid<LinkCategory>() burada giridin LinkCategory veri yapısı ile birlikte çalışaçağını bildiriyoruz.
  • Daha sonra veri yapımızda ki anahtar alanı tanımlıyoruz: .DataKey( keys => keys.Add(..))
  • Toolbar üzerine Insert Command ekliyoruz:  .Toolbar( commands=> commands.Insert()…)
  • Daha sonrada gridin veri bağlama işlemlerini nasıl yapacağını yani grid ile Controller sınıf arasında ki bağlantıyı belirtiyoruz: .DataBinding(dataBinding.Ajax().Select(…).Insert(…)…. ) Databinding mekanizması olarak Ajax() kullanılacak ve her bir CRUD işlemi için çağırılacak Controller ve Action belirtiliyor: .Select(“SelectAjaxEditing”, “LinkCategory”)
  • Tüm kolonların nasıl gözükmesi gerektiğini söylüyoruz: .Columns( columns=> columns.Bound(…)) (standart gridden gelen DataAnnotation desteği devam ediyor)
  • Command butonları ekliyoruz: columns.Command(….)
  • Gridi edit moduna ceviriyoruz: .Editable(…)
  • Ekstra özellikleri ekliyoruz: .Pageable().Scrollable().Sortable()

Gridin çalışması için gerekli MVC Controller sınıfınada göz atalım:

[cs]

using  Telerik.Web.Mvc; 
namespace  RoC.Modules.Admin.Controllers{     
 public  class  LinkCategoryController:Controller{         
  [Import]private  AdminContext  _adminContext;         
  
  [GridAction] 
  public ActionResult CategoriesManage()        
  {             
   return  View ();         
  }         
  
  [GridAction ]         
  public  ActionResult  _SelectAjaxEditing()         
  {             
   return  View (new  GridModel (_adminContext.Links));         
  }         
  
  [HttpPost ]         
  [GridAction ]         
  public  ActionResult  _SaveAjaxEditing(int  id)         
  {             
   .....             
   return View(new GridModel(_adminContext.LinkCategories));         
  }         

  [HttpPost ]         
  [GridAction ]         
  public  ActionResult  _InsertAjaxEditing () {             
   ....             
   return View(new GridModel(_adminContext.LinkCategories));         
  }         

  [HttpPost ]         
  [GridAction ]         
  public  ActionResult  _DeleteAjaxEditing (int  id)  {            
   ...             
   return  View (new  GridModel (_adminContext .LinkCategories ));         
  }     
 } 
}

Yukarıdaki kod bloğunuda inceleyelim:

  • CategoriesManage()  fonskiyonu sadece view dönmektedir. Çünkü veriler gridin select fonksiyonu ile doldurulacaktır.
  • Alt çizgi ile başlattığımız tüm grid fonksiyonları [GridAction] niteliğine sahiptir. Grid fonksiyonları kendilerine ait veri tabanı işlemini yaptıkdan sonra veriyi GridModel tipinde döndürmektedir.

Tüm bu kütüphaneyi kullanmak için master.page/_layout.cshtml içinde eklemeniz gereken kodlar vardır.

[cs]

@(Html.Telerik().StyleSheetRegistrar()   
 .DefaultGroup(group => 
  group.Add("telerik.common.min.css" )   
   .Add( Html.GetCurrentTheme()+"/"+"telerik.min.css")   
   .Add("telerik.rtl.min.css" )                             
   .Combined(true )                             
   .Compress(true )) )  
@Html.Telerik().ScriptRegistrar()

Yukarıdaki kod bloğu ile Telerik kontrollerinin kullandığı Css ve javascript dosyaları sayfaya eklenmektedir.

Böylelikle tüm CRUD işlemlerini destekleyen bir grid ekranı elde etmiş olduk.

Sonuç

Telerik firmasının standart MVC kontrolerini genişleterek oluşturduğu TelereikMVCExtension paketi MVC uygulamaları ile gerçeken güzel ve yetenekli arayüzler oluşturmanıza yardımcı olaracaktır. Bileşenleri buradan indirebilir. Daha ayrıntılı örneklere buradan erişebilirsiniz.

16 Şubat 2011 Çarşamba

Entity Framework Code Fisrt Veri Doğrulama

Bir önce ki yazıda Veri Açıklaması/DataAnnotations isim uzayını inceledik. Veri açıklamaları ile veri moleliniz üzerinde property seviyesinde doğrulama mantığımızı çalıştırabiliyoruz. Veri modeli sınıflarınıza .NET 4 tarafından tamamen desteklenen [Range], [RegulerExpress] gibi doğrulama nitelikleri ekleyerek veri tabanı işlemleri yapmadan önce gerçekten kolay bir şekilde doğrulama kontrolüne zorlayabiliyoruz.

Property seviyesindeki doğrulama çok işe yaramasına rağmen tüm veri doğrulama ihtiyaclarmızı karşılamamaktadır. Veri modeli sınıfımızdaki bir çok property değerinin birlikte kontrol edileceği sınıf seviyesinde kontrol gerekmektedir.

System.ComponentModel.DataAnnotation isin uzayında bulunan IValidateableObject arayüzü ile entity nesnelerinize sınıf seviyesinde doğrulama ekleyebilirsiniz.

Bu serinin daha önce ki yazılarnda ki gibi bir adres defteri uygulaması üzerinden sınıf seviyesinde doğrulama nasıl yapılır inceleyelim. Aşağıda ki örnekte kişi tanımlarını taşıyan Person entity sınıfımız yer almaktadır. Yapmak isteğimiz adres defterinde ki her erkeğin şirket bilgisinin her bayanında doğum günü bilgisinin boş bırakılmamasını sağlamak. Bu kuralı işletebilmek için aynı anda en az iki property değirini kontrol etmemiz gerekmektedir.

[cs]

public  partial  class  Person  : IValidatableObject 
 {
     public  int  PersonId  { get ; set ; }
     public  string  FullName  { get ; set ; }
     public  string  Company  { get ; set ; }
     public  DateTime ? BirthDay  { get ; set ; }
     public  bool  IsFemale  { get ; set ; }
     public  virtual  List  Adresses  { get ; set ; }
     public  IEnumerableValidate(ValidationContext validationContext)
     {
         if  (IsFemale  && BirthDay  ==  null )
             yield  return   new  
                 ValidationResult (
                     "Bayanların doğum günü boş bırakılamaz" ,
                     new [] { "BirthDay"  });
         if  (!IsFemale  && string .IsNullOrEmpty (Company ))
             yield  return  new  
                 ValidationResult (
                     "Erkeklerin şirket bilgisi boş bırakılamaz" , 
                     new [] { "Company"  });
     }
 }

Yukarıda ki kod bloğunda erkeğin adres bilgisi ve bayanın doğum günü bilgisi kontrol edilmektedir. IValidateableObject.Validate() metodu bir çok property değerini kontrol etmekte ve bir çok doğrulama hatası dönebilmektedir. Doğrulama hatalarının birinci parametresi hata mesajıdır. İkinci parametre ise hataya sebeb olan property isimleri dizisidir.

Otomatik Doğrulamaya Zorlamak

Entity Framework Code First IValidateableObject arayüzünü uygulayan entity neslerini kayıt ederken Validate metodunu otomatik olarak çağırmaktadır. Validate metodunu çağırmak için her hangi bir kod yazmanıza gerek yoktur. DbContext.SaveChanges() metodunu çağırdığınızda IValidateableObject.Validate EF Code First tarafından çağırılacak ve eğer hata oluşursa tüm transaction otomatik olarak geri alınacaktır.

[cs]

var  person = new  Person 
         {
             FullName  = "Ali Veli" ,
             IsFemale  = false ,
             BirthDay  = new  DateTime (2000, 1, 1)
         };
 var  dbContext = new  AdressBook ();
 dbContext.Persons .Add (person);
 // Ali Veli kişi kaydını IvalidateableObject.Validate 
// metodunda ki kurala uymadığı için 
// aşağıda ki satırda exception throw edecektir
 dbContext.SaveChanges ();

Hatanın throw edilmesi her zaman istenmeyen bir durumdur. Çünkü throw edilen hatanın arayüze yakın bir yerde catch edilmesi gerekmektedir. Bu sebebden entity nesnenizi kontrol etmek ve eğer veri iş kurallarına uygunsa veri tabanı işlemleri yapmak isteriz. SaveChanges() metodunu çağırmadan önce GetValidationErrors() metodu ile doğrulama hatalarını throw edilmeden elde ederiz.

[cs]

var  person = new  Person 
 {
     FullName  = "Ali Veli" ,
     IsFemale  = false ,
     BirthDay  = new  DateTime (2000, 1, 1)
 };
 var  dbContext = new  AdressBook ();
 dbContext.Persons .Add (person);
 // tüm hataları listele 
 var  errors = dbContext.GetValidationErrors ();
 if (errors.Count () > 0 )
     // hataları ekranda göster 
     errors.ToList ().ForEach (
         c=>c.ValidationErrors .ToList ().ForEach (
             l=>ModelState .AddModelError (l.PropertyName ,l.ErrorMessage )));  
 else 
     dbContext.SaveChanges ();

Arayüz Entegrasyonu

Bir önce ki yazıda DataAnnotation isim uzayının .NET 4 ile getiştirilen tüm platformlar tarafından desteklendiğine değinmiştik. MVC 3 ile entity sınıfını hazırladığımız adres defteri uygulamamızı geliştirelim ve sınıf seviyesinde doğrulama işleminin önyüzde nasıl bir etki oluşturduğunu görelim. Öncelikle bir PersonController sınıfına göz atalım.

[cs]

public  ActionResult  Create ()
 {
     return  View ();
 }
 [HttpPost ]
 public  ActionResult  Create (Person  person)
 {
     if  (ModelState .IsValid )
     {
         var  adressBook = new  AdressBook ();
         adressBook.Persons .Add (person);
         adressBook.SaveChanges ();
         return  RedirectToAction ("Index" );
     }
     return  View (person);
 }

Yukarıda standart MVC Controller sınıfı kodlaması bulunmaktadır. İlk Create motodu ile arayüz hazırlanmakta ve ikinci Create(person) metodu ile kullanıcının girdiği Person verisi veri tabanına kayıd edilmektedir. Create(person ) metodu öncelikle gelen verinin doğrulamaları geçmesini eklemektedir. Eğer doğrulamaları geçti ise veri tabanına kayıt etmektedir. Veri doğrulama için ekstra hiçbir kodlama bulunmamaktadır. Veri doğrulaması DataAnnotation isim uzayına duyarlı MVC tarafından otomatik olarak yapılmaktadır. Birde önyüzü inceleyelim. Aşağıdaki Create() metodunun döndüğü ön yüz görülmektedir. Burada da hiçbir fazladan kodlama yok.


  @model   RoC.CodeFirst.Models.Person
  @{
     ViewBag.Title = "ViewPage1" ;
  }
 <h2> ViewPage1</h2>
 <script  src="  @Url.Content("~/Scripts/jquery.validate.min.js")"  type="text/javascript"></script>
 <script  src="  @Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"  type="text/javascript"></script>
  @   using  (Html.BeginForm()) {
      @  Html.ValidationSummary(true )
     <fieldset>
         <legend> Person</legend>
         <div  class="editor-label">
              @  Html.LabelFor(model => model.FullName)
         </div>
         <div  class="editor-field">
              @  Html.EditorFor(model => model.FullName)
              @  Html.ValidationMessageFor(model => model.FullName)
         </div>
         <div  class="editor-label">
              @  Html.LabelFor(model => model.Company)
         </div>
         <div  class="editor-field">
              @  Html.EditorFor(model => model.Company)
              @  Html.ValidationMessageFor(model => model.Company)
         </div>
         <div  class="editor-label">
              @  Html.LabelFor(model => model.BirthDay)
         </div>
         <div  class="editor-field">
              @  Html.EditorFor(model => model.BirthDay)
              @  Html.ValidationMessageFor(model => model.BirthDay)
         </div>
         <p>
             <input  type="submit"  value="Create"  />
         </p>
     </fieldset>
 }
 <div>
      @  Html.ActionLink("Back to List" , "Index" )
 </div>

Yukarıda ki Controller ve View entegrasyonu ile fazladan hiçbir doğrulama mantığı kodlamadık. Doğrulama mantığını tamamen veri modeli üzerine taşıdık. İş mantığının yazıldığı Controller sınıfı sadece ModelState.IsValid() fonksiyonu ile doğrulama sonucunu kontrol etti.

Eğer IValidateableObject.Validate() metodundan bir doğrulama hatası dönerse ModelState.IsValid otomatik false değerini alacaktır. Controller’ın ModelState.IsValid false değerini alınca View geri döndürdüğünü görülmektedir. Arayüz tekrar gösterilirken IValidateable.Validation() metodunun sonucları ValidationMessageFor metodu ile arayüzde gösterilecektir Sonuç olarak hatalı bir işlemi kayıd etmeye çalıştığımızda aşağıdaki ekran görüntüsüne ulaşırız.

Veri açıklaması/DataAnnotation ve sınıf seviyesinden doğrulama/IValidateableObject ile Entity Framework Code First bir çok uygulamanın doğrulama ihtiyaçlarına cevap vermektedir.

Sonuç

Entity Framework Code First ile birlikte doğrulama ve iş kurallarının veri modeli üzerinde kalması sağlanmıştır. Hedeflenen kendini tekrar etmeyen kodlama (DRY – Dont Repeat Yourself) yapabilmektir. Tüm iş kodları bir yerde bulunsun, basit okunabilir kodlamalar olsun ve asla iş kodları farklı yerlerde tekrar edilmesin istenmektedir. En çok tekrar edilen kodlama ise doğrulama kodlamasıdır. Hem istemci hem sunucu tarafında aynı veri kontrolleri yapılmaktadır. EF Code First (ve MVC 3) ile sunulan veri açıklaması ve sınıf seviyesi doğrulama çözümleri ile kendini tekrar eden doğrulama kodlamalarına çözüm getirmektedir.

14 Şubat 2011 Pazartesi

Veri Açıklama - Data Annotations

“System.ComponentModel.DataAnnotations” isim uzayı NET 3.5 SP1 ile gelen Asp.Net Dynamic Data projesi ile hayatımıza girdi. Hayatımıza girdiği andan itibaren geliştirilen tüm platformlar bu isim uzayına duyarlı geliştirildi. Asp.Net Dynamic Data, Wcf Ria, Silverlight ve şimdide MVC 3 bu isim uzayı ile entegre calışmaktadır. Peki veri açıklması/Data Annotations isim uzayı nedir nasıl kullanılır?

Veri Modelini Açıklama

“System.ComponentModel.DataAnnotations” isim uzayı [Required] [Display] gibi semantik nitelikleri kullanarak UI üzerinde kullanılabilecek veri açıklamaları sunmaktadır. DataAnnotations isim uzayı ile birlikte sunulan çözüm veri modeli açıklamaları ile her uygulamada yapılan doğrulama, lokalizasyon gibi ön yüz işlemlerinin otomatik hale getirilmesidir.

Nasıl Çalışır ?

ASP.Net web form, Silverlight (wcf ria) ve Mvc platformları DataAnnotations isim uzayını desteklemektedir. Ben örneklemek için MVC 3 ve Entity Framework Code First uygulaması kullanacağım. Burada anlatılan Entity Framework Code First adress defteri uygulamamıza veri modeli açıklamaları ekleyerek nasıl çalıştığını inceliyelim.

Aşağıda adres defteri uygulaması için gerekli veri modeli bulunmaktadır.

[cs]

public  partial  class  Person 
 {
     public int  PersonId  { get ; set ; }
     public string  FullName  { get ; set ; }
     public string  Company  { get ; set ; }
     public DateTime  BirthDay  { get ; set ; }
     public virtual ListAdresses{ get; set;}
 }

Yapmak istediğimiz kişi verisi girilirken Name değeri boş bırakılmasın veya yirmi karakterden uzun değerler girilmesin. MVC uygulamamızda bu istediğimizi aşağıdaki şekilde yapabiliriz.

[cs]

[HttpPost ]
 public  ActionResult  Create (Person  person)
 {
     if (string .IsNullOrEmpty (person.FullName ) )
         ModelState.AddModelError("FullName","Ad değeri boş bırakılamaz" );
     if  (person.FullName  != null  && person.FullName .Length  > 20)
         ModelState.AddModelError("FullName","20 karakterden uzun ad girilemez");
     if  (ModelState .IsValid )
     {
         var  adressBook = new  AdressBook ();
         adressBook.Persons .Add (person);
         adressBook.SaveChanges ();
         return  RedirectToAction ("Index" );
     }
     return  View (person);
 }

Bu kod bloğunda girilen verinin veri yapımıza uygunluğunu test ettik. Fakat sadece bir tek property için bile bir çok if yazmak zorundayız. Karmaşık veri yapalarındaki doğrulama işlemi ciddi bir sorun oluşturacaktır. Bir diğer doğrulama seceneğimizde veri modeli açıklaması oluşturmaktır.

[cs]

[MetadataType (typeof (PersonMetadata ))]
 public  partial  class  Person 
 {
 public  class  PersonMetadata 
 {
     [Key , 
     Display (AutoGenerateField  = false )]
     public  int  PersonId  { get ; set ; }
     [Required (ErrorMessage  = "Ad değeri boş bırakılamaz" ),
     Display (Name  = "Ad" ),
     StringLength (20,ErrorMessage  = "20 karakterden uzun ad girilemez" )]
     public  string  FullName  { get ; set ; }
     [Display (Name  = "Şirket" ),
     StringLength(20,ErrorMessage="20 karakterden uzun şirket ismi girilemez")]
     public  string  Company  { get ; set ; }
     [Display (Name  = "Doğum Tarihi" ), 
     Required (ErrorMessage  = "Doğum tarihi boş bırakılamaz" ),
     DataType (DataType . Date  )]
     public  DateTime  BirthDay  { get ; set ; }
 }
 }

Kişileri saklayan bu sınıfa veri modeli açıklamaları ekledik. Doğrudan sınıflarda bulunan public property’lerede açıklama niteliği ekleyebilirsiniz. Ben daha okunabilir olması için Metadata sınıfı ile birlikte veri modeli açıklama niteliklerini kullandım.Yukardaki kod bloğunda Name özelliğinin ekranda “Ad” olarak görüleceğini değerinin boş bırakılamıyacağını ve yirmi karakterden uzun veri girilemeyeceğiniz söylemektedir. Veri doğrulamalasını belirtilen açıklamalara göre MVC bizim için yapacağından Create metodumuz aşağıdaki şekilde olacaktır.

[cs]

[HttpPost ]
 public  ActionResult  Create (Person  person)
 {
     if  (ModelState .IsValid )
     {
         var  adressBook = new  AdressBook ();
         adressBook.Persons .Add (person);
         adressBook.SaveChanges ();
         return  RedirectToAction ("Index" );
     }
     return  View (person);
 }

Yukarıda ki kod bloğunda hiç bir veri doğrulaması yapılmıyor. Çünkü ModelBinder sınıfı verdiğimiz veri açıklama nitelikleri ile veri doğrulamasını yapmakta ve sonuçları ModelState verisine yüklemektedir. Sunucu tarafında yapılan kontroller gibi MVC istemci taraftada veriyi doğrulamaktadır. Şimdi MVC projemizde kişi ekleme sayfası oluşturalım.

  @   using  (Html.BeginForm()) {
  @  Html.ValidationSummary(true )
 <fieldset>
     <legend> Person</legend>
     <div  class="editor-label">
          @  Html.LabelFor(model => model.FullName)
     </div>
     <div  class="editor-field">
          @  Html.EditorFor(model => model.FullName)
          @  Html.ValidationMessageFor(model => model.FullName)
     </div>
     <div  class="editor-label">
          @  Html.LabelFor(model => model.Company)
     </div>
     <div  class="editor-field">
          @  Html.EditorFor(model => model.Company)
          @  Html.ValidationMessageFor(model => model.Company)
     </div>
     <div  class="editor-label">
          @  Html.LabelFor(model => model.BirthDay)
     </div>
     <div  class="editor-field">
          @  Html.EditorFor(model => model.BirthDay)
          @  Html.ValidationMessageFor(model => model.BirthDay)
     </div>
     <p>
         <input  type="submit"  value="Create"  />
     </p>
 </fieldset>
 }

Yukarıda ki kodda standart bir MVC Razor sayfası görülmektedir ve tüm kodu MVC oluşturdu. LabelFor, EditorFor ve ValidationFor metotları bizim veri modeli açıklalarımızı dikkate alacak ve istediğimiz önyüzü oluşturacaktır.

Görüldüğü üzere MVC veri modeli açıklamaları dikkate aldı ve gerekli şekilde önyüzü ve istemci tarafı doğrulama metotlarını oluşturdu. İstemci tarafında ki javascript fonksiyonlarının ve sunucu tarafında veri kontrollerinin otomatik olarak oluştuğuna özellikle dikkat ediniz. Biz ekstra hiç bir kodlamada bulunmadık.

Daha Fazlası

Sistem içinde bir çok genişleme noktası mevcuttur. Kendi doğrulama/validator sınıflarınızı yazabilirsiniz. Data tiplerine özel önyüz kontrolleri oluşturablirsiniz. MVC örneğimizde veriyi önyüz ve sunucu kodları arasında taşıyan binder sınıfı mevcuttur. Kendinize ait DataAnnotations niteliklerinize özel ModelBinder örneği oluşturabilirsiniz.

Sonuç

Veri modelini doğrulama yaklaşımı aslında bir ilgileri ayırma/(Speration of concers) yöntemidir. En bilinen ilgileri ayırma yöntemi Html ve css kodlarının ayrılmasıdır. DataAnnotations ile veri modeli ve veri modelini ilgilendiren veri doğrulama, görüntüleme gibi özellikler veri modeli sınıfı üzerinde turuluyor. Veri ilgilendirmeyen önyüz kodları ayrı bir yerde tutuluyor. Sunucu tarafında ki iş mantığı kodları ayrı bir alanda tutuluyor.

Veri modeli açıklamasının tüm platformlarda aynı şekilde kodlanması ve kolay anlaşılabilirlik ile birlikte sistemin bir çok genişleme noktası bulunması sistemi kullanılabilir olmasını sağlamaktadır. Ciddi bir kodlama yüküne çözüm getirmesi tüm platformlarda desteklenmesi DataAnnotations isim uzayının hayatımızda çok fazla yer alacağını göstermektedir.

7 Şubat 2011 Pazartesi

Entity Framework Code First- Veri Tabanı Eşleştirme

Code First kod merkezli geliştirme ortamı sağlamaktadır. Her hangi bir tool veya XML eşleştirme dosyasına gerek kalmaksınız geliştirme yapabilirsiniz. Veri modeli nesnelerinizi her hangi bir sınıftan türetmeden basit POCO sınıfları olarak yazabilirsiniz. Konfigurasyon yerine isimlendirme özelliklerini kullanarak POCO nesnelerinizi veri tabanı nesneleriniz ile eşleştirebilirsiniz. Serinin ilk yazısında EF Code First bu özelliklerin nasıl kullanıldığını inceledik.

Varsayılan entity-veri tabanı eşleşmesi nasıl değiştirilir? EF Code First ile varsayılan olarak gelen entity ve veri tabanı nesneleri arasında ki eşleştirme nasıl değiştirilir?

Seri boyunca takip ettiğimiz adres defteri uygulamamıza devam edip yukarıda ki sorularmızın cevabını arıyalım. Standart .NET tipleri olan POCO nesnelerimiz hatırlayalım.

[cs]

public  partial  class  Person  
 {
     public  int  PersonId  { get ; set ; }
     public  string  FullName  { get ; set ; }
     public  string  Company  { get ; set ; }
     public  DateTime ? BirthDay  { get ; set ; }
     public  bool  IsFemale  { get ; set ; }
     public  virtual  List  Adresses  { get ; set ; }
 }
 public  partial  class  Adress 
 {
     public  int  AdressId  { get ; set ; }
     public  string  AdressType  { get ; set ; }
     public  string  FullAdress  { get ; set ; }
     public  int  PersonId  { get ; set ; }
     public  virtual  Person  Person  { get ; set ; }
 }

Bu POCO nesnelerimiz ile veri tabanı eşleşmesini sağlayan DbContext sınıfmızıda hatırlayalım. AddressBook DbContext sınıfından türetilmişti ve Persons ve Adresses property’lerine sahipti.

[cs]

public  class  AdressBook  : DbContext 
 {
     public  DbSet  Persons  { get ; set ; }
     public  DbSet  Adresses  { get ; set ; }
 }


Veri tabanı eşleşmeleri için Code First yaklaşımını kullandık. Bunun manası AdressBook sınıfında yer alan Persons ve Addresses bizim veri tabanımızda aynı isimde ki tablolarla eşleşecekdir. Her bir Persons ve Adresses sınıflarıdandaki her bir property’de Persons ve Adresses tablolarında ki kolonlar ile eşleşecekdir.

Ekstra her hangibir konfigurasyon veya kodlama gerekmeden veri tabanı tablolarımız oluşmaktadır. Veri tabanımızda oluşan veri yapımız aşağıdaki gibidir.

Örnek uygulamada SQL CE kullandım. Yukarıdaki veri yapısında görüldüğü gibi Entity Framework Code First varsayılan olarak entity nesnemize birebir benzeyen bir tablo yapısı oluştudu.

Veri Tabanı Eşleştirme

Entity Framework Code First ile gelen veri tabanı eşleştirmesini DbContext.OnModelCreating metodunu override ederek değiştirebilirsiniz.

[cs]

public  class  AdressBook  : DbContext 
 {
     public  DbSet  Persons  { get ; set ; }
     public  DbSet  Adresses  { get ; set ; }
     protected  override  void  OnModelCreating ( ModelBuilder  modelBuilder)
     {
         // entity - tablo eşleşmesini değiştir 
     }
 }

OnModelCreating(modelBuilder) metodu veri tabanınız ilk defa oluşturulurken çağırılacakdır. OnModelCreating metotu ile veri tabanı nesnelerinizin nasıl oluşturulması gerektiğini söyleyebilirsiniz. Adım adım olabilecek senaryoları inceleyelim.

Tablo İsmini Değiştirme

Nasıl Persons tablosunun ismini aşağıdaki gibi tblPersons yaparız?

Persons tablosunu tlbPersons yapmak için gerekli olan OnModelCreating metodu içinde Entity sınıfmıza ToTable(name) metodu ile özelleştirilmiş bir tablo adı vermektir.

[cs]

public  class  AdressBook  : DbContext 
 {
     public  DbSet  Persons  { get ; set ; }
     public  DbSet  Adresses  { get ; set ; }
     protected  override  void  OnModelCreating ( ModelBuilder  modelBuilder)
     {
         modelBuilder.Entity ().ToTable ("tblPersons" );
     }
 }

Yukarıda ki kod bloğu tablo ismi vermek için kullanılan sezgisel/fluent kuralları devre dışı bırakır ve OnModelCreating metotu ile belirtilen yeni kuralları uygulayarak tablo isimlendirmesini yapılır.

Kolon İsimlerini Değiştirme

Nasıl aşağıdakii gibi tüm kolonlara "col" ön eki ekleyebiliriz?

Tüm kolonlarımıza “col” ön eki eklemek için OnModelCreating metodu içinde entity nesnemiz için yeni bir eşleştirme/mapping eklememiz gerekmektedir.

[cs]

public  class  AdressBook  : DbContext 
 {
 public  DbSet  Persons  { get ; set ; }
 public  DbSet  Adresses  { get ; set ; }
 protected  override  void  OnModelCreating ( ModelBuilder  modelBuilder)
 {
     modelBuilder.Entity ().ToTable ("tblPersons" );
     modelBuilder.Entity ().Property (c => c.PersonId ).HasColumnName ("colId" );
     modelBuilder.Entity ().Property (c => c.FullName ).HasColumnName ("colFullName" );
     modelBuilder.Entity ().Property (c => c.Company ).HasColumnName ("colCompany" );
     modelBuilder.Entity ().Property (c => c.BirthDay ).HasColumnName ("colBirthDay" );
     modelBuilder.Entity ().Property (c => c.IsFemale ).HasColumnName ("colIsFemale" );
     modelBuilder.Entity ().ToTable ("tblAdresses" );
     modelBuilder.Entity ().Property (c => c.AdressId ).HasColumnName ("colId" );
     modelBuilder.Entity ().Property (c => c.FullAdress ).HasColumnName ("colFullAdress" );
     modelBuilder.Entity ().Property (c => c.PersonId ).HasColumnName ("colRefId" );
 }
 }

Yukarıdaki kod bloğunda mevcut kurallar yerine OnModelCreating(modelBuilder) içindeki kuralların işletilmesi sağlanmıştır . Böylelikle varsayılan eşleştirme kuralları override edilmektedir. Code First ile Ansi SQL kolon yapısında desteklenen tüm özellikleri yukarıdaki gibi kullanabilirsiniz. Daha ayrıntılı özellikleri buradan inceleyebilirsiniz.

Bir Çok Entity Bir Table

En sık karşılaşılan seneryolardan biriside uygulama içinde ki entity nesnemiz ile veri tabanında ki tablo yapılarımızın bire bir örtüşmemesidir. Normalizasyon kuralları gereği aynı tabloda yer alması gereken veriler, nesne güdümlü yazılım mimarisi içinde farklı sınıflarda yer alması gerekebilmektedir. Aşağıdaki veri yapısında Person tablosuna göze atalım.

Burada IsWorkWith, IsDrunkWith, MustTakeGift bilgisi gibi alanlar Person tablosunda yer alması istiyoruz. Çünkü bu veriler Person verisine niteleyen verilerdir. Fakat bu bilgi özelleşmiş bir bilgi olduğu için farklı bir entity içinde taşınması daha doğrudur.

[cs]

public  partial   class  Familiarity 
 {
     public  bool  IsWorkWith  { get ; set ; }
     public  bool  IsDrunkWith  { get ; set ; }
     public  bool  MustTakeGift  { get ; set ; }
 }
 public  partial  class  Person  
 {
     public  int  PersonId  { get ; set ; }
     public  string  FullName  { get ; set ; }
     public  string  Company  { get ; set ; }
     public  DateTime ? BirthDay  { get ; set ; }
     public  bool  IsFemale  { get ; set ; }
     public  Familiarity  Familiarity  { get ; set ; }
     public  virtual  List  Adresses  { get ; set ; }
 }

Yukarıda ki entity yapısı ve veri yapısını birbirine uyumlu hale getirmemiz gerekmektedir. İki yapıyı uyumlu hale getirmek için OnModelCreating() metodu içinde Person entity sınıfımızda yer alan Contact property’sinin bir başka entity sınıfı olmadığını Contact sınıfının bir ComplexType olduğunu belirtmemiz gerekmektedir.

[cs]

protected  override  void  OnModelCreating ( ModelBuilder  modelBuilder)
 {
     modelBuilder.ComplexType ();
     modelBuilder.ComplexType ()
  .Property (c => c.MustTakeGift ).HasColumnName ("colMustTakeGift" );
     modelBuilder.ComplexType ()
  .Property (c => c.IsDrunkWith ).HasColumnName ("colIsDrunkWith" );
     modelBuilder.ComplexType ()
  .Property (c => c.IsWorkWith ).HasColumnName ("colIsWorkWith" );
 }

Böylelikle Contact sınıfımız bir entity sınıfı olarak değilde yer aldığı entity nesnesinin alt sınıfı olarak işlem görecektir.

Sonuç

Entity Framework Code First veri erişimini kod merkezli güzel bir yöntem ile çözmektedir. Veri erişimi mekanizmasında yer alan değiştirme noktalarından müdahale ederek veri erişim yapısını kendinize göre özelleştirebilirsiniz. Bu yazıda OnModelCreating(modelBuilder) değiştirme noktasına müdahale ederek veri tabanı yapısını değiştilmesini inceledik. Tüm işlemlerin kodlama ile basitce yapılabilmesi fazladan konfigürasyon gereketirmemesi, doğrulama özelliklerinin önyüz uygulamalar tarafından desteklenemesi gibi özellikler Entity Framework ile uygulama yazmayı kolaylaştırmaktadır.