Skip to content

Instantly share code, notes, and snippets.

@fabiomaulo
Created November 22, 2019 20:17
Show Gist options
  • Save fabiomaulo/e3cf9ee41926ba5c43ba98d116142b95 to your computer and use it in GitHub Desktop.
Save fabiomaulo/e3cf9ee41926ba5c43ba98d116142b95 to your computer and use it in GitHub Desktop.
Data Annotations Validator
public interface IViewModelValidator
{
bool IsValid(object model);
}
public class DataAnnotationsValidator : IViewModelValidator
{
private class ReferenceEqualityComparer : IEqualityComparer<object>
{
public new bool Equals(object x, object y)
{
return ReferenceEquals(x, y);
}
public int GetHashCode(object obj)
{
return RuntimeHelpers.GetHashCode(obj);
}
}
public bool TryValidate(object model, out ICollection<ValidationResult> results)
{
var vrs = new List<ValidationResult>();
results = vrs;
return TryValidate(model, new HashSet<object>(new ReferenceEqualityComparer()), vrs);
}
public bool IsValid(object model)
{
return TryValidate(model, new HashSet<object>(new ReferenceEqualityComparer()), null);
}
private bool TryValidate(object model, ISet<object> validatedObjects, List<ValidationResult> results)
{
if (validatedObjects.Contains(model))
{
return true;
}
validatedObjects.Add(model);
var shortcircuit = results == null;
var context = new ValidationContext(model, serviceProvider: null, items: null);
bool result = Validator.TryValidateObject(model, context, results, validateAllProperties: true);
var properties = TypeDescriptor.GetProperties(model).Cast<PropertyDescriptor>().ToList();
foreach (var property in properties.Where(p => p.PropertyType != typeof(string) && !p.PropertyType.IsValueType))
{
var value = property.GetValue(model);
if (value == null)
{
continue;
}
if (value is IEnumerable asEnumerable)
{
foreach (var enumObj in asEnumerable)
{
if (enumObj != null)
{
var nestedResults = shortcircuit ? null : new List<ValidationResult>();
if (!TryValidate(enumObj, validatedObjects, nestedResults))
{
if (shortcircuit)
{
return false;
}
result = false;
results.AddRange(nestedResults.Select(nr => new ValidationResult(nr.ErrorMessage, nr.MemberNames.Select(x => property.Name + '.' + x))));
};
}
}
}
else
{
var nestedResults = new List<ValidationResult>();
if (!TryValidate(value, validatedObjects, nestedResults))
{
if (shortcircuit)
{
return false;
}
result = false;
results.AddRange(nestedResults.Select(nr => new ValidationResult(nr.ErrorMessage, nr.MemberNames.Select(x => property.Name + '.' + x))));
};
}
}
return result;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment