Silverlight – WCF – Déploiement sur serveurs multiples, endpoint dynamiques

5 minutes read

Introduction

Lorsque l’on développe une application couplant Silverlight et services WCF, l’url de ces derniers est écrite dans le fichier ServiceClient.config embarqué dans l’application.

En phase de développement celà ne pose généralement pas de problèmes mais lorsque l’on souhaite déployer notre application il est souvent nécessaire de changer l’url des services.

Prenons le processus suivant en exemple :

  • Développement de l’application et des services sur le poste du développeur (localhost)
  • Déploiement de l’application et des services sur un serveur d’intégration
  • Phase de recette sur le serveur d’intégration
  • Déploiement de l’application et des services sur des serveurs distincts de pré-production
  • Validation client sur le serveur de pré-production
  • Déploiement de l’application et des services sur des serveurs distincts de production

On peut constater que durant tout le processus les services WCF changent quatre fois d’adresse.

Il y a plusieurs moyens de changer les adresses des services chacun ayant ses avantages et inconvénients.

L’un d’entre eux est d’avoir un fichier 4 profils de compilations sous Visual Studio chacun ayant son propre fichier ServiceClient.config. Celà pose quelques problèmes de maintenance et surtout nous oblige à récompiler l’application juste pour pouvoir la déployer sur un autre serveur.

Une autre solution consiste à renommer le xap en zip, extraire le zip, changer le contenu du fichier ServiceClient.config, recompresser le zip et le renommer en xap et enfin redéployer vers le bon serveur. L’avantage est qu’on ne recompile pas l’application mais le processus est quand même un peu trop manuel.

La solution que je vais vous présenter maintenant présente l’avantage de ne pas avoir à recompiler l’application et ne nécessite pas non plus de processus manuel pour chaque déploiement.

Création d’un ConfigurationManager

Afin cette méthode fonctionne nous allons devoir instancier dynamiquement nos services WCF car nous allons supprimer le fichier ServiceClient.config. Cependant nous avons toujours besoin de l’url racine de nos services WCF (exemple http://monsite.fr:5678/monapplication/services/). Si vous avez plusieurs services WCF hébergés dans la même application web cette méthode est alors d’autant plus intéressante.

Pour récupérer cette url, nous allons créer une sorte de ConfigurationManager personnalisé pour Silverlight.

La particularité de ce ConfigurationManager est que son fichier de configuration se situe sur le serveur hébergeant notre application Silverlight et n’est donc pas embarqué dans le xap.

configless

La capture ci-dessus montre où placer notre fichier de configuration (ici config.xml). L’extension de notre fichier de configuration est .xml et non .config car IIS empêche l’accès aux fichiers portant cette dernière extension (exemple avec les fichiers web.config).

Voici maintenant à quoi ressemble le fichier config.xml :

<?xml version="1.0" encoding="utf-8" ?>
<config>
  <appSettings>
    <add key="ServiceBaseUrl" value="http://127.0.0.1:1031/" />
  </appSettings>
</config>

Comme vous pouvez le voir il s’agit d’un ensemble clef/valeur. Ici nous n’avons renseigné qu’une seule clef, ServiceBaseUrl qui nous indique quelle adresse utiliser pour accéder à nos services.

Voici maintenant le code de notre ConfigurationManager :

/// <summary>
/// Configuration de l'application
/// </summary>
public static class ConfigurationManager
{
    private static IDictionary<string, object> _appSettings;
    public static event EventHandler<EventArgs> Initialized;

    /// <summary>
    /// Dictionnaire de configuration
    /// </summary>
    public static IDictionary<string, object> AppSettings
    {
        get
        {
            return _appSettings;
        }
    }

    /// <summary>
    /// Charge les données du fichier de configuration du serveur.
    /// </summary>
    /// <remarks>Processus asynchrone.</remarks>
    public static void Load()
    {
        var client = new WebClient();
        client.DownloadStringCompleted += DownloadStringCompleted;
        client.DownloadStringAsync(new Uri("config.xml", UriKind.Relative));
    }

    private static void DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (e.Error != null)
            return;

        // Nécessite System.Xml.Linq
        // Parsing du fichier de configuration.
        XDocument config = XDocument.Parse(e.Result);
        var appSettings = config
            .Descendants("appSettings")
            .Descendants("add")
            .Select(n =>
                new KeyValuePair<string, object>(
                    n.Attribute("key").Value,
                    n.Attribute("value").Value));

        _appSettings = new Dictionary<string, object>();

        // Enregistrement de valeurs dans le dictionnaire local.
        if (appSettings.Any())
            foreach (var a in appSettings)
                _appSettings.Add(a);

        // On informe de la fin du chargement de la configuration.
        if (Initialized != null)
            Initialized(null, new EventArgs());
    }
}

