Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Integrate Validation Block with WCF

5.00/5 (3 votes)
23 Sep 2011CPOL4 min read 43K   1.1K  
Introduces how to integrate and use the validation block features in Microsoft Enterprise Library 5.0 with WCF.

Introduction

Validation block is used for validation purposes and supports technology specific integration features such WPF and WCF. It offers an easy and relatively simpler way to validate data compared with the validation methods provided by most technologies. I will focus on how to integrate the validation block in Microsoft Enterprise Library 5.0 with WCF.

You can learn what the validation block is and how to use it in your projects from the following article: Microsoft Enterprise Library 5.0 - Introduction to Validation Block.

Background

WCF employs its own validation methods through the implementation of the IParameterInspector interface. You can find a good article showing how to consume the IParameterInspector interface for validation from the following address: How to: Perform Input Validation in WCF.

This method actually requires more effort and offers more complex ways than the validation block does. Through the validation block, you just focus on what to validate rather than how to validate. This makes the validation block appeal. Also, it prevents you from complexities, writing your own validations and maintaining them.

How to configure

Although it saves you from complexities, I dream of a technology without configuration, but nice news, because you just need to put the following setting into the configuration of your WCF.

XML
<system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="validation"
             type="Microsoft.Practices.EnterpriseLibrary.
                    Validation.Integration.WCF.ValidationElement, 
                  Microsoft.Practices.EnterpriseLibrary.
                    Validation.Integration.WCF, 
                  Version=5.0.414.0, 
                  Culture=neutral, 
                  PublicKeyToken=31bf3856ad364e35" />
      </behaviorExtensions>
    </extensions>
	.......
</system.serviceModel>

After porting the required setting, we need one more step to start. In addition to the validation and common assemblies, we also need to add Microsoft.Practices.EnterpriseLibrary.Validation.Integration.WCF as a reference to our project.

Here is the structure of the references:

References of WCF project

Developing our project

I developed a WCF service and a client WPF application. The WPF application will send the data from the UI to the WCF service and check the results.

Here is the project structure:

Project structure

After configuration and building the project structure, we can write the WCF service and the WPF application. Let's start with the service part.

Service side

In the service side, we create a service named SalaryService. This service exposes two methods: CalculatePartial and CalculateFull. These two methods actually do the same thing, but in different ways.

C#
[ServiceContract]
[ValidationBehavior]
public interface ISalaryService
{
    // This method is validated partially
    // We assume that the client uses proxy to communicate
    // with the service so we do not need to validate the property types
    [OperationContract]
    [FaultContract(typeof(ValidationFault))]
    string CalculatePartial(
                
        [RangeValidator(2, RangeBoundaryType.Inclusive, 5, 
          RangeBoundaryType.Inclusive, 
          MessageTemplate = 
            "Education level must be between {3} and {5}")]
        int EducationLevel,
            
        [RangeValidator(0, RangeBoundaryType.Inclusive, 30, 
          RangeBoundaryType.Inclusive, 
          MessageTemplate = 
            "Your experience in years must be between {3} and {5}")]
        int ExperienceInYears,

        [NotNullValidator(MessageTemplate = 
                          "You must supply your currency")]
        [StringLengthValidator(1, RangeBoundaryType.Inclusive, 3, 
          RangeBoundaryType.Inclusive, 
          MessageTemplate = 
            "Currency code must be between {3} and {5} characters.")]
        string Currency,
            
        bool HasCertificate,
            
        bool InvolvedInManagement
    );

