Maintenir l'état
L’un des principaux inconvénients de l’exemple précédent est qu’il récupère l’ensemble complet des résultats en une seule fois, ce qui n’est pas pratique lorsque les jeux de résultats sont volumineux. Il est plus pratique d’afficher l’ensemble des résultats sur plusieurs pages et de permettre à l’utilisateur de naviguer d’une page à l’autre.
C’est simple dans une application conventionnelle, où la connexion au serveur distinct est maintenue jusqu’à ce que l’utilisateur quitte l’application. Cependant, dans une implémentation web, cette exigence apparemment simple implique un niveau de complexité nettement plus élevé en raison de la nature sans état des pages web. L’une de ces complexités est que, chaque fois qu’une nouvelle page de résultats est affichée, la recherche initiale des enregistrements doit être relancée. Cela est contraignant pour le développeur web et potentiellement lent pour l’utilisateur.
Le serveur IMu fournit une solution à ce problème. Lorsqu’un objet handler est créé, un objet correspondant est créé sur le serveur afin de traiter la requête du handler : cet objet côté serveur se voit attribuer un identifiant unique par le serveur IMu. Lors d’une requête visant à obtenir davantage d’informations, l’identifiant unique peut être utilisé pour connecter un nouveau handler au même objet côté serveur, tout en conservant son état.
L’exemple suivant illustre la connexion d’un deuxième objet Module créé indépendamment au même objet côté serveur :
// Créer un objet module comme d'habitude Module first = new Module("eparties", session); // Lancer une recherche - ceci créera un objet côté serveur int[] keys = { 1, 2, 3, 4, 5, 42 }; first.FindKeys(keys); // Obtenir un jeu de résultats ModuleFetchResult result1 = first.Fetch("start", 0, 2, "SummaryData"); // Créer un second objet module Module second = new Module("eparties", session); /* * L'attacher au même objet côté serveur que le premier module. * C'est l'étape la plus importante. */ second.ID = first.ID ; // Obtenir un deuxième ensemble de résultats à partir de la même recherche ModuleFetchResult result2 = second.Fetch("current", 1, 2, "SummaryData") ;
Bien que deux objets Module totalement distincts aient été créés, ils sont tous deux liés au même objet côté serveur, puisqu’ils partagent la même propriété ID. Cela signifie que le deuxième appel Fetch accédera au même jeu de résultats que le premier appel Fetch. Notez qu’un indicateur current a été transmis au deuxième appel. L’état actuel est conservé dans l’objet côté serveur ; ainsi, dans ce cas, le deuxième appel à Fetch renverra les troisième et quatrième enregistrements du jeu de résultats.
Bien que cet exemple illustre l’utilisation de la propriété ID, il n’est pas particulièrement réaliste, car il est peu probable que deux objets distincts faisant référence au même objet côté serveur soient nécessaires dans le même extrait de code. Il est bien plus probable qu’il faille se reconnecter au même objet côté serveur lors de la génération d’une autre page de résultats. Cette situation implique la création d’un objet Module côté serveur (pour effectuer une recherche dans le module et renvoyer le premier jeu de résultats) lors d’une première requête, puis la reconnexion à ce même objet côté serveur (pour récupérer un deuxième jeu de résultats) lors d’une deuxième requête. Comme précédemment, cela s’obtient en attribuant le même identifiant à la propriété ID de l’objet de la deuxième page, mais deux autres éléments doivent être pris en compte.
Par défaut, le serveur IMu détruit tous les objets côté serveur à la fin d’une session. Cela signifie que, sauf instruction contraire explicite donnée au serveur, l’objet côté serveur peut être détruit lorsque la connexion depuis la première page est fermée. Pour indiquer au serveur de conserver l’objet côté serveur, il suffit de définir la propriété Destroy de cet objet sur false avant d’appeler l’une de ses méthodes. Dans l’exemple ci-dessus, le serveur reçoit l’instruction de ne pas détruire l’objet comme suit :
C#
Module module = new Module("eparties", session);
module.SetDestroy(false);
int[] keys = { 1, 2, 3, 4, 5, 42 };
module.FindKeys(keys);
VB
Le deuxième point est assez subtil. Lorsqu’une connexion est établie à un serveur, il est nécessaire de spécifier le port auquel se connecter. Selon la façon dont le serveur a été configuré, il peut y avoir plus d’un processus serveur à l’écoute des connexions sur ce port. Votre programme n’a aucun contrôle sur le choix du processus qui acceptera effectivement la connexion et traitera les requêtes. En temps normal, cela n’a aucune incidence, mais cela pose problème lorsqu’on tente de maintenir l’état en se reconnectant à un objet côté serveur préexistant.
Par exemple, supposons qu’il existe trois processus serveur distincts à l’écoute des connexions. Lorsque la première requête est exécutée, elle se connecte, de manière pratiquement aléatoire, au premier processus. Ce processus traite la requête, crée un objet côté serveur, effectue une recherche dans le module Personnes / Organisations à l’aide des termes fournis et renvoie le premier jeu de résultats. Le serveur reçoit l’instruction de ne pas détruire l’objet et transmet l’identifiant côté serveur à une autre page, qui récupère le jeu suivant de résultats issus de la même recherche.
Le problème survient lorsque la page suivante se reconnecte au serveur. Une fois la connexion établie, n'importe lequel des trois processus serveur peut l'accepter. Cependant, seul le premier processus maintient l'objet côté serveur correspondant. Si le deuxième ou le troisième processus accepte la connexion, l’objet ne sera pas trouvé.
La solution à ce problème est relativement simple. Avant que la première requête ne ferme la connexion à son serveur, elle doit signaler au serveur que les requêtes suivantes devront se connecter explicitement à ce processus. Pour ce faire, il suffit de définir la propriété Suspend de l'objet Session sur true avant de soumettre toute requête au serveur :
C#
Session session = new Session("server.com", 12345);
Module module = new Module("eparties", session);
session.SetSuspend(true);
module.FindKeys(keys);
VB
Le serveur traite une demande de suspension d’une connexion en se mettant à l’écoute des connexions sur un deuxième port. Contrairement au port principal, ce port est réservé à ce processus serveur spécifique. Cela signifie qu'une page suivante peut se reconnecter à un serveur sur ce deuxième port et avoir la garantie de se connecter au même processus serveur. Cela signifie que tout objet conservé côté serveur sera accessible via son identifiant. Une fois la requête terminée (dans cet exemple, il s’agissait d’un appel à FindKeys), la propriété Port de l’objet Session contient le numéro de port auquel se reconnecter :
C#
session.SetSuspend(true);
module.FindKeys(keys);
int reconnect = session.Port;
VB
Exemple
Pour illustrer cela, nous allons modifier la page de résultats très simple de la section précédente afin d’afficher la liste des noms trouvés par groupes de cinq enregistrements par page. À des fins de démonstration, nous fournirons de simples mécanismes Suivant et Précédent permettant à l’utilisateur de se déplacer dans les résultats, et nous conserverons le port auquel nous voulons nous reconnecter, l’identifiant de l’objet côté serveur et le rownum du premier enregistrement à afficher.
- Créez un objet
Sessionavec les paramètresHostetPort, puis établissez une connexion et définissez immédiatement la propriétéSuspendsur true pour indiquer au serveur que nous pouvons vouloir nous reconnecter :Session session = new Session(Host, Port);
session.Connect();
session.Suspend = true;
Cela garantit que le serveur écoute sur un nouveau port unique.
- Créez l’objet côté client
Moduleet définissez sa propriétéDestroysurfalse:Module parties = new Module("eparties", session);
parties.Destroy = false;
Cela garantit que le serveur ne détruira pas l’objet côté serveur correspondant à la fin de la session.
- Si nous avons des termes de recherche, nous effectuerons une nouvelle recherche. Si nous nous connectons à un nouveau module côté serveur, nous enregistrons la propriété
ModuleIDactuelle ; sinon, nous définissons la propriétéID:parties. Destroy = false ;
if (ID == null)
ID = parties.ID;
else
parties.ID = ID;
- Si nous n’avons pas de termes de recherche mais que nous voulons récupérer des données à partir d’un jeu de résultats existant, nous définissons simplement le
ModuleID:parties.ID = ID;
- Construire une liste de colonnes à récupérer :
static private String[] columns =
{
"NamFirst",
"NamLast"
};
- Si les paramètres de la fonction comprenaient un paramètre
rownum, nous récupérons les enregistrements à partir de celui-ci. Sinon, nous définissons une valeur par défaut derownumde1et commençons à partir de là :parties.ID = ID ;
Après cela, nous affichons les résultats. Pour les besoins de notre exemple de console, nous nous déconnectons manuellement du serveur, car nous n’avons pas besoin d’actualiser la page comme un navigateur web.
- Enfin, nous permettons à l’utilisateur d’avancer et de reculer dans les résultats. Pour ce faire, nous devons transmettre des informations sur notre connexion, notre position actuelle et le nombre de résultats.
Map value = new Map();
value.Add("ID", parties.ID);
value.Add("Hits", results.Hits);
value.Add("rownum", rownum);
return value;
Nous commençons alors une boucle afin de permettre à l’utilisateur d’avancer ou de reculer dans les résultats, ou de quitter :
input = Console.ReadKey();
if (input.Key == ConsoleKey.Q)
break;
if (input.Key == ConsoleKey.LeftArrow)
{
rownum -= count;
if (rownum < 1)
rownum = 1;
}
else if (input.Key == ConsoleKey.RightArrow)
{
if (rownum + count < Hits)
rownum += count;
}
else
continue;
result = ServerRequest(ID, rownum);
rownum = result.GetInt("rownum");
Hits = result.GetLong("Hits");

