Try fast search NHibernate

17 November 2009

NHibernate.Validator: Tags

At the begin of NHibernate Validator we have talked about the exigency of assign a “gravity level” to each validation (for instance Error, Warning, Information). In the last year we had requests for “validation grouping” and “different validation, for the same entity, in different contexts”.

Our answer is: validation tagging

What is a tag

In NHibernate Validator a tag has de same meaning you are giving to a tag in your blog, bookmarks, feed reader, Wave and so on. This mean that each validation-constraint may have none, one or more tags.

The type of the Tag is your choice; you can use System.Type, string, enum, int or whatever you want implements IEqualityComparer and… yes!! even altogether:

public class Error { }
public class Warning { }

public enum MyEnum
{
Error,
Warining
}

public class Entity
{
[Min(Value = 20, Tags = typeof(Error))]
[Max(Tags = new[] { typeof(Error), typeof(Warning) }, Value = 100)]
public int ValueUsingTypes { get; set; }

[Min(Value = 20, Tags = "error")]
[Max(Tags = new[] { "error", "warning" }, Value = 100)]
public int ValueUsingStrings { get; set; }

[Min(Value = 20, Tags = MyEnum.Error)]
[Max(Tags = new[] { MyEnum.Error, MyEnum.Warining }, Value = 100)]
public int ValueUsingEnums { get; set; }

[Min(20)]
[Max(100)]
public int ValueWithoutTags { get; set; }
}

If you are using the XML mapping (to configure the validation), all Tags are strings but using attributes or Loquacious you can use anything you want.

Even the follow declaration is possible:

[Min(Value = 20, Tags = new object[] {typeof(Error), 1, "warning"})]

The usage of an array of objects is a language limitation but, at the end, we can read it as a feature ;-) (have a look to the end of this link).

How validate using Tags

Tags are available in any method used, in the ValidatorEngine, to validate a property or an entity. To explain how work with Tags I prefer to show you some examples:

Example 1:
var entity = new Entity();
validatorEngine.Validate(entity);

This is the “normal” usage without tags. In this case we have four invalid values (one for each property) because without specify a tag we will use all constraints, and in this case, we are breaking all Min constraints.

Example 2:
var entity = new Entity();
validatorEngine.Validate(entity, typeof(Warning));

We have only one constraint tagged with typeof(Warning) : the property ValueUsingTypes should be less than or equal to 100. As default ValueUsingTypes=0 so, using the example2, we will have a valid entity (none invalid values).

Example 3:
var entity = new Entity {ValueUsingTypes = 101};
validatorEngine.Validate(entity, typeof (Warning));

Similar situation of the previous example but this time ValueUsingTypes is greater than 100 so we will have one invalid value.

Example 4:
var entity = new Entity();
validatorEngine.Validate(entity, typeof (Error), null);

Here I’m using two tags: typeof(Error) and the other tag is null. In this case we are selecting all constraints tagged with typeof(Error) and all constraints without tags so we will have two invalid values: ValueUsingTypes is less than 20 and ValueWithoutTags is less than 20.

In practice null is a special reserved tag used to select those constraints without a specific tag.

Example 5:
var entity = new Entity();
validatorEngine.Validate(entity, typeof(Error), MyEnum.Error, "error");

I’m using three Tags: typeof(Error), MyEnum.Error, "error". We will have three invalid values: ValueUsingTypes is less than 20 and ValueUsingStrings is less than 20 and ValueUsingEnums is less than 20.

Example 6:

To validate only those constraint without a tag the syntax is:

var entity = new Entity();
validatorEngine.Validate(entity, new object[] {null});
In this case we will have only one invalid value: ValueWithoutTags is less than 20.

Consideration

In the examples above I have used a really strange way to use Tags only to show you the freedom you having. My personal advise is to define one, two or as most three “meaning” tags. Take care with tagged and no-tagged constraints: in my opinion you should work with all tagged constraint or without tags; if you want mix tagged with no-tagged, don’t forget example-1, example-4 and example-6. If you have some constraints involving the persistence and you want run it only when all others constraints does not have invalid values, the "constraint tagging" is a good candidate to solve the issue.

The result InvalidValue with Tags

The InvalidValue class now has a new property: MatchTags. The new property will contain the intersect between the tags declared for a constraint and the tags used for a specific validation.
Example :
public static class Tags
{
public static readonly string Error = "Error";
public static readonly string Warning = "Warning";
public static readonly string Information = "Information";
}
public class Entity
{
public int ValueMinMax { get; set; }
public int ValueMin { get; set; }
}
public class EntityValidator : ValidationDef<Entity>
{
public EntityValidator()
{
Define(x => x.ValueMinMax)
.Satisfy(v => v >= 20).WithTags(Tags.Error)
.And
.Satisfy(v => v <= 100).WithTags(Tags.Error, Tags.Warning);

Define(x => x.ValueMin)
.GreaterThanOrEqualTo(20).WithTags(Tags.Warning, Tags.Information);
}
}

running a validation as

var entity = new Entity { ValueMinMax = 101 };
validatorEngine.Validate(entity, Tags.Information, Tags.Warning);

For the InvalidValue related to ValueMinMax property the MatchTags is “Warning

For the InvalidValue related to ValueMin property, the MatchTags is [“Warning”, “Information”]

5 comments:

  1. The tag property is not in the current source, is it?
    Even with it, it doesn't compare with Enterprise Library's Validation Block: the RuleSet property is quite useful. NHibernate Validator also doesn't allow validation composition (and, or) or access to other properties or attributes in the class.

    ReplyDelete
  2. What? The code is there in the repository.do you have tortoise-svn ?

    ReplyDelete
  3. I'm sorry, but I just checked to see if I had the latest source from SVN, which I did, and I don't see most of the things you refer to in your post (for example, method Validate with more than one argument). Is it not in the trunk?
    Thanks.

    ReplyDelete