namespace Validation.Tests { using System; using System.Collections.Generic; using Xunit; public class User { public string Name { get; set; } public int Age { get; set; } } public class UserValidator : IValidator { public void Validate(RuleSet r) { r += u => u.Age > 0; // TODO : recognise expression and select "greater than" message pattern r += u => (u.Name != null, when: u.Age >= 1, "You should have a name if you are 1 or older"); r += u => (u.Name == "test", "Name should be equal to test"); } } public class ValidationTest { [Fact] public void PocoValidation() { var factory = new ValidatorFactory(); factory.Register(new UserValidator()); var emptyName = factory.Validate(new User { }); Assert.Equal(emptyName[0], "There is an error"); var nameRequired = factory.Validate(new User { Age = 2 }); Assert.Equal(nameRequired[0], "You should have a name if you are 1 or older"); var wrongName = factory.Validate(new User { Age = 2, Name = "wrong" }); Assert.Equal(wrongName[0], "Name should be equal to test"); } } public class ValidatorFactory { public Dictionary Validators { get; set; } = new Dictionary(); public void Register(IValidator validator) { var ruleSet = new RuleSet(); validator.Validate(ruleSet); Validators.Add(typeof(T), ruleSet); } public List Validate(T poco) { var ruleSet = (RuleSet) Validators[typeof(T)]; var messages = new List(); foreach (var rule in ruleSet.Rules) { bool validate; var message = "There is an error"; var when = true; switch (rule.Type) { case RuleTypes.Simple: (validate) = rule.RuleSimple.Invoke(poco); break; case RuleTypes.WithMessage: (validate, message) = rule.RuleWithMessage.Invoke(poco); break; case RuleTypes.When: (validate, when) = rule.RuleWhen.Invoke(poco); break; case RuleTypes.WhenWithMessage: (validate, when, message) = rule.RuleWhenWithMessage.Invoke(poco); break; default: throw new ArgumentOutOfRangeException(); } if (when && validate == false) messages.Add(message); } return messages; } } public delegate (bool validate, string message) RuleWithMessage(T poco); public delegate (bool validate, bool when) RuleWhen(T poco); public delegate (bool validate, bool when, string message) RuleWhenWithMessage(T poco); public delegate bool RuleSimple(T poco); public enum RuleTypes { Simple, WithMessage, When, WhenWithMessage } public class Rule { public RuleTypes Type { get; set; } public RuleSimple RuleSimple { get; set; } public RuleWithMessage RuleWithMessage { get; set; } public RuleWhen RuleWhen { get; set; } public RuleWhenWithMessage RuleWhenWithMessage { get; set; } } public class RuleSet { public List> Rules { get; set; } = new List> (); public static RuleSet operator +(RuleSet a, RuleWithMessage b) { a.Rules.Add(new Rule { Type = RuleTypes.WithMessage, RuleWithMessage = b.Invoke }); return a; } public static RuleSet operator +(RuleSet a, RuleSimple b) { a.Rules.Add(new Rule { Type = RuleTypes.Simple, RuleSimple = b.Invoke }); return a; } public static RuleSet operator +(RuleSet a, RuleWhen b) { a.Rules.Add(new Rule { Type = RuleTypes.When, RuleWhen = b.Invoke }); return a; } public static RuleSet operator +(RuleSet a, RuleWhenWithMessage b) { a.Rules.Add(new Rule { Type = RuleTypes.WhenWithMessage, RuleWhenWithMessage = b.Invoke }); return a; } } public interface IValidator { } public interface IValidator : IValidator { void Validate(RuleSet r); } }