Frédéric Colin
WCF : L’extensibilité par la pratique – La base
Windows Communication Foundation offre des possibilités d’extensibilité intrinsèques très souvent méconnues. C’est pourquoi j’ai décidé d’en explorer un aspect, l’invocation, au travers d’un exemple très simple.
Par Frédéric Colin publié le 30/08/2009 à 23:14, lu 1313 fois, 6 pages
 3 | Exemple d’utilisation d’une extensibilité d’invocation
Maintenant que nous avons vu les différentes étapes logiques pour créer un comportement d’invocation, voici un petit exemple simple qui servira de base de travail au prochain article. La solution est donc classiquement composée des projets suivants :
 
/content/72fdf4e8-3573-4f9f-a390-4d5428c9a249/image5.png
 
Le service permettra des opérations simples sur des entiers et sera exposé via le binding « NetTcpBinding » sur le port « 1234 ». Dans cet exemple, tout sera paramétré de manière déclarative. Le proxy WCF client sera généré automatiquement à l’aide de Visual Studio. Un EndPoint de type MEX sur le protocole tcp (« MexTcpBinding ») sera ajouté afin d’exposer les contrats des services.
Le métier développé pour illustrer la création d’une extensibilité d’invocation, permet lors de l’appel à une opération et en fonction du nom élémentaire de l’opération et du type de paramètre retourné, d’ajouter la valeur entière 10000 à la valeur initialement calculée et renvoyée.
Voici donc le code de l’implémentation de l’interface « IOperationInvoker » :

public class FacadeOperationInvoker : IOperationInvoker

{

    private IOperationInvoker invoker;

    private OperationDescription operationDescription;

 

    public FacadeOperationInvoker(IOperationInvoker invok, OperationDescription opDescription)

    {

        this.invoker = invok;

        this.operationDescription = opDescription;

    }

 

    #region IOperationInvoker Members

 

    public object[] AllocateInputs()

    {

        return invoker.AllocateInputs();

    }

 

    public object Invoke(object instance, object[] inputs, out object[] outputs)

    {

        object returnedValue = invoker.Invoke(instance, inputs, out outputs);

 

        if (returnedValue != null)

        {

            if (operationDescription.Name.StartsWith("Add"))

            {

                return Convert.ToInt32(returnedValue) + 10000;

            }

        }

 

        return returnedValue;

    }

 

    public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)

    {

        return invoker.InvokeBegin(instance, inputs, callback, state);

    }

 

    public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)

    {

        return invoker.InvokeEnd(instance, out outputs, result);

    }

 

    public bool IsSynchronous

    {

        get { return invoker.IsSynchronous; }

    }

 

    #endregion

}

Vous noterez que le gros du « travail » a été réalisé sur la méthode « Invoke ». Les deux membres privés de type « IOperationInvoker » et « OperationDescription » sont passés au seul constructeur paramétré de la classe par le comportement d’injection dont le code suit :

public class FacadeOperationBehavior : Attribute, IOperationBehavior

{

    #region IOperationBehavior Members

 

    public void AddBindingParameters(OperationDescription operationDescription,

        System.ServiceModel.Channels.BindingParameterCollection bindingParameters)

    {

        return;

    }

 

    public void ApplyClientBehavior(OperationDescription operationDescription,

        System.ServiceModel.Dispatcher.ClientOperation clientOperation)

    {

        throw new NotImplementedException();

    }

 

    public void ApplyDispatchBehavior(OperationDescription operationDescription,

        System.ServiceModel.Dispatcher.DispatchOperation dispatchOperation)

    {

        IOperationInvoker invoker = dispatchOperation.Invoker;

        // Override default Behavior

        dispatchOperation.Invoker = new FacadeOperationInvoker(invoker, operationDescription);   

    }

 

    public void Validate(OperationDescription operationDescription)

    {

        return;

    }

 

    #endregion

}

Vous noterez que c’est l’appel à la méthode « ApplyDispatchBehavior » par la Runtime WCF, qui se chargera de l’injection. Elle crée une instance de notre classe d’invocation en lui passant en paramètre les informations sur l’ancien « invoker » créé par défaut (et qui sera utilisé pour réaliser l’action) et les informations sur l’opération en cours d’appel.
Il ne reste plus qu’à marquer les opérations nécessitant ce type de comportement spécifique avec l’attribut précédent et le tour est joué :

[ServiceContract()]

public interface IService1

{

    [OperationContract(Name = "Add2")]

    [THB.Sample.ServiceModel.Extensions.FacadeOperationBehavior()]

    int Add(int i, int j, int k, int l);

 

    [OperationContract(Name = "Add1")]

    [THB.Sample.ServiceModel.Extensions.FacadeOperationBehavior()]

    int Add(int i, int j, int k);

 

    [OperationContract()]

    [THB.Sample.ServiceModel.Extensions.FacadeOperationBehavior()]

    int Add(int i, int j);

 

    [OperationContract()]

    [THB.Sample.ServiceModel.Extensions.FacadeOperationBehavior()]

    int AddWithConstant(int i);

 

    [OperationContract()]

    [THB.Sample.ServiceModel.Extensions.FacadeOperationBehavior()]

    int Div(int i, int j);

 

    [OperationContract()]

    [THB.Sample.ServiceModel.Extensions.FacadeOperationBehavior()]

    int Minus(int i, int j);

 

    [OperationContract()]

    [THB.Sample.ServiceModel.Extensions.FacadeOperationBehavior()]

    int Mult(int i, int j);

}

Vous noterez au passage le renommage des méthodes surchargées au niveau opération via le paramètre « Name » du constructeur de la classe « OperationContract ». En effet, les surcharges ne sont pas gérées au niveau de la norme de description des services.
Voici le résultat de l’exécution de cette magnifique ;-) application :
 
/content/72fdf4e8-3573-4f9f-a390-4d5428c9a249/image6.png
 
 
» Démarrer une discussion