using System; using System.Collections.Concurrent; using System.Collections.Generic; using Swan.Reflection; namespace Swan.Validators { /// /// Represents an object validator. /// /// /// The following code describes how to perform a simple object validation. /// /// using Swan.Validators; /// /// class Example /// { /// public static void Main() /// { /// // create an instance of ObjectValidator /// var obj = new ObjectValidator(); /// /// // Add a validation to the 'Simple' class with a custom error message /// obj.AddValidator<Simple>(x => /// !string.IsNullOrEmpty(x.Name), "Name must not be empty"); /// /// // check if object is valid /// var res = obj.IsValid(new Simple { Name = "Name" }); /// } /// /// class Simple /// { /// public string Name { get; set; } /// } /// } /// /// /// The following code shows of to validate an object with a custom validator and some attributes using the Runtime ObjectValidator singleton. /// /// using Swan.Validators; /// /// class Example /// { /// public static void Main() /// { /// // create an instance of ObjectValidator /// Runtime.ObjectValidator /// .AddValidator<Simple>(x => /// !x.Name.Equals("Name"), "Name must not be 'Name'"); /// /// // validate object /// var res = Runtime.ObjectValidator /// .Validate(new Simple{ Name = "name", Number = 5, Email ="email@mail.com"}) /// } /// /// class Simple /// { /// [NotNull] /// public string Name { get; set; } /// /// [Range(1, 10)] /// public int Number { get; set; } /// /// [Email] /// public string Email { get; set; } /// } /// } /// /// public class ObjectValidator { private static readonly Lazy LazyInstance = new Lazy(() => new ObjectValidator()); private readonly ConcurrentDictionary>> _predicates = new ConcurrentDictionary>>(); /// /// Gets the current. /// /// /// The current. /// public static ObjectValidator Current => LazyInstance.Value; /// /// Validates an object given the specified validators and attributes. /// /// The type of the object. /// The object. /// A validation result. public ObjectValidationResult Validate(T target) { ObjectValidationResult errorList = new ObjectValidationResult(); _ = this.ValidateObject(target, false, errorList.Add); return errorList; } /// /// Validates an object given the specified validators and attributes. /// /// The type. /// The object. /// /// true if the specified object is valid; otherwise, false. /// /// obj. public Boolean IsValid(T target) => this.ValidateObject(target); /// /// Adds a validator to a specific class. /// /// The type of the object. /// The predicate that will be evaluated. /// The message. /// /// predicate /// or /// message. /// public void AddValidator(Predicate predicate, String message) where T : class { if(predicate == null) { throw new ArgumentNullException(nameof(predicate)); } if(String.IsNullOrEmpty(message)) { throw new ArgumentNullException(message); } if(!this._predicates.TryGetValue(typeof(T), out List> existing)) { existing = new List>(); this._predicates[typeof(T)] = existing; } existing.Add(Tuple.Create((Delegate)predicate, message)); } private Boolean ValidateObject(T obj, Boolean returnOnError = true, Action action = null) { if(Equals(obj, null)) { throw new ArgumentNullException(nameof(obj)); } if(this._predicates.ContainsKey(typeof(T))) { foreach((Delegate @delegate, String value) in this._predicates[typeof(T)]) { if((Boolean)@delegate.DynamicInvoke(obj)) { continue; } action?.Invoke(String.Empty, value); if(returnOnError) { return false; } } } Dictionary> properties = AttributeCache.DefaultCache.Value.RetrieveFromType(); foreach(KeyValuePair> prop in properties) { foreach(Object attribute in prop.Value) { IValidator val = (IValidator)attribute; if(val.IsValid(prop.Key.GetValue(obj, null))) { continue; } action?.Invoke(prop.Key.Name, val.ErrorMessage); if(returnOnError) { return false; } } } return true; } } }