Click here to Skip to main content
15,895,142 members
Articles / Programming Languages / C# 4.0

Composable Data Validations. Func Delegates Everywhere

Rate me:
Please Sign up or sign in to vote.
0.00/5 (No votes)
3 Dec 2012CPOL10 min read 14.4K   105   18  
Composable data validations
This article will show you how to combine type based validation rules with other validations, how to compose them and apply them in different scenarios, adding/removing validations when applied on concrete instances, combining objects of different types and implementing one validation strategy on all.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ComposableCore;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace ComposableServices
{
    /// <summary>
    /// Validation extensions which
    /// </summary>
    public static class ValidationExt
    {
        /// <summary>
        /// Combines a stateful source (combination of a source of T and a state object of a list of validationerrors) with a validationfunction and a validationerrormessage into a Ftype 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="Source"></param>
        /// <param name="F"></param>
        /// <param name="sError"></param>
        /// <returns></returns>
        public static fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>,
            Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>> fRule<T>(this Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> Source, Func<T, bool> F, String sError)
        {
            return fType.toFtype<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>, Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>>((x => Source.Rule(F, sError)));
        }
        /// <summary>
        /// Wraps a function that excutes a validation and that puts the validation error on the state object of a stateful object.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="Source"></param>
        /// <param name="F"></param>
        /// <param name="sError"></param>
        /// <returns></returns>
        public static Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> Rule<T>(this Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> Source, Func<T, bool> F, String sError)
        {
            return Source.Then((x =>
            {
                if (!F(x))
                {
                    Source.State.setState(new ValidationError(sError));
                }
                return x;
            }));
        }
        /// <summary>
        /// Reads the validation attributes on the properties of a Type and store the returned fvalidation functions into a Ftype.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="Source"></param>
        /// <param name="obj"></param>
        /// <param name="setCurrentValue">Call back function needed to replace the referenced object value at the time the Ftype was created with the current value of object that is validated.</param>
        /// <returns></returns>
        public static fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>, Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>> fValidationAttrToRule<T>(
            this Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> Source, T obj, Func<T, PropertyInfo, Object, Object> setCurrentValue)
        {
            return fValidationAttrToRule<T>(Source, obj, new List<string>(), setCurrentValue);
        }
        /// <summary>
        /// Reads the validation attributes on the properties of a Type and store the returned fvalidation functions into a Ftype. Exclude the validation attributes on properties listed in Excluded.
        /// </summary>
        /// <typeparam name="T">The type which is decorated with the validation attributes</typeparam>
        /// <param name="Source"></param>
        /// <param name="obj"></param>
        /// <param name="Excludes"></param>
        /// <param name="setCurrentValue">Call back function needed to replace the referenced object value at the time the Ftype was created with the current value of object that is validated.</param>
        /// <returns></returns>
        public static fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>, Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>> fValidationAttrToRule<T>(
            this Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> Source, T obj, List<String> Excludes, Func<T, PropertyInfo, Object, Object> setCurrentValue)
        {
            if (Excludes == null) { Excludes = new List<string>(); }
            Func<List<string>, string, bool> fExclude = ((e, i) => { return !e.Contains(i); });
            fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>, Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>> retvalue = null;

            System.Reflection.PropertyInfo[] props = obj.GetType().GetProperties();
            Func<Object, T, string,
                PropertyInfo,
                Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>,
                fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>,
                Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>>> eval =
                ((v, tp, p, prpinfo, ctx) =>
                {
                    fType<Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>, Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>> fret;
                    fret = fType.toFtype<
                        Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>,
                        Stateful<T, fState<ValidationError, CloneableList<ValidationError>>>>
                        (x =>
                        {

                            if (setCurrentValue != null)
                            {
                                //v is de value of the property at time of creation of this eval function
                                //get te real value for the current instance
                                v = setCurrentValue(tp, prpinfo, v);
                            }

                            //var s= Stateful.ToStateful(setInstance(x.Value), x.State);

                            ValidationContext context = new ValidationContext(tp, null, null) { MemberName = p };
                            List<ValidationResult> valrets = new List<ValidationResult>();
                            Validator.TryValidateProperty(v, context, valrets);
                            if (valrets.Count > 0)
                            {
                                valrets.ForEach((vr) => ctx.State.setState(new ValidationError(vr.ErrorMessage)));
                            }
                            return x;
                        });
                    if (retvalue == null)
                    {
                        retvalue = fret;
                    }
                    else
                    {
                        retvalue = retvalue.Then(fret);
                    }
                    return retvalue;
                });


            var qry = (from prop in props
                       where (prop.GetCustomAttributes(typeof(ValidationAttribute), false).Length > 0) && (fExclude(Excludes, prop.Name))
                       select eval((object)prop.GetValue(obj, null), obj, prop.Name, prop, Source)).ToList();
            return retvalue;
        }
        /// <summary>
        /// Wraps a source of T in a Stateful object of T combined with a fState object.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="Source"></param>
        /// <returns></returns>
        public static Stateful<T, fState<ValidationError, CloneableList<ValidationError>>> toValidationStateFul<T>(this T Source)
        {
            //The action delegates manage the state. These delegates are private to this method.
            //The fState does'nt have any member variable to hold the state. This construct gives maximum protection.
            fState<ValidationError, CloneableList<ValidationError>> mystate;
            Action<CloneableList<ValidationError>, ValidationError> statesetter = ((L, I) => L.Add(I));
            Action<CloneableList<ValidationError>> resetsetter = ((L) => L.Clear());
            mystate = new fState<ValidationError, CloneableList<ValidationError>>(statesetter, new CloneableList<ValidationError>(), resetsetter);
            return Source.ToStateful(mystate);
        }
        
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Netherlands Netherlands
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions