Archive

Archive for January, 2014

Data Annotations for Complex class structures

January 30, 2014 Leave a comment

Data Annotations in C# Classes

When you use data classes (also known as entity classes or POCO) in your application, you can apply attributes to the class or members that specify validation rules, specify how the data is displayed, and set relationships between classes. The “System.ComponentModel.DataAnnotations” namespace contains the classes that are used as data attributes. By applying these attributes on the data class or member, you centralize the data definition and do not have to re-apply the same rules in multiple places.

The “System.ComponentModel.DataAnnotations” namespace contains the following attributes which are used to enforce validation rules for data applied to the class or member:

Validation Attribute

Description

CustomValidationAttribute Uses a custom method for validation.
DataTypeAttribute Specifies a particular type of data, such as e-mail address or phone number.
EnumDataTypeAttribute Ensures that the value exists in an enumeration.
RangeAttribute Designates minimum and maximum constraints.
RegularExpressionAttribute Uses a regular expression to determine valid values.
RequiredAttribute Specifies that a value must be provided.
StringLengthAttribute Designates maximum and minimum number of characters.
ValidationAttribute Serves as base class for validation attributes.

All validation attributes derive from the “ValidationAttribute” class. The logic to determine if a value is valid is implemented in the overridden “IsValid” method. The “Validate method calls the “IsValid” method and throws a “ValidationException” if the value is not valid.

Below code snippet is used to validate the plain entity classes e.g. POCO entities.

private static List<ValidationResult> CallMyFunction(object oObject)

{

var context = new ValidationContext(oObject, serviceProvider: null, items: null);

var results = new List<ValidationResult>();

Validator.TryValidateObject(oObject, context, results, false);

return results;

}

To create customized validation checks, you can either create a class that derives from the “ValidationAttribute” class or create a method that performs the validation check and reference that method when applying the “CustomValidationAttribute” to the data member. When you create a class that derives from “ValidationAttribute”, override the “IsValid” method to provide the logic for your customized validation check.

Note: You cannot automatically validate complex child objects when validating a parent object and include the results in the populated “ICollection<ValidationResult>”.  Data Annotations validator does not validate complex child properties. To do so, slap this attribute on your property (probably a nested view model). For that you will need to make your own validator attribute that validates the child properties.

public class ComplexClassValidation: ValidationAttribute

{

protected override ValidationResult IsValid(object value)

{

var isValid = true;

var result = ValidationResult.Success;

var nestedValidationProperties = value.GetType().GetProperties()

.Where(p => IsDefined(p, typeof(ValidationAttribute)))

.OrderBy(p => p.Name);

foreach (var property in nestedValidationProperties)

{

var validators = GetCustomAttributes(property, typeof(ValidationAttribute)) as ValidationAttribute[];

if (validators == null || validators.Length == 0) continue;

foreach (var validator in validators)

{

var propertyValue = property.GetValue(value, null);

result = validator.GetValidationResult(propertyValue, new ValidationContext(value, null, null));

if (result == ValidationResult.Success) continue;

isValid = false;

break;

}

if (!isValid)

{

break;

}

}

return result;

}

}

There are situations when you need to obtain the list of nested properties within an object using Reflection. For such situations you can use below code snippet.

public static object GetNestedPropertyValue(object customObject, string fullyQualifiedPropertyName)

{

if (!String.IsNullOrEmpty(fullyQualifiedPropertyName))

foreach (string propertyName in fullyQualifiedPropertyName.Split(‘.’))

{

PropertyInfo propertyInfo = customObject.GetType().GetProperty(propertyName);

customObject = propertyInfo.GetValue(customObject, null);

}

if (customObject == null)

throw new Exception(“Property value could not be determined”);

return customObject;

}

Above method takes in the fully qualified property name and the custom object (to which the property belongs) as input parameters and returns all the properties of that object.

For more details about Data Annotations, click here.

Reference:

  1. Validator Class
  2. Validation Result
  3. Validation Context
  4. http://stackoverflow.com/questions/17944211/how-can-i-iterate-through-nested-classes-and-pass-the-object-to-my-function