Tuesday, May 15, 2012

Model

Data Validation

Client-side validation is being driven by these attributes. We ALSO get validation on the Server-side.

To turn client validation on/off:

<appSettings>
  <add key=”ClientValidationEnabled” value= “true/false”/>
</appSettings>

There is also an HTML helper called EnableClientValidation(bool) that turns off client validation on a view by view basis. Now no validation happens on client but upon Submit, the Server sends back the errors.
  • Required
  • Regex -  match string against
  • Range
  • StringLength

Custom Validation using a Custom Attribute

Good for packaging reusable validation logic
  1. Create a new attribute class with ValidationAttribute as a base class
  2. Override the IsValid
  3. The framework will pass in a value to validate
  4. IsValid returns true or false

[AttributeUsage (AttributeTargets.Property)]
public sealed class MinLengthAttribute : ValidationAttribute
{
  // ..
  public override bool IsValid(object value)
  {
    string valueAsString = value As String;
    return (valueAsString != null & valueAsString.Legth >= _minCharacters);
  }
}

Self-Validating Model

Model itself is self-validating using IValidatableObject

public class Review : IValidatableObject
{
  public virtual int ID {get; set}
  ...
  [Required]
  public virtual string Body {get; set;}

  public virtual DateTime DiningDate {get; set;}

  // impl IValidatableObject
  public IEnumerable<ValidationResult>
    Validate(ValidationContext validationContext)
  {
    if (ReviewDate > DateTime.Now)
  {
    yield return new ValidationResult(“Dinning date cannot be in the future”);
  }
  if (ReviewDate >DateTime.Now.AddYears(-1))
  {
    yield return new ValidationResult(“Dining date cannot be too far in the past”);
  }

}

Validation attributes validation occurs first then this IValidatableObject occurs.

Error Display


Model-level errors appears at the top in the ValidationSummary
ValidationResult needs to be associated with a property to display it beside the property



Only the Model-level validation errors are shown in the ValidationSummary because:
@using (HtmlBeginForm()) {
  @Html.ValidationSummary(true)  // causes the summary display model-level errors only
<..

To display all errors in the summary:  @Html.ValidationSummary(false)

To programmatically display error next to property: 
var   field = new [] {“DiningDate”};

 if (DiningData > DateTime.Now)
  {
    yield return new ValidationResult(“Dinning date cannot be in the future”, field);
  }

Display Format Annotations

  • DisplayColumn - Specify the property of a model class for simple text display
  • HiddenInput - Render value in hidden input (when editing)
  • UIHint - Specify the name of the template to use for rendering
  • DataType - Common templates (email, passoword, URL, currency, multiline)
  • ReadOnly - Specify a read-only property (for model binding)
  • DisplayFormat - Format strings and null display text.  [DisplayFormat(DataFormatString={0:d”}, ApplyFormatInEditMode=true)]  shows date portion only
  • ScaffoldColumn - Turn off display and edit capabilities
  • DisplayName - Friendly-name for  labels. [DisplayName(“Score Date”) ]  


// Model holds the data for a View and interacts with the backend
//   May also contain data validation rules
using System.ComponentModel.DataAnnotations;

// FEATURES:
// Declarative Validation and Formatting using Data
//  Annotation attributes
//  Model Binder and Razor Views enforce these attributes
//  Controller action: Call ModelState.IsValid
//  Razor Views: Display errors using Html.ValidationMessaageFor


public class SomeClass
{
  public int ID {get; set;}

  [Required(ErrorMessage = "Customer Name is required")]
  public string CustomerName {get; set;}

  [Required(ErrorMessage = "Customer Since date is required")]
  [DisplayFormat(DataFormatString = "{0:d}"]
  public DateTime CustomerSince {get; set;}

  [Required(ErrorMessage = "Credit limit must be entered")]
  [Range(1, 1000, ErrorMessage="Credit limit must be between $1 and $1000")]
  [DisplayFormat(DataFormatString = "{0:c}"]
  public decimal CreditLimit {get; set;}

  [StringLength(5)]
  public string ZipCode {get; set;}
  
}