Monday, November 02, 2009 10:13 AM srkirkland

An xVal Provider For NHibernate Validator

I wrote a post about a month ago about using xVal with NHibernate Validator 1.2 which solved a problem I was having upgrading the xVal ‘in-the-box’ provider to work with a newer version of NHibernate Validator. There was a caveat that my solution only worked for ValidatorMode.UseAttribute and I wouldn’t catch XML or Loquacious (or other?) validation.  This seemed to work OK, but Fabio Maulo wrote a comment to that post saying NHV has metadata which should be the same no matter which validation mode was used.

So I decided to investigate how I could get the metadata without resorting to the NHibernate.Validator.Mappings validationMode specific engines (I was using ReflectionClassMapping, but there are others like XmlClassMapping).

I think I found the solution (at least, it does work…) in the ValidatorEngine’s GetClassValidator method.  GetClassValidator takes in a System.Type and returns an IClassValidator for that Type.  Once you have the class validator, you can get constraints for any member of that class by using GetMemberConstraints(memberName).

After getting the constraints you are onto the easy part, which is basically just mapping the rules into xVal rules and the code is much the same as before.

So without further ado, here is the new ValidatorRulesProvider implementation:

public class ValidatorRulesProvider : CachingRulesProvider
{
    private readonly RuleEmitterList<IRuleArgs> _ruleEmitters;
 
    public ValidatorRulesProvider()
    {
        _ruleEmitters = new RuleEmitterList<IRuleArgs>();
 
        _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<NotNullAttribute>(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 = new ValidatorEngine().GetClassValidator(type);
 
        var rules = from member in type.GetMembers()
                    where member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property
                    from constraint in classMapping.GetMemberConstraints(member.Name).OfType<IRuleArgs>()
                    // All NHibernate Validation validators attributes must implement this interface
                    from rule in ConvertToXValRules(constraint)
                    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 ruleArgs)
    {
        foreach (var rule in _ruleEmitters.EmitRules(ruleArgs))
        {
            if (rule != null)
            {
                rule.ErrorMessage = MessageIfSpecified(ruleArgs.Message);
                yield return rule;
            }
        }
    }
 
    private static 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 string MessageIfSpecified(string message)
    {
        // We don't want to display the default {validator.*} messages
        if ((message != null) && !message.StartsWith("{validator."))
            return message;
        return null;
    }
}

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

xVal.ActiveRuleProviders.Providers.Add(
    new ValidatorRulesProvider(
    ValidatorMode.UseAttribute));

If you aren’t using xVal I would highly recommend it, and if you are using ASP.NET MVC and not using xVal, what are you waiting for?  They are a great match!

 

Enjoy!

Filed under: , , , , , ,

Comments

# Twitter Trackbacks for An xVal Provider For NHibernate Validator - Scott's Blog [asp.net] on Topsy.com

Pingback from  Twitter Trackbacks for                 An xVal Provider For NHibernate Validator - Scott's Blog         [asp.net]        on Topsy.com

# An xVal Provider For NHibernate Validator - Scott Kirkland

Tuesday, November 03, 2009 1:48 AM by DotNetShoutout

Thank you for submitting this cool story - Trackback from DotNetShoutout

# An xVal Provider For NHibernate Validator - Scott's Blog

Tuesday, November 03, 2009 3:25 AM by Servefault.com

Thank you for submitting this cool story - Trackback from Servefault.com

# re: An xVal Provider For NHibernate Validator

Tuesday, November 03, 2009 9:54 PM by Anil

Hello,

Are you able to implement conditional/dependent validation with xVal+NHV?

Thanks

# re: An xVal Provider For NHibernate Validator

Sunday, November 08, 2009 11:20 PM by Fabio Maulo

Now I like it a little bit more.

But when you need an help, for such kind of things, don't forget to ping me. I'm glad to help you.

Take care with "new ValidatorEngine()" because what you need is a configured validatorEngine and not a new one (the new ValidatorEngine will use only the JITClassMappingFactory).

Here the link to the last series about NHV

fabiomaulo.blogspot.com/.../Validator

Ping me ;)

# Finasteride dr reddys labs inc.

Thursday, November 12, 2009 11:36 AM by Finasteride.

Finasteride 1mg. Prostate finasteride. Finasteride. 1mg finasteride for bph. Finasteride side effects. Finasteride systemic mayoclinic com.

# re: An xVal Provider For NHibernate Validator

Sunday, January 24, 2010 11:00 PM by firefly

You said use this to register

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

but the class name is ValidatorRulesProvider am I missing something?

# re: An xVal Provider For NHibernate Validator

Monday, January 25, 2010 1:44 PM by srkirkland

@firefly,

Thanks, you are correct it should be ValidatorRulesProvider.  I've updated the post to reflect this.

# re: An xVal Provider For NHibernate Validator

Thursday, February 11, 2010 8:57 AM by Leonardo Lima

Hi,

Can you provide one example of how to work with NHibernate.Validator and xVal ?

Its just register into global.asax and what I should do in the controller ? just call dao.Save() and catch(RulesException) ??

I started using the example that have in the NHibernate Blog, that shows how to use NHibernate.Validator + ASP.NET MVC, and this was working fine, but I could not take the xVal work :/

Thanks

# re: An xVal Provider For NHibernate Validator

Thursday, February 11, 2010 6:38 PM by srkirkland

@Leonardo Lima,

  After you register the xVal provider in the web.config, you need to call <%= Html.ClientSideValidation<Customer>() %> on your form page.  This will inspect the Customer class for NHibernate Validator constraints, and inject those jQuery validation rules into your page.  

Your controller isn't involved in this process at all because xVal is a client side validation framework/provider.  Of course, you should always do server side validation as well, but that doesn't involve xVal.

Scott

PS: Ping me if you have any other questions or if you'd like to see an example project with these technologies working together.

# re: An xVal Provider For NHibernate Validator

Thursday, April 22, 2010 8:36 AM by pmgriffin

Also notice that ValidatorRulesProvider has a parameterless constructor and doesn't require  ValidatorMode.UseAttribute param?

Thanks for the post!

Paul.

# re: An xVal Provider For NHibernate Validator

Monday, May 24, 2010 5:46 AM by La Martina

...HI......Are you able to implement conditional/dependent validation with xVal+NHV?

Thanks

# re: An xVal Provider For NHibernate Validator

Wednesday, June 02, 2010 5:37 PM by La Martina

This is very nice post

# re: An xVal Provider For NHibernate Validator

Wednesday, June 16, 2010 4:05 PM by jordan II kicks

Thanks, you are correct it should be ValidatorRulesProvider.  I have updated the post to reflect this. greatfull artilce

# re: An xVal Provider For NHibernate Validator

Thursday, July 01, 2010 5:48 AM by mivpl

this process at all because xVal is a client side validation framework/provider.  Of course, you should always do server side validation as well, but that doesn't involve xVal. <a  href="http://www.officechairsuk.com">office chairs</a>

# re: An xVal Provider For NHibernate Validator

Saturday, September 11, 2010 12:03 PM by mini clip

Pretty good post. I just stumbled upon your blog and wanted to say that I have really enjoyed reading your blog posts.Any way Ill be subscribing to your feed and I hope you post again soon

# re: An xVal Provider For NHibernate Validator

Friday, November 05, 2010 6:39 AM by Leonardo Lima

Unfortunately I am no project is to use technology ... Can not wait to get a new to use xVal...

# re: An xVal Provider For NHibernate Validator

Tuesday, March 15, 2011 3:49 AM by web marketing

I am going to email it to all of my friends.

Leave a Comment

(required) 
(required) 
(optional)
(required)