Click here to Skip to main content
15,887,746 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.

Introduction

.NET comes with attributes for validating data. These validation attributes are specified on class level and that makes it a bit rigid.

For example: say you have an address class with street and a PObox number. Only if you use the PObox, the street can be empty. You cannot easily set a conditional validation. Another case, say I have a 4 step process. In step 1, I ask the user his email account. In step 4, I ask for this bank account number (in steps 2 and 3, he has brought something). If I have a person class with the required attributes on properties Email and BankAccountNr, I can only validate in step 4. And what should I do when a validation is dependent on a property of another class? I know there are some workarounds, but they are not straightforward. I want to validate data structures even if they are in 'partially filled classes' in any combination at any step moment.

Which route should we go? A lot of developers like complex data structures. A client class with a collection of orders that in themselves hold order items, etc. One data structure for all scenarios. Or they too easily assume constraints that are always applicable, for instance, if you have a repair class, the field Complaint should be mandatory, because without a complaint, you cannot know what to repair. In my case, it turned out that a complaint was not mandatory in case of low-priced devices that are directly swapped.

Different scenarios, complex data structures, and implicit assumptions on constraints. It all ends up in extra validations, all over the place, on top of the class validation attributes. The route to go is to deal with this complexity by breaking it up to simple things and start from there: composing by combining. That’s the idea behind composable validations. You can compose the attributes of different classes with other validation functions for different scenarios (in the examples below, a scenario is a strategy). Actually, I went a little step further and applied the composability also to classes themselves. In the end, composability makes things simple and more maintainable, but the architecture behind it, is not simple.

Composable Classes

Let’s start with composable classes. I defined a (abstract) ServiceBase class. The classes ServiceSingle and ServiceCompose derive from ServiceBase. The ServiceCompose constructor takes two ServiceBase classes, that on their turn, can be a ServiceCompose class. A concrete Service, for example LegitimationService, is derived from ServiceSingle.

In this way, you can compose a complete tree of concrete services. On top of that, I defined ServiceBaseEnumerator that implements IEnumerator<ServiceBase>. ServiceBase implements IEnumerable<ServiceBase>. The enumerable and enumerator implementations makes serviceBase LINQ compatible, so you can write the following method in ServiceBase:

C#
public static ServiceBase Find(this ServiceBase Source, System.Type TheType)
{
    var x = (from itm in Source
             where itm.GetType().Equals(TheType)
             select itm).FirstOrDefault();
   return (ServiceBase)x;
}

If you put the following method in the legitimationService class:

C#
public new static Func<ServiceBase, bool> checkType() 
{
     return (x =>
     {
         if (x is LegitimationService) return true;
          return false;
     });
}

You can make code like the following:

C#
ServiceCompose myser = new ServiceCompose(new ServiceCompose(
    new LegitimationService(), new InstructionService()), new AddressService());
LegitimationService legsrv = (LegitimationService)myser.Find(LegitimationService.checkType());

So far so good. The essence of rules composability is that you can code something like:

C#
var compos = legsrv.Then(addRule(somerule)).Then(addRule(somerule))

Where the Thens can be added as needed.

Then is essentially a function. But any function can return Nothing, which, if you think about it, is actually very strange. Say, for example, you program Then as follows:

C#
T Then<T>(T source)
{
    return null;
}

You actually cannot do the above. In a typed language like .NET, null cannot be converted to type parameter T.

