Makale Özeti

Verinin kendi kendini test etme yaklaşımı Validation Application Block ile iyice güçlendi. Artık Entity’ler daha zeki ve sakladıkları verinin doğruluğundan Entity nesnesi sorumlu. Validation Application Block veriyi Entity üzerinde saklayan programlar için mükemmel bir yaklaşım sunuyor. Fakat Typed DataRow üzerinde bir Application Block yok. Peki benim gibi üşengeç bir .Net yazılımcısı iseniz Entity nesneleri ile ilişkisel verileri saklamak zor geliyorsa, tüm verileri Typed DataSet içinde taşımak ve işlemek size daha kolay geliyorsa ne yapmalısınız?

Makale

Kendi Kendini Doğrulayan Typed DataRow


Verinin kendi kendini test etme yaklaşımı Validation Application Block ile iyice güçlendi. Artık Entity’ler daha zeki ve sakladıkları verinin doğruluğundan Entity nesnesi sorumlu. Validation Application Block veriyi Entity üzerinde saklayan programlar için mükemmel bir yaklaşım sunuyor. Fakat Typed DataRow üzerinde bir Application Block yok. Peki benim gibi üşengeç bir .Net yazılımcısı iseniz Entity nesneleri ile ilişkisel verileri saklamak zor geliyorsa, tüm verileri Typed DataSet içinde taşımak ve işlemek size daha kolay geliyorsa ne yapmalısınız?


Seçenek kalmadı kullandığınız Typed DataSet’leri kendi kendini test edebilir şekle getirmeliyiz. Ne gerekli bize:

  1. Atomik test işlemlerini yapan Attribute sınıflar.

  2. Bu test işlemlerini işletecek Controller sınıfı


Tabi tüm bu sınıfların veriden bağımsız olması yani Generic olması gerekmektedir. Burada ki kısıtlama Validation Application Block her bir Entity Field’a attribute vererek doğrulama yapılmakta fakat Typed DataRow üzerinden ki Field’lara Attribute kalıcı olarak eklenemez. Onun için bizim CheckAtiibute’lerimiz direk Typed DataRow nesnelerine eklenmelidir.


Doğrulama iki şekilde olur:

  1. Check: Veri kendi başına doğru mu sorusudur. Mesela email alanı gecerli mi? Para alanına negatif değer girildi mi?

  2. Validation: Veri iş kurallarına uygun mu sorusudur. Yani veri diğer veriler ile birlikte doğru mu?


 

Tüm işi gerçekleştiren Controller sınıfıdır. Check hatalarını girildiği anda (OnFly) yakalamak için DataSource almaktadır. DataSource üzerinden veri değiştiği zaman BaseCheck sınıfından türetilen Check kuralları çalıştırılmakta ve karşılaşılan hatalar ColumnError olarak atanmaktadır.

Önce takip edilecek Row’a Attributeler atanmalı: 

 

 

[NotNullCheck("Column1",UserFriendlyName="Birinci alan")]

[MinimumCheck("Column2",12, UserFriendlyMessage = "İkinci alandaki veri 12 den k‡k olamaz")]

[UniqueCheck("Column3", UserFriendlyMessage = "İkinci alandaki veri tüm tabloda unique olmalı")]

partial class DataTable1Row

{

 

}


 

Veri değişimi takibi:


#region FollowTable

protected virtual void FollowDataSource(DataTable table)

{

table.ColumnChanging += new DataColumnChangeEventHandler(table_ColumnChanging);

}

protected virtual void table_ColumnChanging(object sender, DataColumnChangeEventArgs e)

{

// veri değişti check kurallarını işlet

 if( e.Row.RowState != DataRowState.Deleted)

e.Row.SetColumnError(e.Column, CheckRow(e.Column, e.Row, e.ProposedValue));

}

#endregion


Değişen veriyi test et


#region check

/// <summary>

/// kolon verisi değişti kontrolü

/// </summary>

/// <param name="dataColumn">değişen kolon</param>

/// <param name="dataRow">kullanılan row</param>

/// <param name="columnNewValue">yeni değer</param>

/// <returns>hata iceriyorsa hata mesajı. hata yoksa boş string</returns>

protected virtual string CheckRow(System.Data.DataColumn dataColumn, System.Data.DataRow dataRow, object columnNewValue)

{

dataRow.EndEdit();

string result = string.Empty;

BaseCheck[] attrs = (BaseCheck[])dataRow.GetType().GetCustomAttributes(typeof(BaseCheck), true);

foreach (BaseCheck attr in attrs)

{

if (attr.ColumnName == dataColumn.ColumnName)

{

result += attr.IsValid(dataRow, columnNewValue);

}

}

return result;

}

#endregion


Check işleminin çalışma zamanı görüntüsü:


IsValid çağrısı ile önce Check kontrolleri çalıştırılır eğer tüm checkler doğru ise kontrol edilen Row HasSelfValidation attribute’ne sahip mi kontrolü yapılır. Eğer Row HasSelfValidation attribute’ne sahip ise SelfValidation attribute’ne sahip fonksiyonlar aranır ve bu fonksiyonlar çağırılır.


#region validate

/// <summary>

/// Veri tabanına kayıt etmeden önce kayıtın doğruluğunu kontrol et

/// hata bulunur ise hatayı throw et

/// </summary>

/// <param name="row">kontrol edilecek kayıt</param>

public virtual bool IsValid(DataRow row)

{

row.EndEdit();

_errorMessage = string.Empty;

if (row.RowState != DataRowState.Deleted && row.RowState != DataRowState.Detached)

{

 foreach (DataColumn col in row.Table.Columns)

_errorMessage += CheckRow(col, row, row[col]);

                         

 //tüm childe row'ları valid mi

foreach (System.Data.DataRelation relation in row.Table.ChildRelations)

foreach (System.Data.DataRow childeRow in row.GetChildRows(relation))

foreach (System.Data.DataColumn col in childeRow.Table.Columns)

_errorMessage += CheckRow(col, childeRow, childeRow[col]);

}

// validation

 if (_errorMessage.Length == 0 && row.RowState != DataRowState.Deleted && row.RowState != DataRowState.Detached)

{

HasSelfValidation[] attrs = (HasSelfValidation[])row.GetType().GetCustomAttributes(typeof(HasSelfValidation), false);

if (attrs != null && attrs.Length > 0)

{

foreach (MethodInfo method in row.GetType().GetMethods())

{

SelfValidation[] validation = (SelfValidation[])method.GetCustomAttributes(typeof(SelfValidation), false);

if (validation != null && validation.Length > 0)

{

object result = method.Invoke(row, new object[] { });

 if (!string.IsNullOrEmpty((string)result))

{

 this._errorMessage += (string)result;

}

}

}

}

}

return string.IsNullOrEmpty(_errorMessage);

}

#endregion



Validation kontrollerinin çalışma zamanı görüntüsü:




Emre Coşkun

http://emrecoskun.blogspot.com/

23 / 3 / 2007


Proje Demosu