    // This method is validated fully
    // We check all conditions to make sure that
    // the input is totally right using Validation Block
    [OperationContract]
    [FaultContract(typeof(ValidationFault))]
    string CalculateFull(
                
        // EducationLevel is not null, it is parsed
        // to Int as correctly, and it is between 2 and 5
        [NotNullValidator(MessageTemplate = 
                "You must supply your education level")]
        [TypeConversionValidator(typeof(int), 
          MessageTemplate = 
            "You must supply a valid level of your education")]
        [RangeValidator("2", RangeBoundaryType.Inclusive, "5", 
          RangeBoundaryType.Inclusive, 
          MessageTemplate = 
            "Education level must be between {3} and {5}")]
        string EducationLevel,
            
        [NotNullValidator(MessageTemplate = 
          "You must supply your experience in years")]
        [TypeConversionValidator(typeof(int), 
          MessageTemplate = "You must supply a valid 
                             number of experience in years")]
        [RangeValidator("0", RangeBoundaryType.Inclusive, "30", 
          RangeBoundaryType.Inclusive, 
          MessageTemplate = "Your experience in years 
                             must be between {3} and {5}")]
        string ExperienceInYears,
            
        [NotNullValidator(MessageTemplate = "You must supply your currency")]
        [StringLengthValidator(1, RangeBoundaryType.Inclusive, 3, 
          RangeBoundaryType.Inclusive, 
          MessageTemplate = "Currency code must 
                             be between {3} and {5} characters.")]
        string Currency,

        [NotNullValidator(MessageTemplate = 
          "You must supply your certificate status")]
        [TypeConversionValidator(typeof(bool), 
          MessageTemplate = "You must supply a valid 
                             status for your certificate status")]
        string HasCertificate,

        [NotNullValidator(MessageTemplate = 
          "You must supply your status for management involvement")]
        [TypeConversionValidator(typeof(bool), 
          MessageTemplate = "You must supply a valid 
                             status for management involvement")]
        string InvolvedInManagement
    );
}

Above, you see the contract for SalaryService. In order to make WCF service work with the validation block, we need to do two things:

  1. Decorate service contract (interface) with the ValidationBehavior attribute
  2. Decorate operation with [FaultContract(typeof(ValidationFault))]

By doing these items, we tell the validation block to check for validations and provide the validation errors to the client on failure. Let's see how clients get these error messages now.

Client side

C#
SalaryServiceReference.SalaryServiceClient salaryClient;

private void btnSendDataPartial_Click(object sender, RoutedEventArgs e)
{
    salaryClient = new SalaryServiceReference.SalaryServiceClient();

    try
    {
        var result = salaryClient.CalculatePartial(
                Convert.ToInt32(this.txtEducationLevel.Text),
                Convert.ToInt32(this.txtExperienceInYears.Text),
                this.txtCurrency.Text,
                Convert.ToBoolean(this.txtHasCertificate.Text),
                Convert.ToBoolean(this.txtInvolvedInManagement.Text));

        MessageBox.Show(result);
    }
    catch (FaultException<ValidationFault> validationEx)
    {
        // We check for the error messages
        // and the parameters which cause the failure
        foreach (var validationError in validationEx.Detail.Details)
        {
            MessageBox.Show(String.Format("Invalid input for {0} - {1}", 
                       validationError.Tag, validationError.Message));
        }

        salaryClient.Abort();
    }
    catch (Exception ex)
    {
        MessageBox.Show("Unhandled exception: " + ex.ToString());

        salaryClient.Abort();
    }
    finally
    {
        if (salaryClient.State == CommunicationState.Opened)
        {
            salaryClient.Close();
        }
    }
}

private void btnSendDataFull_Click(object sender, RoutedEventArgs e)
{
    salaryClient = new SalaryServiceReference.SalaryServiceClient();

    try
    {
        // We rely fully on the service, so we send the input as we get from UI
        var result = salaryClient.CalculateFull(
                this.txtEducationLevel.Text,
                this.txtExperienceInYears.Text,
                this.txtCurrency.Text,
                this.txtHasCertificate.Text,
                this.txtInvolvedInManagement.Text);

        MessageBox.Show(result);
    }
    catch (FaultException<ValidationFault> validationEx)
    {
        // We check for the error messages
        // and the parameters which cause the failure
        foreach (var validationError in validationEx.Detail.Details)
        {
            MessageBox.Show(String.Format("Invalid input for {0} - {1}", 
                 validationError.Tag, validationError.Message));
        }

        salaryClient.Abort();
    }
    catch (Exception ex)
    {
        MessageBox.Show("Unhandled exception: " + ex.ToString());

        salaryClient.Abort();
    }
    finally
    {
        if (salaryClient.State == CommunicationState.Opened)
        {
            salaryClient.Close();
        }
    }
}

Above, you see how the WCF service operations are consumed by the client. We get the exceptions as we do for other types of exceptions for WCF, with the help of FaultException<>. Remember that we decorated the service operations with ValidationFault, which is a custom SOAP exception provided by the validation block, so we can detect the validation exceptions with FaultException<ValidationFault>.

After capturing the validation exception, it is easy to iterate over the error messages and the parameters which cause the validation exceptions. Let's see it in action now.

Sample

Here is a screen from the client WPF application:

Screen from WPF

After filling the input and clicking on one of the buttons, we see a similar screen, including the error message and the reason provided by the WCF service through the validation block.

Result returned from WCF service

Also, we have two methods, CalculatePartial and CalculateFull. The difference between these two methods is that the first one uses strongly typed parameters while the latter uses string type as the base type for all the parameters and forces the validation block to validate the input type through the attribute TypeConversionValidator.

Final words

Validation block is impressive when it comes to validation and keeps its power through the support of technology specific features for WCF, WPF, WinForms, and ASP.NET. It makes validations simpler and more maintainable compared with other solutions and make you focus on what to validate. I tried to explain how to integrate the WCF features provided by the validation block in Microsoft Enterprise Library. Lastly, I want to mention a difference in the way in which validation block is used in the implementation with WCF. There is no support to validate the parameters using a configuration file in WCF, so we must consider using code base validation.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)