This article is not about theory, but there is a well known way around this, via the sometimes called Option class (F# has one), other times the Maybe class. The Maybe class can have a Value, or not in case of null, and looks like:

C#
public class Maybe<T>
{
    public readonly static Maybe<T> Nothing = new Maybe<T>();
    public T Value { get; private set; }
    public bool HasValue { get; private set; }
    public Maybe()
    {
       HasValue = false;
    }
    [System.Diagnostics.DebuggerNonUserCode()] 
    public Maybe(T value)
    {
      Value = value;
      HasValue = true;
    }
}

You can make a static helper class also called Maybe with functions like:

C#
public static class Maybe
{
    [System.Diagnostics.DebuggerNonUserCode()] 
    public static Maybe<T> ToMaybe<T>(this T value)
    {
        if (value == null)
        { 
           return Maybe<T>.Nothing; 
        }
        return new Maybe<T>(value);
    }
    public static Maybe<T> ToNone<T>()
    {
        return Maybe<T>.Nothing;
    }
    [System.Diagnostics.DebuggerNonUserCode()] 
    public static Maybe<R> Then<T, R>(this Maybe<T> source, Func<T, R> ret)
    {
        if (!source.HasValue) return ToNone<R>();
        return ret(source.Value).ToMaybe<R>();
    }
}

This is not that much code, but take the time to understand it. It comes down to the fact that you wrap any value of a Type and also a Null into a Typed Maybe object. If we now supply the Then as second argument a function that, when invoked, returns a Null, this Then still returns a typed Maybe object (with property HasValue = false). Also be aware that Then can change the type of the returned Maybe object from T to R. We can not only compose, but compose while changing the type. For example:

C#
var ret = "Some string".ToMaybe().Then(x => 2).Value

Starts with a string and returns an integer 2.

That’s is our first step in composability. Now back to the validation. If we want to use existing validation attributes and use them as composable validation rules, we have to extract these attributes first. We use LINQ for this:

C#
if (Excludes == null) { Excludes = new List<string>(); }
Func<List<string>, string, bool> fExclude = ((e, i) => { return !e.Contains(i); });
var qry = (from prop in props
           where (prop.GetCustomAttributes(typeof(ValidationAttribute), false).Length > 0) 
                  && (fExclude(Excludes, prop.Name))
           select [something];

You are reading all the Validation attributes of a Type with the possibility to exclude some. But we also need a function that applies the validations on a concrete object and returns whether the validation succeeds or not.

To apply a validation, you can use the following code (tp is a type, v the value of a concrete property):

C#
ValidationContext context = new ValidationContext(tp, null, null){ MemberName = p };
List<ValidationResult> valrets = new List<ValidationResult>();
Validator.TryValidateProperty(v, context, valrets);

valrets holds a list of validation results.

The LINQ query to get the attributes is applied on the Type and should be invoked once and the validation itself is on a concrete object and can be invoked as many times as applicable. To solve this, I apply the following construct: the LINQ query on the Type produces an object that holds a Function that can be invoked on any instance. Because this sounds rather complicated, I will explain this in steps:

C#
Func<Object, T, string, PropertyInfo, [Stateful], [Ftype]> eval = 
((v, tp, p, prpinfo, ctx) => 
{
   fType<[Statefull], [Statefull]> fret;
   fret = fret.toFtype<[Statefull], [Statefull]>
   ( x=> [Above validation code]
      Return x; 
    )};
    Etc.
}

And the select part in the above LINQ query will be something like:

C#
select eval((object)prop.GetValue(obj, null), obj, prop.Name, prop, Source))

Eval returns a [FType]. What does this Ftype look like?

C#
public class fType<T, R>
{
    private readonly Func<T, R> f;
    internal fType(Func<T, R> f)
    {
         this.f = f;
    }
    internal Func<T, R> fFunc
    {
        get { return f; }
    }
    public R Invoke(T data)
    {
        return f.Invoke(data);
    }
}
public static class fType
{
    public static fType<T, R> toFtype<T, R>(Func<T, R> f)
    {
        return new fType<T, R>(f);
    }
    public static fType<T, S> Then<T, R, S>(this fType<T, R> Left, fType<R, S> Right)
    {
        return toFtype<T, S>(ctx =>
        {
             return Right.fFunc(Left.fFunc(ctx));
        });
    }
}

Again, not that much code and with some similarity with the MayBe class. In essence, Ftype encapsulates a function in a typed object. A func delegate is not executed directly but invoked on demand. The static fType function Then combines a fType of T that returns a R, with a Ftype of R that returns a S. In other words with Then, you chain fTypes. This is exactly the same composability capacity as in the Maybe case. Now we can write code like:

C#
var mycomposierules = somefType.Then(SomeOtherfType)

So Maybe and Ftype is the underlying architecture for composing objects and Func delegates.

If we apply validation rules on a composed service, we change the validation state of the composed service. The service can be valid or invalid due to Validation errors. In this way, Validation can be seen as an internal state of a composed service. We therefore wrap the composed Service and the Validation in a StateFul object that holds both. That is the above [Stateful]. The above Ftype.toFtype in the eval function takes this StateFul object as its parameter and returns a StateFul object.

How does the state part of the Stateful object look like? You can choose any object, but I choose the following, making it real Funky, although you don’t need to follow this approach:

C#
public class fState<T, S> where S : ICloneable
{
    private Action<S, T> _statesetter;
    private fType<S, S> _invoke;
    private Func<Unit, S> _getstate;
    public fState(Action<S, T> stateSetter, S stateHolder)
    {
        //Encapsulate stateholder variable in Action!
        Func<S, Action<S, T>> init = (x =>
        {
            x = stateHolder;
            return stateSetter;
        });
        _statesetter = init.Invoke(stateHolder);
        _invoke = fType.toFtype<S, S>(y => stateHolder);
        _getstate = (x => stateHolder);
    }
    public void setState(T value)
    {
        fType<S, S> theninvoke = fType.toFtype<S, S>(y =>
        {
            _statesetter(y, value);
            return y;
        });
        _invoke = _invoke.Then(theninvoke);
    }
    public S getState()
    {
        S ret = _invoke.Invoke(_getstate.Invoke(new Unit()));
        _invoke = fType.toFtype<S, S>(y => ret);
        return (S)ret.Clone();
    }
}

(Unit is the most simple class there is, namely public class Unit{}).

We have a State class without having a member variable holding a State. setState will only generate a chain of function calls (through fType) for invoking an Action (_statesetter). getState will execute these Action calls, ‘reset’ the chain to a ‘one item function call’, and return a cloned state. The only way to get the State is through the getState method and even if the caller of getState holds a reference pointer to the begotten state, because he wants to change it, he only changes a cloned state. You cannot easily mess around with this kind of state, that’s why I use it.

If we use this kind of state, we need a cloneable state, the class ValidationError is the cloneable version of ValidationResult.

C#
public static Stateful<T, fState<ValidationError, 
         CloneableList<ValidationError>>> toValidationStateFul<T>(this T Source)
{
  fState<ValidationError, CloneableList<ValidationError>> mystate;
  Action<CloneableList<ValidationError>, ValidationError> statesetter = ((L, I) => L.Add(I));
  mystate = new fState<ValidationError, CloneableList<ValidationError>>(
                    statesetter, new CloneableList<ValidationError>());
  return Source.ToStateful(mystate);
}

The statesetter is the Action which adds a ValidationError to a List of ValidationErrors. This function is private to toValidationStateful, and unreachable for anybody (remember: no member variables!).

Now you can fill in the [Stateful] placeholder of the eval function from above. What is the end result: attribute based validations are wrapped within Func delegates (fType) that can be invoked when needed. Other kind of validations can also be easily wrapped in a Func delegate contained by a Ftype. The method fRule is exactly doing that:

C#
public static fType<[Stateful], Stateful]> 
       fRule<T>(this [Stateful] Source, Func<T, bool> F, String sError)
{
    return fType.toFtype<[Stateful], Stateful]>((x => Source.Rule(F, sError)));
}
public static [Stateful] Rule<T>(this [Stateful] Source, Func<T, bool> F, String sError)
{
    return Source.Then((x => {
    if( !F(x))
    {
        Source.State.setState(new ValidationError(sError));
    }
    return x;
}));
}

