Использование атрибутов для нормализации и валидации бизнес-сущностей - Базовый класс для атрибутов валидации

ОГЛАВЛЕНИЕ



А теперь напишем аналогичный базовый класс для атрибутов валидации.

/// <summary>

/// Базовый класс-атрибут для создания атрибутов валидации

/// </summary>

[AttributeUsage(AttributeTargets.Property)]
public abstract class ValidationBaseAttribute : Attribute
{

int m_validationOrder = 0;
/// <summary>

/// Порядок проведения валидации

/// </summary>

public virtual int ValidationOrder
{
get {return m_validationOrder;}
}

WarningLevel m_level = WarningLevel.Warning;
/// <summary>

/// Уровень предупреждения

/// </summary>

public virtual WarningLevel Level
{
get {return m_level;}
set {m_level = value;}
}

/// <summary>

/// Базовый атрибут валидации

/// </summary>

/// <param name="validationOrder">Порядковый номер в процессе валидации</param>

public ValidationBaseAttribute(int validationOrder)
{
m_validationOrder = validationOrder;
}

/// <summary>

/// Произвести валидацию

/// </summary>

/// <param name="value">значение для проверки</param>

/// <returns>Строку с предупреждением, либо пустую строку если всё в порядке</returns>

public virtual string Validate(object value)
{
return "#ValidationBase - Validate method not overrided!";
}
}
также напишем сортировщик (по аналогии с нормализацией) и несколько атрибутов валидации (их код можно найти в прилагаемом архиве)
  • ValidationEmptyAttribute (Атрибут проверки свойства на пустоту (null, DBNull, string.empty, (int)0))

  • ValidationStringLengthAttribute (Атрибут проверки свойства на длину строки (может применяться несколько раз))

  • ValidationIntValueAttribute (Атрибут проверки свойства на значение целочисленного типа (может применяться несколько раз))

  • ValidationCollectionEmptyAttribute (Атрибут проверки свойства на пустоту коллекции (списка))

  • ValidationDateYearRangeAttribute (Атрибут проверки свойства на нахождение года даты в указанных пределах (может применяться несколько раз))

Напишем достаточно несложный код проверки. Он получает список всех свойств объекта, для каждого свойства запрашивается список атрибутов, дочерних для ValidationBaseAttribute. Затем вызывается виртуальный (переопределенный в дочерних классах) метод Validate который возвращает пустую строку, если предупреждений не было или строку сообщения в противном случае. Полученную строку сообщения мы добавляем в словарь (Hashtable)
/// <summary>

/// Проверить объект на соответствие установленным ограничениям

/// </summary>

/// <returns>Словарь найденных несоответствий</returns>

public IDictionary Validate()
{
IDictionary result = new Hashtable();

// Получить атрибуты уровня свойств.

// Получить все свойства данного класса и поместить их в массив

PropertyInfo[] pInfo = this.GetType().GetProperties();

// атрибуты всех свойств класса

for (int j=0; j<pInfo.Length; j++)
{
Attribute[] atts = Attribute.GetCustomAttributes(pInfo[j], typeof(ValidationBaseAttribute));
if (atts.Length == 0)
continue;

Array.Sort(atts, new ValidationComparer());
string _fieldName = pInfo[j].Name;
Attribute dna = Attribute.GetCustomAttribute(pInfo[j], typeof(DisplayNameAttribute));
if (dna != null)
_fieldName = ((DisplayNameAttribute)dna).Name;

for (int k=0; k<atts.Length; k++)
{
ValidationBaseAttribute att = (ValidationBaseAttribute)atts[k];
if (att != null)
{
string vr = att.Validate(pInfo[j].GetValue(this, null));
if (vr != "")
{
result.Add(_fieldName +": "+ vr, att.Level);
//если ошибка - прекратить проверку данного поля

if (att.Level == WarningLevel.Error)
break;
}
}
}
}

//additional :(
return result;
}
В строчке //additional можно добавить вызовы собственных, комплексных функций проверки введенных данных, определенных в этом же классе. Словарь возвращенных предупреждений мы выводим в диалоговом окне в виде списка, чтобы пользователь мог с ними ознакомиться (или в данном случае в консоль).

и результаты выполнения следующего кода
/// <summary>

/// The main entry point for the application.

/// </summary>

[STAThread]
static void Main(string[] args)
{
Person p = new Person(-1);
p.ShowNames();
Console.ReadLine();

p.Name = " Иванов Иван Иванович ";
Console.WriteLine("'{0}'", p.Name);
p.Normalize();
Console.WriteLine("'{0}'", p.Name);
Console.ReadLine();

<b>p.Validate();</b>
Console.ReadLine();
}
Итак, несмотря на некоторые недостатки, данный подход имеет право на существование.