Seçenek kalmadı kullandığınız Typed DataSet’leri kendi kendini test edebilir şekle getirmeliyiz. Ne gerekli bize:
- Atomik test işlemlerini yapan Attribute sınıflar.
- Bu test işlemlerini işletecek Controller sınıfı
Doğrulama iki şekilde olur:
- Check: Veri kendi başına doğru mu sorusudur. Mesela email alanı gecerli mi? Para alanına negatif değer girildi mi?
- 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ı:
[cs]
[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: [cs]
#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:[cs]
#region check
///
/// kolon verisi değişti kontrolü
///
/// değişen kolon
/// kullanılan row
/// yeni değer
/// hata iceriyorsa hata mesajı. hata yoksa boş string
protected virtual string CheckRow(DataColumn dataColumn, 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.
[cs]
#region validate
///
/// Veri tabanına kayıt etmeden önce kayıtın doğruluğunu kontrol et
/// hata bulunur ise hatayı throw et
///
/// kontrol edilecek kayıt
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 (DataRelation relation in row.Table.ChildRelations)
foreach (DataRow childeRow in row.GetChildRows(relation))
foreach (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ü: