Created
May 11, 2018 21:06
-
-
Save kiwipiet/dd17c38dea98ed849490429f6a4f444e to your computer and use it in GitHub Desktop.
DataAnnotationsValidator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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