1) Cas concret

Prenons donc un service WCF retournant un "Country". Ci-dessous le contrat et l'implémentation:
[ServiceContract]
public interface ICountryService
{
    [OperationContract]
    Country GetCountry(string code);
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class MyCountryService : ICountryService
{
    public Country GetCountry(string code)
    {
        return CountryDAL.GetCountry(code);
    }
}

2) Ajout de la validation des inputs

Il est possible assez facilement d'ajouter des validations/modifications des paramètres d'entée dans un service WCF au niveau du serveur (et/ou au niveau du client). Pour se faire nous allons implémenter l'interface IParameterInspector. Nous allons pouvoir vérifier que l'utilisateur a bien envoyé un bon code de pays connu par notre système.
public class CountryCodeValidation : IParameterInspector
{
    public void AfterCall(string operationName, object[] outputs, 
                        object returnValue, object correlationState)
    {
    }
    public object BeforeCall(string operationName, object[] inputs)
    {
        string code = inputs[0] as string;
        if (string.IsNullOrEmpty(code) 
            || !new[] { "BE""FR""UK""USA" }.Any(elt => elt == code))
            throw new FaultException("Code inexistant");
        return null;
    }
}

3) Liaison de notre validation à l'appel de la méthode

Nous devons maintenant "lier" notre classe de validation d'input avec notre service. Pour ce faire, nous avons en premier à implémenter un Attribute que l'on positionnera au dessus de la méthode. En réalité, nous rajoutons via cet attribut un nouveau "behavior" sur notre operation. Nous devons donc implémenter l'interface IOperationBehavior.
public class ServerCountryValidationAttribute : AttributeIOperationBehavior
{
    public void AddBindingParameters(OperationDescription operationDescription, 
                                        BindingParameterCollection bindingParameters)
    {
    }
    public void ApplyClientBehavior(OperationDescription operationDescription, 
                                        ClientOperation clientOperation)
    {
    }
    public void ApplyDispatchBehavior(OperationDescription operationDescription, 
                                        DispatchOperation dispatchOperation)
    {
        CountryCodeValidation validation = new CountryCodeValidation();
        dispatchOperation.ParameterInspectors.Add(validation);
    }
    public void Validate(OperationDescription operationDescription)
    {
    }
}
Nous lions donc bien notre méthode avec cet attribut:
[ServiceContract]
public interface ICountryService
{
    [OperationContract]
    [ServerCountryValidation]
    Country GetCountry(string code);
}
Nous pouvons également effectuer cet appel au niveau du client (si celui-ci est en .NET). Ceci évite un appel vers le service en cas d'échec. Pour ce faire, nous allons implémenter dans la classe ServerContryValidationAttribute  la méthode ApplyClientBehavior (ci-dessous et ci-dessus).
public void ApplyClientBehavior(OperationDescription operationDescription, 
                                                ClientOperation clientOperation)
{
    CountryCodeValidation validation = new CountryCodeValidation();
    clientOperation.ParameterInspectors.Add(validation);
}
Nous pouvons donc ajouter la validation dans le client comme montré ci-dessous:
CountryServiceClient client = new CountryServiceClient();
client.Endpoint.Contract
                .Operations
                .First(elt => elt.Name == "GetCountry")
                .Behaviors
                .Add(new ClientCountryCodeValidation());

4) Interception de l'appel de la méthode du service

Il est possible de "catcher" l'exécution d'une méthode et d'altérer les objets en entrée,etc. Pour ce faire, il faut implémenter l'interface IOperationInvoker. Dans l'exemple ci-dessous, nous allons mettre en mémoire les Country déjà appelés afin de réduire les appels DB.
public class CountryCaching : IOperationInvoker
{
    IOperationInvoker baseInvoker;
    Dictionary<stringobject> cache = new Dictionary<stringobject>();
    public CountryCaching(IOperationInvoker invoker)
    {
        this.baseInvoker = invoker;
    }
    public object Invoke(object instance, object[] inputs, out object[] outputs)
    {
        outputs = new object[0];
        string code = inputs[0] as string;
        object value;
        if (!this.cache.TryGetValue(code, out value))
        {
            value = this.baseInvoker.Invoke(instance, inputs, out outputs);
            this.cache.Add(code, value);
        }
        return value;
    }
    public object[] AllocateInputs()
    {
        return this.baseInvoker.AllocateInputs();
    }
    public IAsyncResult InvokeBegin(object instance, object[] inputs,
                                                AsyncCallback callback, object state)
    {
        return this.baseInvoker.InvokeBegin(instance, inputs, callback, state);
    }
    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
    {
        return this.baseInvoker.InvokeEnd(instance, out outputs, result);
    }
    public bool IsSynchronous
    {
        get { return this.baseInvoker.IsSynchronous; }
    }
}
Nous devons après modifier dans notre classe ServerContryValidationAttribute la méthode ApplyDispatchBehavior.
public void ApplyDispatchBehavior(OperationDescription operationDescription, 
                                                   DispatchOperation dispatchOperation)
{
    CountryCodeValidation validation = new CountryCodeValidation();
    dispatchOperation.ParameterInspectors.Add(validation);
    dispatchOperation.Invoker = new CountryCaching(dispatchOperation.Invoker);
}
Voici donc expliqué en quelques mots comment modifier des paramètres ou altérer l'invocation de la méthode dans le service. Le prochain article se portera sur la modification des couches d'identification.