Be aware, we are not invoking any validation method, we are only wrapping Func delegates to be executed. Even the setState is in the end, a Func delegate only executed in case a getState is called.

We have now all the main ingredients. So let’s put them together in a test application.

Test Application

The test application loads XML in which the servicecompose parts are defined. In the XML, so-called strategies are defined. A strategy consists of a servicecompose object combined with rule sets that determine which validation attribute should be applied.

If no strategy is defined, the default strategy is applied (data validation on the basis of validation attributes), in other cases, the defined strategy class takes care of the validations to apply. The XML holds also metadata on the properties to display and to exclude from displaying. A factory class parses the XML and creates the defined servicecomposed object. This test implementations has standard (strategy) methods that can be overridden in case of a custom strategy class. These methods are:

  • setRules. Reads the XML rules section, gets the validations attributes of the defined types with the exclusion of the properties listed as excluded, and puts them in a fType.
  • setProps. Reads the XML property section and returns a list of propertyInfos to be displayed, with the exclusion of properties listed as excluded, and puts them in a fType.
  • determineConcreteStrategy. Gather all the Rules. In case of the standard strategy, these are the rules of 1 and store these in fConcreteScenario.
  • determineConcreteProps. Gather all the propertyinfos. In case of the standard strategy, these are all the properties found in 2.
  • ApplyConcreteStrategy. Apply the strategy on a concrete servicecomposed object and return the list of validationerrors. In the standard strategy, this is the fConcreteScenario of 3.

Only step 4 is executed more than once. In the test application, the user first selects a strategy, the factory executes step 1 to 3, resulting in a UI. When a user leaves an input field, the validating event is fired and on that event, step 4 is called.

The aim of this test application is not to make a perfect parser or perfect UI framework around the validations, treat is as a showcase. Feel free to modify the XML in such a way that it incorporates also the non annotation based validations. Say, you code these validations as Func<ServiceBase, Boolean> delegates and add them in the setRules method to a Dictionary of <string, Func<ServiceBase, Boolean>>. You very easily can use the dictionary key in your meta data for including/excluding these validations. In the test application, the SpecificLegitimationStrategy class is an example of adding non annotation base validation to a strategy.

The difficultFlowStrategy class shows you how to implement a custom strategy. In the XML, the strategy attribute determines if a custom strategy is called. This class shows all a lot of composing. With the Maybe construct, we compose an address check. If a function in the chain returns null, the rest of the chain isn't executed. We do the same with the check on the combination of Mark, Price, and Legitimation. If the mark is not Apple, the other checks are not executed. In the end, I combine the validation and I give you two ways to do it. The last step is the Invoking.

Points of Interest

People who do a lot of functional programming will find of lot of familiar stuff here. I deliberately didn’t refer to this. But perhaps you maybe more interested in this field of coding. fState is an example of having state without having any member variables on the class level.

History

  • 1st December, 2012: First version

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

 
-- There are no messages in this forum --