Using xVal with NHibernate Validator 1.2

This will be a quick and dirty post about how to get xVal 1.0 (http://xval.codeplex.com/) to work with the new NHibernate Validator 1.2beta (http://nhforge.org/media/p/7.aspx).

The problem is that xVal 1.0 ships with a NHibernate Validator (NHVal) Ruleset Provider which does not compile against the 1.2beta DLLs because of some API changes.  Meanwhile, S#arp Architecture uses NHVal 1.2 so using those Dlls is a requirement for me.

In the process of rewriting the NHVal ruleset provider I stripped out all XML rule definition support, so for my code to work you must be using NHVal attributes.

With those disclaimers, here is the code:

public class NHibernateValidatorRulesProvider : CachingRulesProvider
{
    private readonly ValidatorMode configMode;
    private readonly RuleEmitterList<IRuleArgs> ruleEmitters = new RuleEmitterList<IRuleArgs>();
 
    public NHibernateValidatorRulesProvider(ValidatorMode configMode)
    {
        this.configMode = configMode;
 
        ruleEmitters.AddSingle<LengthAttribute>(x => new StringLengthRule(x.Min, x.Max));
        ruleEmitters.AddSingle<MinAttribute>(x => new RangeRule(x.Value, null));
        ruleEmitters.AddSingle<MaxAttribute>(x => new RangeRule(null, x.Value));
        ruleEmitters.AddSingle<RangeAttribute>(x => new RangeRule(x.Min, x.Max));
        ruleEmitters.AddSingle<NotEmptyAttribute>(x => new RequiredRule());
        ruleEmitters.AddSingle<NotNullNotEmptyAttribute>(x => new RequiredRule());
        ruleEmitters.AddSingle<PatternAttribute>(x => new RegularExpressionRule(x.Regex, x.Flags));
        ruleEmitters.AddSingle<EmailAttribute>(x => new DataTypeRule(DataTypeRule.DataType.EmailAddress));
        ruleEmitters.AddSingle<DigitsAttribute>(MakeDigitsRule);
    }
 
    protected override RuleSet GetRulesFromTypeCore(Type type)
    {
        var classMapping = ClassMappingFactory.GetClassMapping(type, configMode);
 
        var rules = from member in type.GetMembers()
                    where member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property
                    from att in classMapping.GetMemberAttributes(member).OfType<IRuleArgs>()
                    // All NHibernate Validation validators attributes must implement this interface
                    from rule in ConvertToXValRules(att)
                    where rule != null
                    select new {MemberName = member.Name, Rule = rule};
 
        return new RuleSet(rules.ToLookup(x => x.MemberName, x => x.Rule));
    }
 
    private IEnumerable<Rule> ConvertToXValRules(IRuleArgs att)
    {
        foreach (var rule in ruleEmitters.EmitRules(att))
        {
            if (rule != null)
            {
                rule.ErrorMessage = MessageIfSpecified(att.Message);
                yield return rule;
            }
        }
    }
 
    private string MessageIfSpecified(string message)
    {
        // We don't want to display the default {validator.*} messages
        if ((message != null) && !message.StartsWith("{validator."))
            return message;
        return null;
    }
 
    private RegularExpressionRule MakeDigitsRule(DigitsAttribute att)
    {
        if (att == null) throw new ArgumentNullException("att");
        string pattern;
        if (att.FractionalDigits < 1)
            pattern = string.Format(@"\d{{0,{0}}}", att.IntegerDigits);
        else
            pattern = string.Format(@"\d{{0,{0}}}(\.\d{{1,{1}}})?", att.IntegerDigits, att.FractionalDigits);
        return new RegularExpressionRule(pattern);
    }
 
    private static class ClassMappingFactory
    {
        public static IClassMapping GetClassMapping(Type type, ValidatorMode mode)
        {
            const IClassMapping result = null;
 
            switch (mode)
            {
                case ValidatorMode.UseAttribute:
                    break;
                default:
                    throw new NotImplementedException("Only attributes are supported");
            }
 
            return result ?? new ReflectionClassMapping(type);
        }
    }
}

 

Now in the global.asax file, you just need to hook xVal and this class together like so:

xVal.ActiveRuleProviders.Providers.Add(
                new NHibernateValidatorRulesProvider(
                    ValidatorMode.UseAttribute));
 
Enjoy!

3 Comments

Comments have been disabled for this content.