Share via


Validation, Part 2

Applies to: Windows Communication Foundation

Published: June 2011

Author: Alex Culp

Referenced Image

This topic contains the following sections.

  • Enterprise Library Validation Block
  • The Validation Application Block and WCF Integration
  • Additional Resources

Enterprise Library Validation Block

A better approach is to use the Microsoft Enterprise Library Validation Application Block to perform validation logic on the parameters to your service operations. There are two possible approaches. You either can use configuration files to manage validation, or you can use attributes. Each approach has its own advantages. Configuration files are a more flexible way to enforce validation because it is easy to update the files if you find missing, or incorrect validation. Attributes make the logic easier to understand because they are embedded with the entity that is being validated. In addition, cross-field validation logic is often impossible if you use configuration. For example, suppose a validation rule is that if a person chooses email as their primary contact preference, then the email address is a required field. The Validation Application Block already has a large number of built-in validators such as NonNullValidator, StringLengthValidator, and RegexValidator that you can use. For more information about the Validation Application Block, see https://msdn.microsoft.com/en-us/library/ff664356(v=PandP.50).aspx. The following code shows how to use the Validation Application Block to create a data contract.

Visual C# Example DataContract using Validation Blocks

[DataContract]
public class User
{
    [DataMember]
    [StringLengthValidator(1,255)]
    public string FirstName { get; set; }
 
    [DataMember]
    [StringLengthValidator(1, 255)]
    public string LastName { get; set; }
 
    [DataMember]
    [EmailAddressValidator]
    public string EmailAddress { get; set; }
 
    [DataMember]
    [RangeValidator(18,RangeBoundaryType.Inclusive,120,RangeBoundaryType.Inclusive)]
    public int Age { get; set; }
}

Visual Basic Example DataContract using Validation Blocks

<DataContract> _
Public Class User
     <DataMember> _
     <StringLengthValidator(1, 255)> _
     Public Property FirstName() As String
          Get
               Return m_FirstName
          End Get
          Set
               m_FirstName = Value
          End Set
     End Property
     Private m_FirstName As String

     <DataMember> _
     <StringLengthValidator(1, 255)> _
     Public Property LastName() As String
          Get
               Return m_LastName
          End Get
          Set
               m_LastName = Value
          End Set
     End Property
     Private m_LastName As String

     <DataMember> _
     <EmailAddressValidator> _
     Public Property EmailAddress() As String
          Get
               Return m_EmailAddress
          End Get
          Set
               m_EmailAddress = Value
          End Set
     End Property
     Private m_EmailAddress As String

     <DataMember> _
     <RangeValidator(18, RangeBoundaryType.Inclusive, 120, RangeBoundaryType.Inclusive)> _
     Public Property Age() As Integer
          Get
               Return m_Age
          End Get
          Set
               m_Age = Value
          End Set
     End Property
     Private m_Age As Integer
End Class

Note

Do not use the RegexValidator directly for common validations. For example, if you are validating an email address, and the email address appears on multiple entities, it is better to create a custom validator to validate the addresses. This prevents unexpected errors from occurring when someone mistypes the regular expression.

The following code is an example of a custom validator.

Visual C# Example of an Email Address Validator

/// <summary>
/// Regex validator that is specific to email addresses.
/// </summary>
public sealed class EmailAddressValidator : RegexValidator
{
        
    private const string Pattern = @"^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$";
        
    /// <summary>
    /// Initializes a new instance of the <see cref="EmailAddressValidator"/> class.
    /// </summary>
    public EmailAddressValidator()
        : this(false, false)
    {}
 
    /// <summary>
    /// Initializes a new instance of the <see cref="EmailAddressValidator"/> class.
    /// </summary>
    /// <param name="allowNullOrEmptyString">if set to <c>true</c> [allow null or empty string].</param>
    /// <param name="negated">if set to <c>true</c> [negated].</param>
    public EmailAddressValidator(bool allowNullOrEmptyString, bool negated)
        : base(Pattern, negated)
    {
        AllowNullOrEmptyString = allowNullOrEmptyString;
    }
 
    /// <summary>
    /// Implements the validation logic for the receiver.
    /// </summary>
    /// <param name="objectToValidate">The object to validate.</param>
    /// <param name="currentTarget">The object on the behalf of which the validation is performed.</param>
    /// <param name="key">The key that identifies the source of <paramref name="objectToValidate"/>.</param>
    /// <param name="validationResults">The validation results to which the outcome of the validation should be stored.</param>
    /// <remarks>
    /// The implementation for this method will perform type checking and converstion before forwarding the
    /// validation request to method <see cref="M:Microsoft.Practices.EnterpriseLibrary.Validation.Validator`1.DoValidate(`0,System.Object,System.String,Microsoft.Practices.EnterpriseLibrary.Validation.ValidationResults)"/>.
    /// </remarks>
    /// <see cref="M:Microsoft.Practices.EnterpriseLibrary.Validation.Validator.DoValidate(System.Object,System.Object,System.String,Microsoft.Practices.EnterpriseLibrary.Validation.ValidationResults)"/>
    protected override void DoValidate(object objectToValidate, object currentTarget, string key, ValidationResults validationResults)
    {
        var value = objectToValidate as string;
 
        if ((AllowNullOrEmptyString) && (string.IsNullOrEmpty(value)))
            return;
        else
            base.DoValidate(objectToValidate, currentTarget, key, validationResults);
    }
        
