Validating WCF service operations using System.ComponentModel.DataAnnotations

All WCF services operations need some level of parameter validation to ensure that the data passed to the operation is present and correct. Two popular methods are manual checking and the Validation Application Block (VAB) from Enterprise Library. This article discusses another option - using the validation features from System.ComponentModel.DataAnnotations.

This article was heavily inspired by a recent post by Thoai Nguyen.

Most WCF service operations take in one or more parameters and in almost all cases, you will want some sort of validation of these parameters. This validation could be something as simple as a null check or something a bit more sophisticated. There are many different methods you can use to achieve this. The simplest approach is to perform the validation at the start of each operation:

if (authenticateRequest == null)
{
    throw new ArgumentNullException("authenticateRequest");
}

if (authenticateRequest.Username == null)
{
    throw new ArgumentException("authenticateRequest.Username cannot be null");
}

This approach is very transparent but depending on the complexity of the request and the amount of validation required, it can seriously affect the readability of the operation. There may also be duplication issues.

Another option that is very popular is to use the Validation Application Block (VAB) from Microsoft's Enterprise Library. VAB includes numerous attributes that you decorate your datacontracts with and a WCF behavior that enables all the magic.

[ValidationBehavior]
public interface IMembershipService
{
  [OperationContract]
  [FaultContract(typeof(ValidationFault))]
  AuthenticateResponse Authenticate(AuthenticateRequest authenticateRequest);

  ...
}

[NotNullValidator]
public class AuthenticateRequest
{
  [DataMember]
  [NotNullValidator]
  public string Username { get; set; }

  ...
}

The VAB approach is very comprehensive and I have used it successfully on several projects but I do have some problems with it. Firstly, it requires that your data and service contracts have a reference to the following EntLib DLLs:

At the service level this is OK, but for internal services, it is very common to share the contract assembly with the client, meaning that the client also needs to reference these assemblies.

The other problem that I have with VAB is the FaultException that is raised when validation fails. The structured ValidationFault that VAB throws is very useful if you want to explicitly catch and deal with validation errors from the server, but this is not what I want for the majority of my non-public facing services. As far as I am concerned, if an invalid call (a call which fails service validation) is made to the service, then there is a bug in the client. It should not be possible to make an invalid call because all clients should validate user input sufficiently. I never want to write client code to deal with these validation failures specifically, so have no use for a structured exception.

Having said that, receiving this structured information in addition to a readable error message would be fine but unfortunately, with VAB this is not the case. This is what we get when debugging through Visual Studio:

Visual Studio VAB Unfriendly Error Message"

If you have exception logging code, then unless you are doing something special, you will end up logging "The creator of this fault did not specify a Reason" and the actual problem(s) will not even be recorded. Similarly, if you use the WCF Test Client to pass invalid data to a WCF service using VAB, you get this extremely unhelpful message:

Validation Application Block Unfriendly Error Message

Yes, I can go into the SOAP XML and try and find out the actual error but it is not the behaviour that I want. I just want a simple message indicating exactly what is wrong with my request. In case you hadn't already guessed, I find this VAB quirk a little annoying.

Why choose DataAnnotations?

So as we have seen, I am not 100% happy with the Validation Application Block or manual validation but I certainly don't want to go to the trouble of implementing an entire validation framework from scratch. A problem ... with a simple solution. Enter System.ComponentModel.DataAnnotations.

The System.ComponentModel.DataAnnotations namespace is well established and reasonably well-known thanks to its use in ASP.NET MVC, WPF, Silverlight et al. The assembly is part of the .NET framework, so no external assembly references are required. It is also very capable and easily customisable. In conclusion, it is ideal for our needs.

So a fantastic validation framework is available with .NET. All we need to do is integrate it into WCF.

Hooking into each WCF operation

WCF has an excellent extensibility model so hooking in to most aspects of WCF is possible. Runtime extensibility is usually achieved using behaviors, of which there are four types: service, endpoint, contract and operation. I think it makes most sense to apply our validation behavior to the actual service, so all operations get validated without needing to explicitly include each one. Therefore our behavior will need to implement IServiceBehavior.

The next question that we need to answer is how to get access to the parameters for each operation. Thanks to a great article by Thoai Nguyen, I now know about the IParameterInspector interface which is exactly what we need.

public interface IParameterInspector
{
  object BeforeCall(string operationName, object[] inputs);
  void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState);
}

Essentially, we need to use a WCF behavior to add a parameter inspector to each operation. In the BeforeCall method of the parameter inspector, we need to cycle through all inputs and validate them. If there are any validation failures, we throw a FaultException with the details.

Integrating DataAnnotations into WCF

Note that I am not going to explain every detail of the package, so if you are not sure about something, please take a look at the source code on CodePlex which is very readable and comes complete with unit tests.

To begin, we create the behavior class and implement IServiceBehavior. In the ApplyDispatchBehavior method of our behavior, we enumerate over all operations and then add our ValidatingParameterInspector instance to each one.

public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
    var operations =
        from dispatcher in serviceHostBase.ChannelDispatchers.Cast<ChannelDispatcher>()
        from endpoint in dispatcher.Endpoints
        from operation in endpoint.DispatchRuntime.Operations
        select operation;

    operations.ToList().ForEach(operation => operation.ParameterInspectors.Add(_validatingParameterInspector));
}

Now we come to actually defining how and what we are going to validate. The System.ComponentModel.DataAnnotations namespace is probably most widely known as the home of numerous validation attributes. These includes [Required], [RegularExpression] and [Range] plus several others. We also have an abstract ValidationAttribute class that can be used to write your own validation attributes very easily. If you are a regular reader of this blog, you will remember that we talked about writing custom validation attributes in the context of ASP.NET MVC3 in a previous article. We are definitely going to have to support both built-in and custom attributes but as we will see, this is very easy.

Sometimes, for ad hoc validation requirements, you might not want to go to the trouble of subclassing ValidationAttribute. For these situations, DataAnnotations provides another option - IValidatableObject. Again, we have discussed this interface previously. In the context of ASP.NET MVC, we concluded that it was not particularly useful due to its inability to perform client-side validation. In WCF however, it is much more usable and it makes sense that we support it. Instead of using attributes, it involves implementing a single Validate method: IEnumerable Validate(ValidationContext validationContext);

We are also going to add a third level of validation to check for null arguments. Please note that this code assumes that you are using request/response type contracts rather than RPC-style simple types. I believe that most people (certainly at enterprise level) will be implementing their services in this manner.

Given the fact that there are three types of validation, it makes sense to define a validator interface that we can use to decouple our code. We can then inject a collection of validators into our parameter inspector. We should also probably abstract out the error message generation and inject that too.

public interface IObjectValidator
{
    IEnumerable<ValidationResult> Validate(object input);
}

public class ValidatingParameterInspector: IParameterInspector
{
    private readonly IEnumerable<IObjectValidator> _validators;
    private readonly IErrorMessageGenerator _errorMessageGenerator;

    public ValidatingParameterInspector(IEnumerable<IObjectValidator> validators, IErrorMessageGenerator errorMessageGenerator)
    {
        ValidateArguments(validators, errorMessageGenerator);

        _validators = validators;
        _errorMessageGenerator = errorMessageGenerator;
    }

    ...
}

With these abstractions in place, our parameter inspector implementation is trivial. The BeforeCall method is shown below:

public object BeforeCall(string operationName, object[] inputs)
{
    var validationResults = new List<ValidationResult>();

    foreach (var input in inputs)
    {
        foreach (var validator in _validators)
        {
            var results = validator.Validate(input);
            validationResults.AddRange(results);
		}
    }

    if (validationResults.Count > 0)
    {
        throw new FaultException(_errorMessageGenerator.GenerateErrorMessage(operationName, validationResults));
    }

    return null;
}

Now let's look at each object validator. Starting with the NullCheckObjectValidator which needs no explanation:

public class NullCheckObjectValidator : IObjectValidator
{
    public IEnumerable<ValidationResult> Validate(object input)
    {
        if (input == null)
        {
            yield return new ValidationResult("Input is null.");
        }
    }
}

The DataAnnotationsObjectValidator uses reflection to query all ValidationAttributes from all properties. We call the IsValid method on each validator, passing in the property value. For all invalid properties, we return a validation result using the error message defined in the validation attribute.