Il est important de noter que le processus de chargement est asynchrone (comme toute opération faisant appel au serveur en Silverlight).

Notre application sera donc incapable de faire appel à des webservices tant que le chargement de la configuration est en cours.

Maintenant nous allons modifier le fichier App.xaml.cs pour lancer le chargement de la configuration dès le début de l’application :

private void Application_Startup(object sender, StartupEventArgs e)
{
    var page = new MainPage();
    RootVisual = page;
    ConfigurationManager.Initialized += (s, a) => page.Initialize();
    ConfigurationManager.Load();
}

Ici le choix à été d’ajouter une méthode Initialize à notre MainPage servant à l’informer que la configuration est prête et qu’elle peut faire des appels aux services.

Nous aurions aussi pu désactiver la page le temps du chargement de la configuration de la manière suivante :

private void Application_Startup(object sender, StartupEventArgs e)
{
    var page = new MainPage();
    page.IsEnabled = false;
    RootVisual = page;
    ConfigurationManager.Initialized += (s, a) => page.IsEnabled = true;
    ConfigurationManager.Load();
}

Tout dépend de la manière dont fonctionne votre application. Si les appels aux services se font automatiquement sans intervention de l’utilisateur la première méthode est un bon choix. Si ces appels ne se font que sur intervention de l’utilisateur (exemple clic sur un bouton), alors la seconde méthode constitue un bon choix. Nous pourrions aussi avoir un BusyIndicator indiquant le chargement, ici tout dépend de votre application et de vos contraintes. A vous de choisir ce qui convient le mieux.

Instanciation dynamique des services WCF

Maintenant que nous savons où sont situés nos services WCF nous allons pouvoir les appeler. Ici j’ai créé une petite factory me renvoyant une instance configurée du proxy client WCF. D’autres façon de procéder sont bien sûr possibles, le principe reste néanmoins le même quelque soit son implémentation.

/// <summary>
/// Factory des services WCF.
/// </summary>
public static class ServiceHelper
{
    /// <summary>
    /// Construit et configure une instance de UserServiceClient.
    /// </summary>
    /// <returns>Instance de UserServiceClient.</returns>
    public static UserServiceClient GetUserServiceClient()
    {
        // Configuration du binding
        var binding = new BasicHttpBinding(BasicHttpSecurityMode.None)
        {
            MaxBufferSize = int.MaxValue,
            MaxReceivedMessageSize = int.MaxValue,
            Name = string.Format("BasicHttpBinding_{0}", "UserService")
        };

        // Construction du Endpoint.
        string baseUrl = ConfigurationManager.AppSettings["ServiceBaseUrl"] as string;
        string uri = string.Format("{0}{1}.svc", baseUrl, "UserService");
        var remoteAddress = new EndpointAddress(uri);

        // Instanciation du proxy WCF.
        var client = new UserServiceClient(binding, remoteAddress);

        return client;
    }
}

Appels aux services

L’appel aux services WCF est à présent presque identique à ce que vous pouviez faire avec le fichier ServiceClient.config.

// AVANT : var client = new UserServiceClient();
var client = ServiceHelper.GetUserServiceClient();

client.GetUserCompleted += GetUserCompleted;
client.GetUserAsync(42);

Conclusion

Vous pouvez dès lors copier votre xap sur tout vos serveurs sans récompiler votre application.

Tant que le fichier config.xml est présent dans le répertoire de votre application Silverlight, celle-ci pourra appeler les bons services.

Il est important de noter que ce fichier est accessible à tous, il est donc important de ne pas y mettre de détails sensibles.

J’espère que cet article vous sera utile et comme d’habitude vous pourrez trouver une application d’exemple sur mon skydrive.

Updated:

Leave a Comment