Dot.Blog

C#, XAML, Xamarin, UWP/Android/iOS

Modifier le Timeout des Wcf Ria Services

[new:4/02/2011]Les Wcf Ria Services sont très souples, ils permettent une large gamme de personnalisations, notamment celle du Tmeout des communications.

Timeout

Lorsqu’une application Silverlight fait un appel à un service Wcf Ria Services pour obtenir des données la communication qui s’établie est asynchrone. Je n’entrerais pas dans ces détails aujourd’hui mais il est nécessaire de le préciser. Car cela signifie qu’il n’y a pas vraiment de “tuyau” entre l’application et le serveur, en tout cas aucune communication permanente comme on pourrait le concevoir en client/serveur sur un  réseau d’entreprise.

La tuyauterie existe bien, c’est WCF d’ailleurs, mais le plombier est un gars bizarre qui installe les tuyaux uniquement quand vous ouvrez le robinet pour demander de l’eau et qui démonte immédiatement la tuyauterie jusqu’à temps que la réserve d’eau renvoie de l’eau en réponse à votre demande, moment où il réinstalle (temporairement) tous les tuyaux pour que l’eau vous arrive, et ainsi de suite...

Un monde physique qui fonctionnerait de cette manière serait bien étrange... Mais en informatique rien n’est impossible puisque tout est virtuel !

De fait, lorsqu’une application Silverlight demande des données à un serveur exposant un service de type Wcf Ria Services il y a établissement d’une communication au moment de la demande puis plus grand chose jusqu’à ce que le serveur réponde, s’il répond.

Dans la réalité c’est un peu plus compliqué que cela, vous vous en doutiez, WCF est une mécanique assez complexe qui créée des canaux de communication restant virtuellement en place jusque ce que la réponse arrive. Mais WCF n’attend pas indéfiniment. Passé un certain délai il démonte vraiment la tuyauterie !

C’est à ce moment là, si la réponse n’est pas arrivée bien sur, que se déclenche une erreur de Timeout côté client (application Silverlight donc). Un dépassement de délai. Mais quel délai ?

Par défaut les WCF Ria Services utilisent un Timeout de 1 minute. Cela semble un choix très raisonnable. Toute requête qui dépasse ce temps est à revoir ou à séparer en plusieurs petites requêtes plus rapides. C’est une question d’efficacité.

Un petit plus long s’il vous plait M. Cadbury !

Bon, des requêtes plus longues qu’une minute c’est mal. Ok. Mais parfois on ne peut pas faire autrement. La requête ne peut pas être découpée et elle prend beaucoup de temps, on n’a pas la main sur le serveur Ria Services pour atomiser la requête en petites unités, etc...

Mais heureusement, il est possible de modifier le Timeout. Toute la question est de savoir où brancher la dérivation dans cette tuyauterie virtuelle plus complexe que les célèbres tuyaux d’aération du Centre Pompidou !

Vous me connaissez, je l’ai déjà dis, j’ai horreur de la plomberie. De la vraie, et pourtant je suis un bon bricoleur, autant que de la virtuelle (sécurité, protocoles de communications...). Tous ces machins me filent des boutons.

Je ne vous mentirai donc pas en vous disant que j’ai dépiauté sous Reflector le source de WCF pour trouver une solution,  non, je l’ai repiquée à un plombier certifié, Kyle McClellan, un gars de l’équipe de développeurs des WCF Ria Services... Quitte à recopier autant choisir ce qui ce fait de mieux, c’est comme à l’école (il fallait être idiot pour se mettre à côté du dernier de la classe le jour d’une interro sur laquelle on ne se sentait pas très au point...).

Ce charmant développeur au patronyme qui fleure bon le vrai whisky pur malt ne doit pas abuser lui-même de la boisson de ses ancêtres écossais car sa solution est très lucide et assez simple (une fois qu’on sait comment faire, tout est là). Cela se décompose en deux étapes que je présente ci-dessous.

Une classe outil pour modifier le Timeout

C’est de la plomberie donc il faut des outils... La bête ne se livrera pas comme ça à vous sans un peu de code bien corsé. Donc dans un premier temps il faut déclarer dans un coin la méthode suivante :

 