public class DataAnnotationsObjectValidator : IObjectValidator
{
    public IEnumerable<ValidationResult> Validate(object input)
    {
        if (input == null) return Enumerable.Empty<ValidationResult>();

        return from property in TypeDescriptor.GetProperties(input).Cast<PropertyDescriptor>()
               from attribute in property.Attributes.OfType<ValidationAttribute>()
               where !attribute.IsValid(property.GetValue(input))
               select new ValidationResult
               (
                   attribute.FormatErrorMessage(string.Empty),
                   new[] { property.Name }
               );
    }
}

Now, we move on to support for IValidatableObject. As we saw earlier, IValidatableObject contains a single validate method which should be fairly easy for us to call:

public class ValidatableObjectValidator : IObjectValidator
{
    public IEnumerable<ValidationResult> Validate(object input)
    {
        var validatableInput = input as IValidatableObject;

        if (validatableInput != null)
        {
            return validatableInput.Validate(new ValidationContext(input, null, null));
        }

        return Enumerable.Empty<ValidationResult>();
    }
}

Again very simple. We try and cast the input to IValidatableObject and if successful, we call the Validate method with a ValidationContext and return the results. If we find that the input does not implement IValidatableObject, we return an empty enumeration.

Finally, if any validation errors have been found in the three previous steps, we generate a readable error message to be thrown as a fault exception. We could return a structured fault exception here but as mentioned previously, I do not want to write code to handle these kinds of exceptions at the client. When debugging, I want the client to break immediately. When released, I want to handle the exception as any other unforeseen exception - typically logging it and showing the user a friendly error message. In both cases, a structured error would not add value. If you disagree, feel free to modify it to suit your requirements.

public class ErrorMessageGenerator : IErrorMessageGenerator
{
    public string GenerateErrorMessage(string operationName, IEnumerable<ValidationResult> validationResults)
    {
        ValidateArguments(operationName, validationResults);

        var errorMessageBuilder = new StringBuilder();

        errorMessageBuilder.AppendFormat("Service operation {0} failed due to validation errors: \n\n", operationName);

        foreach (var validationResult in validationResults)
        {
            var memberName = validationResult.MemberNames.FirstOrDefault();

            if (memberName == null)
            {
                errorMessageBuilder.AppendFormat("{0} \n", validationResult.ErrorMessage);
            }
            else
            {
                errorMessageBuilder.AppendFormat("{0}: {1} \n", memberName, validationResult.ErrorMessage);
            }
        }

        return errorMessageBuilder.ToString();
    }
}

Using the ValidateDataAnnotationsAttribute

(1) Install the NuGet package using the Install-Package WCFDataAnnotations command. Alternatively, download the dll from CodePlex and manually add a reference to DevTrends.WCFDataAnnotations.dll in the project containing your service implementation.

(2) Decorate your service implementation with the ValidateDataAnnotationsBehavior attribute: [ValidateDataAnnotationsBehavior] public class MembershipService : IMembershipService

If you prefer, instead of using the attribute, you can add the behavior in configuration:

<system.serviceModel>
  <serviceHostingEnvironment multipleSiteBindingsEnabled="true"/>
  <extensions>
    <behaviorExtensions>
      <add name="validateDataAnnotationsBehavior"
           type="DevTrends.WCFDataAnnotations.ValidateDataAnnotationsBehaviorExtensionElement, DevTrends.WCFDataAnnotations, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    </behaviorExtensions>
  </extensions>
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <validateDataAnnotationsBehavior/>
      </behavior>
    </serviceBehaviors>
  </behaviors>
  <services>
    </service>...</services>
  <bindings>...</bindings>
  <client>...</client>
</system.serviceModel>

(3) Add a reference to the .NET 4.0 assembly System.ComponentModel.DataAnnotations in the project containing your data contracts.

(4) Decorate your data contracts with the relevant data annotations.

[DataContract(Namespace = "http://schemas.devtrends.co.uk/example/data")]
public class AddCustomerAffiliationRequest
{
    [DataMember]
    [Required]
    public string ThirdPartyIdentifier { get; set; }

    [DataMember]
    [Required]
    [StringLength(500, MinimumLength = 5)]
    public string Description{ get; set; }

    ...
}

You can also/instead implement IValidatableObject:

[DataContract(Namespace = "http://schemas.devtrends.co.uk/example/data")]
public class AddCustomerToPromotionRequest : IValidatableObject
{
    [DataMember]
    public int PromotionId { get; set; }

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        if (PromotionId < 1)
        {
            yield return new ValidationResult("PromotionId must be greater than 0", new[] { "PromotionId" });
        }
    }
}

