Introducing the Validation Application Block
Dec 26, 2015
Agenda
Enterprise Library 3.0 Introduction
Validation Application Block Overview
Applying, using and creating Validators
Validating Data
Summary
Enterprise Library 3.0: New Features At a Glance New application blocks
• Validation Application Block
• Policy Injection Application Block
Improvements to existing application blocks• Data Access Application
Block
• Logging Application Block
.NET Framework 3.0 integration• Logging, Exception
Handling and Validation Application Blocks
Configuration improvements
• Visual Studio-integrated configuration tool
• Environmental Overrides
• Manageable Configuration Source
Automation
• Application Block Software Factory
• Strong Naming Guidance Package
Enterprise Library 3.0 Application Blocks
CachingCaching
SecuritySecurity
Data Data AccessAccess LoggingLogging
ExceptionExceptionHandlingHandling
ConfigConfigHelpers Helpers & Design& Design
Instrumen-Instrumen-tationtation
ObjectObjectBuilderBuilder
CryptographyCryptography
CoreCore
Policy InjectionPolicy InjectionValidationValidation
Agenda
Enterprise Library 3.0 Introduction
Validation Application Block Overview
Applying, using and creating Validators
Validating Data
Summary
Why Validate?
Security
• Protect against script injection and other attacks
Correctness
• Ensure the system doesn’t process invalid data
• Enforce business rules
• Ensure user input is compatible with internal structures, data schemas and external systems
Responsiveness
• Flag invalid data before performing expensive processing tasks
Why a Validation Application Block?
Validation is a requirement in all non-trivial applications
.NET provides some validation capabilities, but these are tied to UI technologies such as ASP.NET
• Often results in the need to re-implement the same validation rules in different parts of an application
Many common validation scenarios require writing a lot of “plumbing” code
Validation Application Block: Goals Specify your validation rules once
• In configuration
• Using attributes
• Programmatically
Easily validate data from anywhere in your application• Programmatically
• Integrated into Windows Forms, ASP.NET or WCF
Composable validation logic• Built-in library of common primitive validation rules
• Combine validation rules on type members and using Boolean logic
• Apply multiple validation rule sets to the same types
Using the Validation Application Block
Create your business objects
Decorate them with validation rules
• Using attributes, configuration or custom code
Validate your data where required
• Using the Validation Application Block API
• Integrated into Windows Forms or ASP.NET UIs
• Integrated into WCF Service Interfaces
• Using the Policy Injection Application Block
Process or display validation results
• Using code or integrated into UIs
Agenda
Enterprise Library 3.0 Introduction
Validation Application Block Overview
Applying, using and creating Validators
Validating Data
Summary
Supplied Validation Rules
Validation Application Block includes the following rules:• Not Null (value must not be null)
• Contains Characters (e.g. does not contain any of /\?<>”:)
• Regular Expression (e.g. value is a valid e-mail address)
• Range (e.g. must be from 10-20 or 1/1/1950 to 12/31/1999)
• Relative DateTime (e.g. birth date is more than 18 years ago)
• String Length (e.g. string is at least 8 characters long)
• Domain (e.g. must be one of {John, Paul, George, Ringo})
• Enum Conversion (e.g. string can be converted to a value in the Color enum type)
• Type Conversion (e.g. string can be converted to a DateTime)
• Property Comparison (e.g. MaxDate > MinDate)
All validation rules can be negated • E.g String Length must not be between 5 and 10 characters
Composing Validation Rules
You can build complex validation rule sets by combining multiple primitive rules
• Combining rules with Boolean AND / OR logic
• Assigning different validators to different members of a type
• Specifying how nested objects or collections are to be validated
A single type may have multiple rule sets that can be used in different contexts
• For example, Valid for displaying or Valid for persisting
• For example, Valid Customer or Preferred Customer
Specifying Validation Rules Using Attributes Attributes allow you to specify validation rules directly
within the type being validated
Validation logic cannot be changed without recompiling
You must own the source code of the type
[StringLengthValidator(1, 50, Ruleset="RuleSetA", MessageTemplate="Last Name must be 1-50 characters")]public string LastName{ get { return lastName; } set { lastName = value; }}
[RegexValidator(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", MessageTemplate="Invalid e-mail address", Ruleset="RuleSetA")]public string Email{ get { return email; } set { email = value; }}
[ObjectValidator("RuleSetA", Ruleset="RuleSetA")]public Address Address{ get { return address; } set { address = value; }}
[StringLengthValidator(1, 50, Ruleset="RuleSetA", MessageTemplate="Last Name must be 1-50 characters")]public string LastName{ get { return lastName; } set { lastName = value; }}
[RegexValidator(@"\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*", MessageTemplate="Invalid e-mail address", Ruleset="RuleSetA")]public string Email{ get { return email; } set { email = value; }}
[ObjectValidator("RuleSetA", Ruleset="RuleSetA")]public Address Address{ get { return address; } set { address = value; }}
Specifying Validation Rules Using Configuration Validation rules are stored in XML and can be edited
with the Enterprise Library configuration tool Validation logic can be changed without recompiling You don’t need to own the source code of the type
Specifying Validation Rules Using Self Validation Sometimes validation rules are best
expressed with code specific to a custom class
Self Validation allows you to create special methods that represent a class’s validation logic
[HasSelfValidation] public class TemperatureRange{ private int min; private int max;
// ...
[SelfValidation] public void CheckTemperature(ValidationResults results) { if (max < min) results.AddResult(new ValidationResult("Max less than min", this, null, null, null)); } }
[HasSelfValidation] public class TemperatureRange{ private int min; private int max;
// ...
[SelfValidation] public void CheckTemperature(ValidationResults results) { if (max < min) results.AddResult(new ValidationResult("Max less than min", this, null, null, null)); } }
Message Templates Define what message will be returned when a
validation call fails All validators include a default message
template• e.g. “The value must match the regular expression
"{3}" with options "{4}".”
You can define your own message template for a validator instance• Using a literal string
• Using a resource type and name (for localization)
A message template may contain tokens {0} {1} etc, that are replaced with values at runtime• Representing validated value, key, tag and validator-
specific values
Writing your own Validators
To create your own reusable validators:
• Write a new class deriving from Validator<T>
Include desired properties and constructors
Override DoValidate and call LogValidationResult if validation fails
Override DefaultMessageTemplate
Apply the ConfigurationElementType attribute to the class, referring to typeof(CustomValidatorData) or your own strongly typed configuration class
• Write a new class deriving from ValidatorAttribute
Include desired properties and constructors
Override DoCreateValidator
The Application Block Software Factory makes this easier
Agenda
Enterprise Library 3.0 Introduction
Validation Application Block Overview
Applying, using and creating Validators
Validating Data
Summary
Validating Data Programmatically Regardless of how you specify rules, objects are
validated in the same way
If validation fails, you get a ValidationResults object, containing a list of ValidationResult objects
Each result includes a message, tag, references to the object and property being validated, and a reference to the original validator.
Validator<Customer> validator = ValidationFactory.CreateValidator<Customer>("Ruleset");ValidationResults results = validator.Validate(customer);if (!results.IsValid){{ foreach (ValidationResult result in results) { Console.WriteLine("Message={0}, Key={1}, "Tag={2}", result.Message, result.Key.ToString(), result.Tag == null ? "null" : "\"" + result.Tag.ToString() + "\""); }}}
Validator<Customer> validator = ValidationFactory.CreateValidator<Customer>("Ruleset");ValidationResults results = validator.Validate(customer);if (!results.IsValid){{ foreach (ValidationResult result in results) { Console.WriteLine("Message={0}, Key={1}, "Tag={2}", result.Message, result.Key.ToString(), result.Tag == null ? "null" : "\"" + result.Tag.ToString() + "\""); }}}
Integrating with WinForms, ASP.NET or WCF Supplied “Integration Adapters” make it
easier to validate data in UI layers and service boundaries
Reuses validation rules defined in attributes and configuration
• Does not tie the validation logic with the UI or service interface layers
Integrating with Windows Forms
Windows Forms integration adapter allows you to associate a control with the validation rules for a particular type and member
• Implemented via the ValidationProvider extender control
• Adds properties to UI controls for binding to a property of the class being validated
• Integrates with the ErrorProvider control to display validation failures alongside the control being validated
• Includes events for converting user input to the correct type for validation
Integrating with ASP.NET
ASP.NET integration adapter allows you to associate a control with the validation rules for a particular type and member
• Implemented via the PropertyProxyValidator control, which is an ASP.NET Validator
• Each PropertyProxyValidator refers to the type and property being validated, the rule set to use and the control containing the input
• Includes events for converting user input to the correct type for validation
Validation occurs server side (no Javascript support)
• But can be used with AJAX techniques
Integrating with WCF
WCF integration implemented as a WCF Behavior that validates data at the service interface layer
• Rules are defined within data contract types or using attributes on the service contract
• Validation occurs automatically when the service is called
• If validation fails, a ValidationFault is returned to the caller
Integrating with WCF: Example
[ServiceContract(Namespace = "http://TestService")][ValidationBehavior]internal interface ITestService{ [OperationContract] [FaultContract(typeof(ValidationFault))] AddCustomerResponse AddCustomer(AddCustomerRequest request);
[OperationContract(Name = "Lookup")] [FaultContract(typeof(ValidationFault))] void LookupById( [RangeValidator(5, RangeBoundaryType.Inclusive, 100, RangeBoundaryType.Exclusive)] int id, [NotNullValidator] [StringLengthValidator(4, RangeBoundaryType.Inclusive, 32, RangeBoundaryType.Inclusive)] string customerName );}
[ServiceContract(Namespace = "http://TestService")][ValidationBehavior]internal interface ITestService{ [OperationContract] [FaultContract(typeof(ValidationFault))] AddCustomerResponse AddCustomer(AddCustomerRequest request);
[OperationContract(Name = "Lookup")] [FaultContract(typeof(ValidationFault))] void LookupById( [RangeValidator(5, RangeBoundaryType.Inclusive, 100, RangeBoundaryType.Exclusive)] int id, [NotNullValidator] [StringLengthValidator(4, RangeBoundaryType.Inclusive, 32, RangeBoundaryType.Inclusive)] string customerName );}
Validating Parameters using thePolicy Injection Application Block The Validation Application Block can be used
along with the Policy Injection Application Block to automatically validate method parameters
Rules can be specified within parameter types (as attributes or configuration), or as attributes directly on the parameter definitions
Objects must be created or wrapped using the PolicyInjection class
Validation Handler can be applied using attribute- or configuration-based policies
If validation fails, an ArgumentValidationException (containing the ValidationResults) will be thrown to the caller
PIAB + VAB Examplepublic class BankAccount : MarshalByRefObject{ private decimal balance;
[ValidationCallHandler] public void Withdraw([RangeValidator(typeof(Decimal), "0.0", RangeBoundaryType.Exclusive, "1000.0", RangeBoundaryType.Inclusive, MessageTemplate="Withdrawal amount must be between zero and 1000.")] decimal withdrawAmount) { if (withdrawAmount > balance) { throw new ArithmeticException(); } balance -= withdrawAmount; }}
public class BankAccount : MarshalByRefObject{ private decimal balance;
[ValidationCallHandler] public void Withdraw([RangeValidator(typeof(Decimal), "0.0", RangeBoundaryType.Exclusive, "1000.0", RangeBoundaryType.Inclusive, MessageTemplate="Withdrawal amount must be between zero and 1000.")] decimal withdrawAmount) { if (withdrawAmount > balance) { throw new ArithmeticException(); } balance -= withdrawAmount; }}
BankAccount account = PolicyInjection.Create<BankAccount>();try{ account.Withdraw(2000);}catch (ArgumentValidationException ex){ // Do Something}
BankAccount account = PolicyInjection.Create<BankAccount>();try{ account.Withdraw(2000);}catch (ArgumentValidationException ex){ // Do Something}
Agenda
Enterprise Library 3.0 Introduction
Validation Application Block Overview
Applying, using and creating Validators
Validating Data
Summary
Summary
The Validation Application Block provides:
• A comprehensive library of primitive validation rules
• The ability to define validation rule sets by applying multiple rules to types using attributes or configuration
• A simple API for validating data and processing results
• Simple integration with Windows Forms, ASP.NET and WCF applications
• Parameter validation, with the help of the Policy Injection Application Block
Resources
Download Enterprise Library and related resources from:
• http://msdn.microsoft.com/practices
Join the Enterprise Library Community at:
• http://codeplex.com/entlib
Share community extensions at:
• http://codeplex.com/entlibcontrib
Read blogs from the Enterprise Library team at:
• http://msdn.microsoft.com/practices/Comm/EntLibBlogs