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
:
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:
public new static Func<ServiceBase, bool> checkType()
{
return (x =>
{
if (x is LegitimationService) return true;
return false;
});
}
You can make code like the following:
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:
var compos = legsrv.Then(addRule(somerule)).Then(addRule(somerule))
Where the Then
s 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:
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:
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:
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:
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:
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):
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:
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:
select eval((object)prop.GetValue(obj, null), obj, prop.Name, prop, Source))
Eval
returns a [FType]
. What does this Ftype
look like?
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:
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:
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)
{
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
.
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 ValidationError
s. 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:
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 propertyInfo
s 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 propertyinfo
s. 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 validationerror
s. 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