That's it. Connect to your WCF service and your operation arguments will automatically be validated using your configured attributes. If you try to pass invalid data, a FaultException will be thrown detailing the validation failure(s).

WCFDataAnnotations Friendly Error Message

Conclusion

The Validation Application Block has some issues that I find rather annoying - the many one being the lack of a fault reason in the generic FaultException, making logging and debugging unnecessarily difficult. Fortunately, it is extremely easy to roll your own validation that hooks into the WCF pipeline. We would certainly not want to start from scratch but we do not need to - thanks to the excellent validation features in System.ComponentModel.DataAnnotations which are already well-known to many .NET developers.

The WCFDataAnnotations Nuget package is now available. The full source code is available on CodePlex should you need it. WCFDataAnnotations allows you to decorate your WCF service contracts with validation attributes and have each operation validate its inputs using these attributes automatically. Custom validation attributes and IValidatableObject are also supported. Hope you find it useful.

Useful or Interesting?

If you liked the article, I would really appreciate it if you could share it with your Twitter followers.

Share on Twitter

Comments

Avatar for Matt Honeycutt Matt Honeycutt wrote on 05 Jan 2012

Very cool! I recently had to do something similar, but my solution wasn't as elegant as this. Nice job!

Avatar for Ray Ray wrote on 16 Feb 2012

Thank you for your great work.
BTW I am trying to apply wcfdataannotations on my WCF service which my jquery ajax invoke. The trouble I have is that I do not know where should I catch the exception.
My service is as follow :

public void AddUser(User user)
{
try
{
using (GolfJinDbContext db = new GolfJinDbContext())
{
db.Users.Add(user);
db.SaveChanges();
}
}
catch (Exception ex)
{
logger.Error("adduser {0}", ex.Message);
}
}

I expected that try-catch would catch the validation error, but prior to execution of AddUser method, validation error is already thrown so that I got "400 Bad Request" error.
To sum up, What should I do to treat validation error on my service method?

Avatar for Paul Hiles Paul Hiles wrote on 17 Feb 2012

@Ray - the validation occurs before the operation is executed so you need to catch it a little earlier in the pipeline. I use IErrorHandler and log the error in the HandleError method.

Avatar for Adam Adam wrote on 11 Mar 2012

Great package!

I don't know if the mechanism exists to do this, but it's a bit difficult to present a user friendly error message.

It doesn't look like FaultException is sealed - however I'm not sure if you could extend it and return a list of ValidationResults or anything. Right now you have to do some string jokeying to get it right.

Thanks!

Avatar for Paul Hiles Paul Hiles wrote on 14 Mar 2012

@Adam - as I mentioned in the article, I am advising that your primary validation mechanism should reside in the client. In my mind, it is not possible to centralise your validation in the service whilst also providing the best user experience (such as immediate JS validation). In an MVC client, you would typically use data annotations on your view models which would allow javascript and server side (mvc) validation, providing friendly error messages to your users in a optimal way.

I see the validation on the service to be more of a backup. The client should never be allowed to make calls to the service with the kind of invalid data that can be picked up by data annotations. If a call is made, then it is a bug. Therefore, there is no need for structured errors as they will never be seen or used by client code. When debugging, the developer will see the error immediately and fix the bug and when in production, the error will be caught and logged and the user will see a generic friendly error message.

The standard way to return structured errors from WCF is the generic version of the FaultException class but in order for the client to use these, a fault contract needs to be created for each operation. This adds a dependency on the WCFDataAnnotations package to the contracts assembly which as outlined above, is not ideal for situations where the contracts assembly is shared between server and client.

Having said all that, I can see situations where structured errors may be more useful - smart clients for example. If it does not have a detrimental affect on the approach mentioned above, I will try and include it in the next release.

In the meantime, as this is the mechanism used by the Validation Application Block (VAB), if you do want structured errors right now, you can either modify the WCFDataAnnotations source code yourself (available on CodePlex) or use the VAB.

Avatar for Matt Matt wrote on 31 Mar 2012

Very handy project!

I am having a problem though as I have an operation contract like below where I havent used a marked up data contract just a simple nullable int.

OrderDTO StartNewOrder(int? profileId)

In this case null is valid but I get an 'Input is null' validation exception. Is there anything I can do?

Avatar for Paul Hiles Paul Hiles wrote on 10 Apr 2012

