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) { var errorList = new ObjectValidationResult(); 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 bool IsValid(T target) => 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 (!_predicates.TryGetValue(typeof(T), out var existing)) { existing = new List>(); _predicates[typeof(T)] = existing; } existing.Add(Tuple.Create((Delegate)predicate, message)); } private bool ValidateObject(T obj, bool returnOnError = true, Action action = null) { if (Equals(obj, null)) throw new ArgumentNullException(nameof(obj)); if (_predicates.ContainsKey(typeof(T))) { foreach (var (@delegate, value) in _predicates[typeof(T)]) { if ((bool)@delegate.DynamicInvoke(obj)) continue; action?.Invoke(string.Empty, value); if (returnOnError) return false; } } var properties = AttributeCache.DefaultCache.Value.RetrieveFromType(); foreach (var prop in properties) { foreach (var attribute in prop.Value) { var 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; } } }