/// <summary>
/// Utility class for changing a domain context's WCF endpoint's
/// SendTimeout. 
/// </summary>
public static class WcfTimeoutUtility
{
  /// <summary>
  /// Changes the WCF endpoint SendTimeout for the specified domain
  /// context. 
  /// </summary>
  /// <param name="context">The domain context to modify.</param>
  /// <param name="sendTimeout">The new timeout value.</param>
  public static void ChangeWcfSendTimeout(DomainContext context, 
                                          TimeSpan sendTimeout)
  {
    PropertyInfo channelFactoryProperty =
      context.DomainClient.GetType().GetProperty("ChannelFactory");
    if (channelFactoryProperty == null)
    {
      throw new InvalidOperationException(
        "There is no 'ChannelFactory' property on the DomainClient.");
    }
 
    ChannelFactory factory = (ChannelFactory)
      channelFactoryProperty.GetValue(context.DomainClient, null);
    factory.Endpoint.Binding.SendTimeout = sendTimeout;
  }
}

Ce n’est pas grand chose, mais ça ne s’invente pas...

Deux options d’utilisation

La première consiste à appeler la méthode définie ci-dessus sur la variable de contexte avant que la moindre opération ne soit effectuée dessus. Je n’aime pas cette méthode car quel que soit l’endroit que vous choisirez il y aura toujours la possibilité qu’un petit malin (peut-être vous d’ailleurs) n’utilise le contexte ailleurs et avant dans une future modification du code...

Je préfère les méthodes sures et imparables.

La seconde méthode consiste plutôt à utiliser l’extensibilité prévue par Microsoft. Dans le code généré côté client (qui est caché par défaut dans VS mais que vous pouvez afficher en cliquant sur l’icône ad  hoc du volet d’inspection du projet) les déclarations sont prévues en “partial” ce qui permet d’étendre le code sans créer de nouvelle classe. Et notamment la méthode OnCreate du contexte est déclarée en partial mais non utilisée par le code généré, elle est juste là pour qu’on puisse justement s’en servir dans un code partiel.

C’est donc par cette porte dérobée qu’il est préférable d’insérer l’appel à la méthode changement de Timeout.

Voici un modèle de code à adapter (selon les noms de classes et les namespaces) :

namespace SampleNamespace.Web.Services
{
  public partial class MyDomainContext
  {
    partial void OnCreated()
    {
      TimeSpan tenMinutes = new TimeSpan(0, 10, 0);
      WcfTimeoutUtility.ChangeWcfSendTimeout(this, tenMinutes);
    }
  }
}

Le problème est justement de remplacer les noms correctement sinon rien ne marchera, au mieux le code ne fera rien (il ne sera appelé par le contexte), au pire vous n’arriverez même pas à compiler.

D’abord ce code doit être déclaré côté client, dans l’application Silverlight donc. Pas sur le serveur.

Ensuite il faut modifier correctement les noms suivants :

“SampleNamespace.Web.Services” : c’est l’espace de nom dans lequel votre contexte est défini, si vous avez un doute, afficher les fichiers cachés du projet et dans le dossier “Generated_code” vous trouverez “xxx.Web.g.cs”, le code source du proxy du service Ria que VS a créé pour vous.

“MyDomainContext” : c’est l’autre nom qui doit être modifier, il doit correspondre exactement au nom du contexte généré.

Dans l’exemple le Timeout est fixé à 10 minutes. C’est un exemple hein... Ne laissez pas la possibilité qu’un utilisateur puisse attendre dix minutes une requête, il aura zappé de site bien avant Smile. Et s’il s’agit d’une application d’entreprise, débranchez votre téléphone, jetez votre portable à la poubelle, changez de bâtiment et mettez une fausse barbe (ou rasez celle que vous portez habituellement). Le port d’un casque de chantier me semble un plus pour votre sécurité.

Bien entendu, si une requête doit vraiment atteindre les dix minutes vous aurez tout intérêt à prévoir plus qu’une jolie animation...

Donc attention, prudence. La valeur par défaut d’une minute est déjà énorme...

Conclusion

Dépasser la minute de Timeout pour une requête est un choix que je ne ferai dans aucune application, sauf si on me le demande avec un gros chèque ou un colt Cobra sur la tempe (avis aux clients, je préfère la première solution Smile).

Toutefois dans certains cas il peut être intéressant de se protéger contre un Timeout intervenant rarement (quand les serveurs sont très chargés, quand le débit de la ligne est mauvais, etc).

A vous de voir. Maintenant vous savez comment faire...

Stay Tuned !

blog comments powered by Disqus