@Matt - There is nothing you can do without modifying the source. I am a strong believer in using data contracts for every operation so I have enforced this in the package. You could remove the NullCheckObjectValidator and recompile if you do not require it though.

Avatar for Ngoc Tri Nguyen Ngoc Tri Nguyen wrote on 16 Apr 2012

Hi,

Thanks for a great article.

Anyhow, I am having problem using this approach with array of data-objects for instance

DataContract:

[DataContract]
public class Data
{
[StringLength(10)]
public string Prop1{get;set;}
}

Service Contract:

[ServiceContract]
[ValidateDataAnnotationsBehavior]
class Service1
{
public void Method1(List<Data> listOfData)
{
...
...
}
}

As you can see i have a constrain on the property, Prop1, and since the DataAnnotationsObjectValidator does not check for parameters of type List/Array/IEnumerable the constrain does not applied.

Is my observe correct or do i misse something?
Please instruct!.

In advance thanks.
/NTN

Avatar for Ngoc Tri Nguyen Ngoc Tri Nguyen wrote on 17 Apr 2012

Hi Paul,

For validating a collection of objects, I have to change the DataAnnotationsObjectValidator and I post it here if you or anyone else can verify it or perhaps make it more robust.

Again thanks for a great article.

public class DataAnnotationsObjectValidator : IObjectValidator
{
public IEnumerable<ValidationResult> Validate(object input)
{
if (input == null) return Enumerable.Empty<ValidationResult>();

if (!IsCollection(input))
return SimpleObject(input);

return IEnumerableObject(input);
}

public bool IsCollection(object o)
{
return typeof(ICollection).IsAssignableFrom(o.GetType())
|| typeof(ICollection<>).IsAssignableFrom(o.GetType());
}


private IEnumerable<ValidationResult> SimpleObject(object input)
{
return from property in TypeDescriptor.GetProperties(input).Cast<PropertyDescriptor>()
from attribute in property.Attributes.OfType<ValidationAttribute>()
where !attribute.IsValid(property.GetValue(input))
select new ValidationResult
(
attribute.FormatErrorMessage(string.Empty),
new[] { property.Name }
);
}

private IEnumerable<ValidationResult> IEnumerableObject(object input)
{
List<ValidationResult> validateResults = new List<ValidationResult>();
foreach (object obj in (input as IEnumerable))
{
validateResults.AddRange(Validate(obj));
}
return validateResults as IEnumerable<ValidationResult>;
}
}

/NTN

Avatar for Paul Hiles Paul Hiles wrote on 26 Apr 2012

@NTN - Thanks for your work. Very useful. Will look at incorporating this plus support for nested complex types in the next release.

Avatar for tollin tollin wrote on 19 Aug 2012

Is that works in Silverlight solution?
if so, i don't know why that haven't take any effect in my solution;
[ServiceContract]
public interface IWCFService
{
[OperationContract]
void DoWork();

[OperationContract]
Person GetPerson();
}


[DataContract(Namespace = "http://schemas.devtrends.co.uk/example/data";)]
public class Person
{
[DataMember]
[Required]
public string ThirdPartyIdentifier { get; set; }

[DataMember]
[Required]
[StringLength(500, MinimumLength = 5)]
public string Description { get; set; }


}


these are the serivce implement class and the entity class.
and this is a silverlight 4 project。
can anyone tell me the reason.

Avatar for if you have lists, this does not work if you have lists, this does not work wrote on 25 Jan 2013

I have a propoerty on my contract of type:

public List<RewardDetail> ShoppingCart { get; set; }


however the nested list does not validate. Even with the patch it does not inherit directly.

Any ideas?

Avatar for Anders Finn Jørgensen Anders Finn Jørgensen wrote on 29 May 2013

Nice post by and nice hack by Ngoc

To the last comment:
Do use List<RewardDetail> use RewardDetail[ ]

Change NTN's method:
public bool IsCollection(object o)
{
return typeof(ICollection).IsAssignableFrom(o.GetType())
|| typeof(ICollection<>).IsAssignableFrom(o.GetType());
}

To:

public bool IsCollection(object o)
{
return typeof(ICollection<>).IsAssignableFrom(o.GetType())
|| typeof(ICollection<>).IsAssignableFrom(o.GetType())
|| o.GetType().IsArray;
}

The last line is the difference. It worked for me.