Skip to content

Commit

Permalink
Add common validation
Browse files Browse the repository at this point in the history
  • Loading branch information
davevans committed Jul 28, 2014
1 parent 351caef commit ffd7630
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/Church/Church.Common/Church.Common.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@
<Compile Include="IOC\TinyIoC.cs" />
<Compile Include="Mapping\Mapper.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Validation\ValidateChildAttribute.cs" />
<Compile Include="Validation\ValidationResults.cs" />
<Compile Include="Validation\Validator.cs" />
<Compile Include="Web\TinyIocWebApiResolver.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
23 changes: 23 additions & 0 deletions src/Church/Church.Common/Validation/ValidateChildAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;

namespace Church.Common.Validation
{
[AttributeUsage(AttributeTargets.Property)]
public class ValidateChildAttribute : Attribute
{
public bool Required { get; set; }
public string ErrorMessage { get; set; }

public ValidateChildAttribute(bool required, string errorMessage)
{
Required = required;
ErrorMessage = errorMessage;
}

public ValidateChildAttribute()
: this(true, "Is required.")
{

}
}
}
17 changes: 17 additions & 0 deletions src/Church/Church.Common/Validation/ValidationResults.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace Church.Common.Validation
{
public class ValidationResults
{
public bool IsValid { get; set; }
public IList<ValidationResult> Errors { get; set; }

public IEnumerable<string> ErrorStrings
{
get { return Errors != null ? Errors.Select(x => x.ErrorMessage).ToList() : Enumerable.Empty<string>(); }
}
}
}
120 changes: 120 additions & 0 deletions src/Church/Church.Common/Validation/Validator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

namespace Church.Common.Validation
{
public static class Validator
{

private class ValidationProperty
{
public bool IsClassValidator;
public PropertyInfo PropertyInfo;
public ValidateChildAttribute Child;
public ValidationAttribute[] Validations;
}

private static readonly ConcurrentDictionary<Type, List<ValidationProperty>> Cache = new ConcurrentDictionary<Type, List<ValidationProperty>>();
public static bool Validate(object toValidate, IList<ValidationResult> results)
{
var ctx = new ValidationContext(toValidate);
return Validate(toValidate, ctx, results);
}

public static ValidationResults Validate(object o)
{
var validationResults = new ValidationResults();
var list = validationResults.Errors = new List<ValidationResult>();
validationResults.IsValid = Validate(o, list);
return validationResults;
}

private static ValidationAttribute[] GetPropertyValidator(PropertyInfo property)
{
return (ValidationAttribute[])property.GetCustomAttributes(typeof(ValidationAttribute), true);
}

public static bool Validate(object instance, ValidationContext validationContext, IList<ValidationResult> validationResults)
{
bool isError = false;
List<ValidationProperty> validationProperties = null;

var type = instance.GetType();

if (!Cache.TryGetValue(type, out validationProperties))
{
//not found in cache
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

validationProperties = properties.Select(p => new ValidationProperty
{
PropertyInfo = p,
Child = (ValidateChildAttribute)p.GetCustomAttributes(typeof(ValidateChildAttribute), true).FirstOrDefault(),
Validations = GetPropertyValidator(p)
}).ToList();


var classLevelValidators = (ValidationAttribute[])type.GetCustomAttributes(typeof (ValidationAttribute), true);
if (classLevelValidators.Any())
{
validationProperties.Add(new ValidationProperty
{
IsClassValidator = true,
PropertyInfo = null,
Child = null,
Validations = classLevelValidators
});
}

Cache.TryAdd(type, validationProperties.Any() ? validationProperties : null);
}

foreach (var validationProperty in validationProperties)
{
object objectValue = validationProperty.IsClassValidator
? instance
: validationProperty.PropertyInfo.GetValue(instance, null);

if (validationProperty.Validations != null)
{
foreach (var validation in validationProperty.Validations)
{
var result = validation.GetValidationResult(objectValue, validationContext);
if (result != null)
{
isError = true;
validationResults.Add(result);
break;
}
}
}

if (validationProperty.Child != null)
{
if (objectValue != null && validationProperty.Child.Required)
{
isError = true;
validationResults.Add(new ValidationResult(validationProperty.Child.ErrorMessage));
}else if (objectValue != null)
{

validationContext = new ValidationContext(objectValue, null, null);
var innerIsValid = Validate(objectValue, validationContext, validationResults);
if (!innerIsValid)
{
isError = true;
}
}
}
}

return !isError;
}


}
}

0 comments on commit ffd7630

Please sign in to comment.