Skip to content

Instantly share code, notes, and snippets.

@mainettis
Created April 17, 2020 16:33
Show Gist options
  • Save mainettis/f1c3b7d79ef5de8b464c3efe689be075 to your computer and use it in GitHub Desktop.
Save mainettis/f1c3b7d79ef5de8b464c3efe689be075 to your computer and use it in GitHub Desktop.
Simple Generic Object Validation as based on solution found here: https://codereview.stackexchange.com/questions/178106/simple-object-validator
using System;
using System.Collections.Generic;
using System.Text;
using System.Linq.Expressions;
// I loved this when I found here: https://codereview.stackexchange.com/questions/178106/simple-object-validator
namespace Tools.Validate.Objects // could be something else
{
public class Validation
{
public bool Success { get; }
public string Message { get; }
public Validation(bool success, string message)
{
Success = success;
Message = message;
}
}
public class ValidationRule<T>
{
private readonly string _expression;
private readonly Func<T, bool> _predicate;
public ValidationRule(string expression, Func<T, bool> predicate)
{
_expression = expression;
_predicate = predicate;
}
public bool IsMet(T obj) => _predicate(obj);
public override string ToString() => _expression;
}
public class Validator<T>
{
const string SUCCESS_VERB = "Object of type {0} Failed: {1}";
const string FAILURE_VERB = "Object of type {0} Succeded: {1}";
private readonly IEnumerable<ValidationRule<T>> _rules;
public Validator(IEnumerable<ValidationRule<T>> rules)
{
_rules = rules;
}
public static ValidatorBuilder<T> Builder => new ValidatorBuilder<T>();
public bool IsValid(T obj)
{
return _rules.All(r => r.IsMet(obj));
}
public IEnumerable<Validation> Validate(T obj)
{
if(obj == null)
{
yield return new Valdiation(false, string.Format(
FAILURE_VERB,
typeof(T).Name,
"Is not null"));
yield break;
}
foreach(var rule in _rules)
{
var isValid = rule.IsMet(obj);
yield return new Validation(isValid, isValid
? string.Format(SUCCESS_VERB, typeof(T).Name, rule)
: string.Format(FAILURE_VERB, typeof(T).Name, rule));
}
}
}
public class ReplaceVisitor : ExpressionVisitor
{
private Expression _from, _to;
public ReplaceVisitor(Expression from, Expression to)
{
_from = from;
_to = to;
}
protected override Expression VisitParameter(ParameterExpression node)
{
return node.Equals(_from) ? _to : base.VisitParameter(node);
}
public static Expression Replace(Expression target, Expression from, Expression to)
{
return new ReplaceVisitor(from, to).Visit(target);
}
}
public class ValidationBuilder<T>
{
private readonly List<ValidationRule<T>> _rules = new List<ValidationRule<T>>();
public ValidationBuilder<T> Where(Expression<Func<T,bool>> expression)
{
var typeParameter = Expression.Parameter(typeof(T), typeof(T).Name);
var expressionString = ReplaceVisitor.Replace(
expression.Body,
expression.Parameters[0],
typeParameter)
.ToString();
_rules.Add(new ValidationRule<T>(expressionString, expression.Compile()));
return this;
}
public Validator<T> Build()
{
return new Validator<T>(_rules);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment