Skip to content

Instantly share code, notes, and snippets.

@olivier5741
Last active February 22, 2024 14:31
Show Gist options
  • Save olivier5741/1609e8dd7dd92f153c5a5992940160b1 to your computer and use it in GitHub Desktop.
Save olivier5741/1609e8dd7dd92f153c5a5992940160b1 to your computer and use it in GitHub Desktop.
An alternative library API to FluentValidation
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);
}
}
@gjkaal
Copy link

gjkaal commented Jun 27, 2023

Looks like an interesting concept. I'm going to try this :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment