IClientValidatable & MVC

IClientValidatable & MVC

Old forum URL: forums.lhotka.net/forums/t/12070.aspx


ajj3085 posted on Friday, July 12, 2013

I'm trying to convert some existing validation attributes to business rules.  I have some which implement IClientValidatable (most of our validation is actually on the view models, something I'm trying to fix by bringing in Csla).  I can easily build the rule in Csla, but my question is how to keep the client side validation.  Bringing the attribute into the business layer would not work as the IClientValidatable attribute is MVC only.

Any ideas on getting the validation client side as well? 

RockfordLhotka replied on Sunday, July 14, 2013

Unless I'm missing something, it looks like this interface basically duplicate the data annotations attributes. Why not just put those on your model(csla supports them), and use cslamodelbinder as shown in the 'Using CSLA 4'

JonnyBee replied on Monday, July 15, 2013

Hi,

Yes, you are missing something. GetClientValidationRules is called directly from the html extensions in the view on the ValidationAttribute and does not use the modelbinder. 

So it's like this: 

     @Html.TextBoxFor(model => model.IsSMS) @Html.LabelFor(model => model.IsSMS)

that will call the GetClientValidationRules on the ValidationAttribute when the attribute implements IClientValidatable.

  /// <summary>
  /// Provides a way for the ASP.NET MVC validation framework to discover at run time whether a validator has support for client validation.
  /// </summary>
  public interface IClientValidatable
  {
    /// <summary>
    /// When implemented in a class, returns client validation rules for that class.
    /// </summary>
    /// 
    /// <returns>
    /// The client validation rules for this validator.
    /// </returns>
    /// <param name="metadata">The model metadata.</param><param name="context">The controller context.</param>
    IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context);
  }

Unfortunately - this means that all your DataAnnotation ValidationRules that implements client side validation must have a reference to the System.Web.Mvc assembly and this assembly must be referenced by your BusinessLibrary in order to add the DataAnnotation rules on properties.

ajj3085 replied on Monday, July 15, 2013

JonnyBee

Hi,

Yes, you are missing something. GetClientValidationRules is called directly from the html extensions in the view on the ValidationAttribute and does not use the modelbinder. 

So it's like this: 

     @Html.TextBoxFor(model => model.IsSMS) @Html.LabelFor(model => model.IsSMS)

that will call the GetClientValidationRules on the ValidationAttribute when the attribute implements IClientValidatable.

  /// <summary>   /// Provides a way for the ASP.NET MVC validation framework to discover at run time whether a validator has support for client validation.   /// </summary>   public interface IClientValidatable   {     /// <summary>     /// When implemented in a class, returns client validation rules for that class.     /// </summary>     ///      /// <returns>     /// The client validation rules for this validator.     /// </returns>     /// <param name="metadata">The model metadata.</param><param name="context">The controller context.</param>     IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context);   }

Unfortunately - this means that all your DataAnnotation ValidationRules that implements client side validation must have a reference to the System.Web.Mvc assembly and this assembly must be referenced by your BusinessLibrary in order to add the DataAnnotation rules on properties.

Jonny, thanks for taking the time to answer my question.  I do understand how the IClientValidatable attribute works, and I also understand that one (very bad, IMO) solution is to add a reference to the MVC library to the business layer.  As stated, I also need these classes to work in WCF, which doesn't seem to combine well with MVC.  I've looked for people trying to use both in the same project, they don't seem to play nice together.

My question is what other options have people used to get around this?  I've been started researching to see if, for example, implementing a ModelValidator subclass could help here. I've also been thinking perhaps there is a way to detect the rule and emit some custom JS in the MVC side of things that will call the server and run the same logic the rule is enforcing.  Maybe this is a case for a view model?

I also understand that some rules are best left for only the server to run; however in this case, it really would be best to have the rule run on the client as well.  The rule is basically "this field is required if the configuration flag in the database says it is."  We have a SaaS solution, and some fields can be required or not based on our customer's preference.

JonnyBee replied on Monday, July 15, 2013

Hi,

I am curious as to why this shouldn't work with WCF. 

Have you looked at http://wcfdataannotations.codeplex.com/ ?

Project Description
WCFDataAnnotations allows you to automatically validate WCF service operation arguments using the attributes and IValidatableObject interface from System.ComponentModel.DataAnnotations.

IE: This allows you to use DataAnnotation on your WCF Data Contract objects. It plugs into the WCF Pipeline to do the validation before data reaches your service implementation. 

RockfordLhotka replied on Monday, July 15, 2013

So Microsoft changed the way the DataAnnotation attributes work so they are no longer automatic like they were in MVC 2 and 3?

JonnyBee replied on Monday, July 15, 2013

No. It's not DataAnnotation. It's the IClientValidate interface in System.Web.Mvc for client side validation that custom DataAnnotation rules may implement to say which JavaScript rule and parameters to execute on client. 

ajj3085 replied on Monday, July 15, 2013

RockfordLhotka
Unless I'm missing something, it looks like this interface basically duplicate the data annotations attributes. Why not just put those on your model(csla supports them), and use cslamodelbinder as shown in the 'Using CSLA 4'

The interface is defined in the MVC library, and takes instances of subclasses defined only in MVC (ModelMetadata and ControllerContext).  The full signature is this:

IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context); 

ajj3085 replied on Monday, July 15, 2013

I think I may have found something that should work.  If I create the rule as a ValidationAttribute without the IClientValidatable interface, Csla can pick up on it fine.

Then I can create an implementation of DataAnnotationsModelValidator<T> and use DataAnnotationsModelValidatorProvider.RegisterAdapter to tie the attribute and validator together.  Since I'm doing a simple Requires it should just work provided I emit the correct metadata in my validator adapter.

I'm using this link as a reference (and we actually have done this before in our application, I just didn't really understand how it was working): http://www.ipreferjim.com/2011/08/dataannotations-mvc3-unobtrusive-validations/

Now the only issue is I need to read a different property value off of the Csla object.  The property is private and I was hoping I could get to it in the attribute via the Csla PropertyInfo for it, but I can't find a way to do a ReadValue from there.  I saw a Csla interface that would help, but it's internal.

I suppose I could just make the property public and use reflection to read it (since I'm using reflection anyway to get the PropertyInfo field I want).

RockfordLhotka replied on Monday, July 15, 2013

Where I am confused is that in MVC 2 and 3 it was enough to just apply a DataAnnotations attribute to a property, and MVC would automatically project the necessary js code into the browser to make those rules run client-side.

Now they've added this new interface, and I understand that it is new and is in the Mvc namespace.

But did they take away the prior behavior of DataAnnotations attributes, thus breaking the existing behavior?

JonnyBee replied on Monday, July 15, 2013

Hi,

This is primarily for custom DataAnnotation rules to support client validation. Like for developers to create their own custom data annotation rules rather than CSLA business rules for validation and get the support of javascript based validation on the client side. 

The developer must however create the javascript client side rule as well but it follows a defined pattern for implementation.

To me - the programming model is flawed in the sense that a business library must have a reference to the interface in System.Web.Mvc and all the interface defines is the method GetClientValidationRules

ajj3085 replied on Monday, August 19, 2013

Just wanted to update this thread in case others are looking at something similar.

Basically instead of writing the rule as a normal Csla BusinessRule, if you create it as a ValidationAttribute subclass instead, you can then in your MVC project implement a subclass of DataAnnotationsModelValidator<T> and override the GetClientValidationRules method.  You can then link things up using the DataAnnotationsModelValidatorProvider.RegisterAdapter(Type attributeType, Type validatorType) method in the global.asax. 

The drawbacks are that you're no longer able to utilize the normal business rule framework and these rules are all at priority 0, but in my case it works out fine as it ends up being effectively a Required validation rule that can be turned on or off via configuration.

The information in this blog post was very helpful, and gives more details on  how you'd implement GetClientValidationRules to tie into the jQuery validation framework.

Copyright (c) Marimer LLC