Validation, Part 2
Applies to: Windows Communication Foundation
Published: June 2011
Author: Alex Culp
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:
Get logging in Windows Azure with Enterprise Library https://blogs.msdn.com/b/tomholl/archive/2010/09/06/get-logging-in-windows-azure-with-enterprise-library.aspx
WSCF.blue http://wscfblue.codeplex.com/
Enterprise Library Validation Application Block https://msdn.microsoft.com/en-us/library/ff664356(v=PandP.50).aspx
Integrating Validation Behavior in WCF https://msdn.microsoft.com/en-us/library/ff648939.aspx
Unity 2.0 Download https://msdn.microsoft.com/en-us/library/ff663144.aspx
Enterprise Library 5.0 Download https://www.microsoft.com/downloads/en/details.aspx?FamilyID=bcb166f7-dd16-448b-a152-9845760d9b4c
See also Testing Strategies for WCF Services and Dependency Injection in Windows Communication Foundation
Previous article: Validation, Part 1
Continue on to the next article: WCF Decision Framework