Skip to content

Instantly share code, notes, and snippets.

@kiwipiet
Created May 11, 2018 21:06
Show Gist options
  • Save kiwipiet/dd17c38dea98ed849490429f6a4f444e to your computer and use it in GitHub Desktop.
Save kiwipiet/dd17c38dea98ed849490429f6a4f444e to your computer and use it in GitHub Desktop.
DataAnnotationsValidator
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using JetBrains.Annotations;
namespace ConsoleApp1
{
public static class DataAnnotationsValidator
{
public static bool Validate(this object @object, [NotNull] ICollection<ValidationResult> results)
{
if (results == null) throw new ArgumentNullException(nameof(results));
var context = new ValidationContext(@object, null, null);
var result = Validator.TryValidateObject(
@object, context, results,
true
);
// TODO : Add verbose logging for validation here...
return result;
}
public static bool ValidateRecursive(this object @object, [NotNull] ICollection<ValidationResult> results)
{
if (results == null) throw new ArgumentNullException(nameof(results));
var result = Validate(@object, results);
var properties = @object.GetType().GetProperties()
.Where(prop => Attribute.IsDefined(prop, typeof(ValidateObjectAttribute)));
foreach (var property in properties)
{
var valAttrib =
property.GetCustomAttributes(typeof(ValidateObjectAttribute), true).FirstOrDefault() as
ValidateObjectAttribute;
var value = property.GetValue(@object, null);
if (value == null || valAttrib == null) continue;
var asEnumerable = value as IEnumerable;
if (asEnumerable != null)
{
var items = new List<object>();
foreach (var enumObj in asEnumerable)
{
items.Add(enumObj);
}
foreach (var enumObj in items)
{
result = ValidateRecursive(enumObj, results) && result;
}
if (items.Count < valAttrib.MinOccursOnEnumerable)
{
var errorMessage = valAttrib.ErrorMessage ?? string.Format(
ValidateObjectAttribute.DefaultErrorMessageTemplate, property.Name,
valAttrib.MinOccursOnEnumerable);
results.Add(new ValidationResult(errorMessage, new[] {property.Name}));
result = false;
}
}
else
{
result = ValidateRecursive(value, results) && result;
}
}
// TODO : Add verbose logging for validation here...
return result;
}
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class ValidateObjectAttribute : Attribute
{
public const string DefaultErrorMessageTemplate =
"The field {0} must be an IEnumerable type with a minimum length of '{1}'.";
//marker for object properties that need to be recursively validated
/// <summary>
/// Check IEnumerables for minimum length
/// </summary>
public int MinOccursOnEnumerable { get; set; }
/// <summary>
/// Override the default error message
/// </summary>
public string ErrorMessage { get; set; }
}
internal class Program
{
private static void Main()
{
var sut = new MatchInformationContainer
{
MatchInformation = new MatchInformation(),
MatchInformationList = new MatchInformation[] { }
};
ICollection<ValidationResult> results = new List<ValidationResult>();
sut.ValidateRecursive(results);
Console.WriteLine(results.Count);
}
private class MatchInformationContainer
{
[Required]
//[MinLength(1)]
[ValidateObject(MinOccursOnEnumerable = 1)]
public MatchInformation[] MatchInformationList { get; set; }
[Required] [ValidateObject] public MatchInformation MatchInformation { get; set; }
}
private class MatchInformation
{
[RequiredNotDefault] public int Int { get; set; }
[RequiredNotDefault] public Guid Id { get; set; }
[Required] public string Value { get; set; }
[Required]
[ValidateObject] public MatchInformation2 MatchInformation2 { get; set; } = new MatchInformation2();
}
private class MatchInformation2
{
[RequiredNotDefault] public int Int2 { get; set; }
[RequiredNotDefault] public Guid Id2 { get; set; }
[Required] public string Value2 { get; set; }
[ValidateObject] public MatchInformation3 MatchInformation3 { get; set; } = new MatchInformation3();
}
private class MatchInformation3
{
[RequiredNotDefault] public int Int3 { get; set; }
[RequiredNotDefault] public Guid Id3 { get; set; }
[Required] public string Value3 { get; set; }
}
}
public class RequiredNotDefaultAttribute : RequiredAttribute
{
public override bool IsValid(object value)
{
var isValid = base.IsValid(value);
if (isValid)
if (value.Equals(GetDefault(value.GetType())))
isValid = false;
return isValid;
}
private static object GetDefault(Type type)
{
if (type.IsValueType) return Activator.CreateInstance(type);
return null;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment