Makale Özeti

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.

Makale

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.

 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 <Adress > Adresses  { get ; set ; }
     public  IEnumerable<ValidationResult>Validate(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.

 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.

 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.

 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.

Emre Coşkun

Emre Coskun http://www.emrecoskun.net/

Demo