    /// <summary>
    /// Gets or sets a value indicating whether [allow null or empty string].
    /// </summary>
    /// <value>
    /// <c>true</c> if [allow null or empty string]; otherwise, <c>false</c>.
    /// </value>
    public bool AllowNullOrEmptyString { get; set; }
 
    /// <summary>
    /// Gets the Default Message Template when the validator is negated.
    /// </summary>
    /// <value></value>
    protected override string  DefaultNegatedMessageTemplate
    {
    get 
    { 
        return "Field should not be in email address format.";
    }
    }
 
    /// <summary>
    /// Gets the Default Message Template when the validator is non negated.
    /// </summary>
    /// <value></value>
    protected override string  DefaultNonNegatedMessageTemplate
    {
    get 
    {
        return "Field is not in email address format.";
    }
    }
}

Visual Basic Example of an Email Address Validator

''' <summary>
''' Regex validator that is specific to email addresses.
''' </summary>
Public NotInheritable Class EmailAddressValidator
     Inherits RegexValidator

     Private Const Pattern As String = "^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+@((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$"

     ''' <summary>
     ''' Initializes a new instance of the <see cref="EmailAddressValidator"/> class.
     ''' </summary>
     Public Sub New()
          Me.New(False, False)
     End Sub

     ''' <summary>
     ''' Initializes a new instance of the <see cref="EmailAddressValidator"/> class.
     ''' </summary>
     ''' <param name="allowNullOrEmptyString">if set to <c>true</c> [allow null or empty string].</param>
     ''' <param name="negated">if set to <c>true</c> [negated].</param>
     Public Sub New(allowNullOrEmptyString__1 As Boolean, negated As Boolean)
          MyBase.New(Pattern, negated)
          AllowNullOrEmptyString = allowNullOrEmptyString__1
     End Sub

     ''' <summary>
     ''' Implements the validation logic for the receiver.
     ''' </summary>
     ''' <param name="objectToValidate">The object to validate.</param>
     ''' <param name="currentTarget">The object on the behalf of which the validation is performed.</param>
     ''' <param name="key">The key that identifies the source of <paramref name="objectToValidate"/>.</param>
     ''' <param name="validationResults">The validation results to which the outcome of the validation should be stored.</param>
     ''' <remarks>
     ''' The implementation for this method will perform type checking and converstion before forwarding the
     ''' validation request to method <see cref="M:Microsoft.Practices.EnterpriseLibrary.Validation.Validator`1.DoValidate(`0,System.Object,System.String,Microsoft.Practices.EnterpriseLibrary.Validation.ValidationResults)"/>.
     ''' </remarks>
     ''' <see cref="M:Microsoft.Practices.EnterpriseLibrary.Validation.Validator.DoValidate(System.Object,System.Object,System.String,Microsoft.Practices.EnterpriseLibrary.Validation.ValidationResults)"/>
     Protected Overrides Sub DoValidate(objectToValidate As Object, currentTarget As Object, key As String, validationResults As ValidationResults)
          Dim value = TryCast(objectToValidate, String)

          If (AllowNullOrEmptyString) AndAlso (String.IsNullOrEmpty(value)) Then
               Return
          Else
               MyBase.DoValidate(objectToValidate, currentTarget, key, validationResults)
          End If
     End Sub

     ''' <summary>
     ''' Gets or sets a value indicating whether [allow null or empty string].
     ''' </summary>
     ''' <value>
     '''      <c>true</c> if [allow null or empty string]; otherwise, <c>false</c>.
     ''' </value>
     Public Property AllowNullOrEmptyString() As Boolean
          Get
               Return m_AllowNullOrEmptyString
          End Get
          Set
               m_AllowNullOrEmptyString = Value
          End Set
     End Property
     Private m_AllowNullOrEmptyString As Boolean

     ''' <summary>
     ''' Gets the Default Message Template when the validator is negated.
     ''' </summary>
     ''' <value></value>
     Protected Overrides ReadOnly Property DefaultNegatedMessageTemplate() As String
          Get
               Return "Field should not be in email address format."
          End Get
     End Property

     ''' <summary>
     ''' Gets the Default Message Template when the validator is non negated.
     ''' </summary>
     ''' <value></value>
     Protected Overrides ReadOnly Property DefaultNonNegatedMessageTemplate() As String
          Get
               Return "Field is not in email address format."
          End Get
     End Property
End Class

The Validation Application Block and WCF Integration

New to Enterprise Library 5.0 is WCF integration, which provides built-in service behaviors that execute validation logic on the parameters to your operations. These behaviors mean that you do not have to write additional custom code to call the Validation Application Block. To integrate the Validation Application Block into your service, decorate your service interface with the ValidationBehavior attribute, and then register the behavior as a behavior extension in your service model configuration file. For more information about integration with WCF see "Integrating with WCF" at https://msdn.microsoft.com/en-us/library/ff664526(v=PandP.50).aspx.

Additional Resources

For more information, see the following resources:

Previous article: Validation, Part 1

Continue on to the next article: WCF Decision Framework