Last active
February 22, 2024 14:31
-
-
Save olivier5741/1609e8dd7dd92f153c5a5992940160b1 to your computer and use it in GitHub Desktop.
An alternative library API to FluentValidation
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
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<User> | |
{ | |
public void Validate(RuleSet<User> 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<Type, object> Validators { get; set; } = new Dictionary<Type, object>(); | |
public void Register<T>(IValidator<T> validator) | |
{ | |
var ruleSet = new RuleSet<T>(); | |
validator.Validate(ruleSet); | |
Validators.Add(typeof(T), ruleSet); | |
} | |
public List<string> Validate<T>(T poco) | |
{ | |
var ruleSet = (RuleSet<T>) Validators[typeof(T)]; | |
var messages = new List<string>(); | |
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<in T>(T poco); | |
public delegate (bool validate, bool when) RuleWhen<in T>(T poco); | |
public delegate (bool validate, bool when, string message) RuleWhenWithMessage<in T>(T poco); | |
public delegate bool RuleSimple<in T>(T poco); | |
public enum RuleTypes { Simple, WithMessage, When, WhenWithMessage } | |
public class Rule<T> | |
{ | |
public RuleTypes Type { get; set; } | |
public RuleSimple<T> RuleSimple { get; set; } | |
public RuleWithMessage<T> RuleWithMessage { get; set; } | |
public RuleWhen<T> RuleWhen { get; set; } | |
public RuleWhenWithMessage<T> RuleWhenWithMessage { get; set; } | |
} | |
public class RuleSet<T> | |
{ | |
public List<Rule<T>> Rules { get; set; } = new List<Rule<T>> (); | |
public static RuleSet<T> operator +(RuleSet<T> a, RuleWithMessage<T> b) | |
{ | |
a.Rules.Add(new Rule<T> | |
{ | |
Type = RuleTypes.WithMessage, | |
RuleWithMessage = b.Invoke | |
}); | |
return a; | |
} | |
public static RuleSet<T> operator +(RuleSet<T> a, RuleSimple<T> b) | |
{ | |
a.Rules.Add(new Rule<T> | |
{ | |
Type = RuleTypes.Simple, | |
RuleSimple = b.Invoke | |
}); | |
return a; | |
} | |
public static RuleSet<T> operator +(RuleSet<T> a, RuleWhen<T> b) | |
{ | |
a.Rules.Add(new Rule<T> | |
{ | |
Type = RuleTypes.When, | |
RuleWhen = b.Invoke | |
}); | |
return a; | |
} | |
public static RuleSet<T> operator +(RuleSet<T> a, RuleWhenWithMessage<T> b) | |
{ | |
a.Rules.Add(new Rule<T> | |
{ | |
Type = RuleTypes.WhenWithMessage, | |
RuleWhenWithMessage = b.Invoke | |
}); | |
return a; | |
} | |
} | |
public interface IValidator | |
{ | |
} | |
public interface IValidator<T> : IValidator | |
{ | |
void Validate(RuleSet<T> r); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Looks like an interesting concept. I'm going to try this :-)