Introduction

Chargement d'objets grace à Load et LoadQuery


Un brin de théorie



Dans la plupart des cours de ce site, lorsque nous avions besoin d'accéder à l'object model de SharePoint, nous le faisions très facilement. On ouvrait un SPSite, un SPWeb et on utilisait des SPList, SPListItem,... très facilement. Cependant, cela ne peut pas se passer de cette manière lorsque nous essayons d'accéder à SharePoint depuis un autre endroit que le serveur.

En fait, c'est légèrement plus complexe que ça. Dans le cas d'une feature par exemple. Celle-ci va être déployée sur le serveur et quand elle sera "exécutée", ce sera par le serveur lui-même, donc même si c'est un utilisateur de Tombouctou qui utilise le site SharePoint localisé à Bruxelles, la feature sera toujours exécutée sur et par le serveur et pourra donc accéder à l'object model normalement.

Imaginons maintenant le cas où l'utilisateur de Tombouctou n'utilise plus directement le site mais une application WPF destinée à afficher des informations sur une liste ou un site SharePoint. Là ce n'est plus pareil. Effectivement, cette fois, c'est l'application qui va lancer des requêtes vers le site qui sera chargé de renvoyer ces informations dans un certain format pour que l'application sache les interpréter et les afficher de la manière adéquate.

Dans la version 2007 de SharePoint, nous avions recours au WebService pour réaliser ces opérations. Nous envoyions du XML au WebService qui se chargeait de l'interpréter et de renvoyer les résultats adéquats. Dans la version 2010, cela ne va plus se passe exactement de la même façon.

Effectivement, SharePoint met maintenant le client object model à notre disposition. Ainsi, nous allons pouvoir travailler avec des objet .NET pour exploiter l'object model de SharePoint presque de la même façon que celle que nous aurions utilisée si nous étions sur le serveur lui-même. Bien entendu, étant donné que nous ne sommes pas sur le serveur, vous vous doutez qu'il va y avoir beaucoup de points à prendre en compte.

La technique utilisée n'est pas fondamentalement différente que pour la version 2007. Le fait est que pour la version 2010, une couche a été faite permettant de faire l'abstraction entre le JSON et le XML et de présenter directement les données sous forme de classe .NET. Effectivement, derrière les planches, le processus reste presque le même. Notre application client va envoyer des informations XML au serveur qui va les traiter et renvoyer un résultat au format JSON. Ce résultat va ensuite être converti en objet .NET que nous allons pouvoir exploiter à notre guise. Mais, trêve de bavardage sur la théorie, passons à la pratique.

En pratique



Pour illustrer notre exemple, nous allons développer une petite application WPF qui va nous permettre de montrer les différents exemple abordés dans ce cours. Ce cours étant une sorte d'introduction au client object model, nous n'allons pas aborder des sujets tels que l'ajout d'élément dans une liste, la modification d'un élément ou d'une liste,... Nous nous contenterons de vous expliquer comment récupérer des informations de et sur une liste. Les autres détails seront abordés dans d'autres cours.

Commencez par ouvrir Visual Studio 2010 et créez un nouveau projet de type WPF Application que vous nommerez AreaProg.WPF.MyFirstREST :



Ne vous en faites pas si vous ne vous y connaissez pas en WPF, c'est également mon cas, mais nous n'aborderons aucun concept important dans ce cours (sur le WPF) donc cela ne vous posera pas de problème. Dans le code XAML de votre application, insérez cette ligne entre l'élément Grid :



Ici, nous ajoutons simplement une liste à notre application pour que celle-ci affiche les données que nous récupérons. Nous ne personnaliserons pas plus notre application, car ces cours sont axés sur SharePoint et non WPF, donc cette application ne nous sert que de support.

La première chose à faire pour utiliser le client object model de SharePoint consiste à ajouter les DLL requises pour pouvoir l'utiliser. Pour ce faire, cliquez avec le bouton droit de votre souris sur le dossier References de votre projet et cliquez sur Add reference. Les fichiers dll à ajouter se trouvent respectivement à l'emplacement C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.Client.dll et C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\ISAPI\Microsoft.SharePoint.Client.Runtime.dll :



Ces deux DLL vont nous permettre d'utiliser le client object model de SharePoint, mais il reste un détail à régler. Par défaut, Visual Studio 2010 crée l'application WPF et la base sur le Framework 4.0 Client Profile. Ce profil va nous empêcher d'utiliser la référence System.Web qui est requise pour pouvoir utiliser le client object model SharePoint. Cliquez donc avec le bouton droit de votre souris sur votre projet et choisissez Properties. Dans la fenêtre qui apparait, choisissez .NET Framework 4 dans la liste déroulante en dessous du libellé Target framework. Répondez ensuite Yes à la boite de dialogue pour que Visual Studio 2010 ferme et recharge votre projet avec le bon Framework.

Passons maintenant à la programmation. Cliquez avec le droit de votre souris sur le fichier MainWindow.xaml et choisissez View Code. C'est dans ce fichier, que nous allons taper nos lignes de code exploitant le client object model SharePoint. Avant de commencer, nous allons devoir ajouter ces deux directives :



Ici, nous choisissons d'utiliser le préfixe SP pour désigner le namespace que nous référençons. Nous faisons cela car le namespace Microsoft.SharePoint.Client possède certains noms de classe communs avec d'autres namespace. Nous choisissons donc d'utiliser le préfixe SP pour éviter toute confusion.

Avant de poursuivre, nous allons devoir nous étendre encore un peu sur de la théorie, effectivement, mieux vaut ne pas foncer tête baissée pour éviter les mauvaises habitudes. Une grosse contrainte qu'il faut garder en tête est le fait que à chaque fois que nous voulons récupérer des données, celles-ci doivent passer par le réseau, ainsi, si on ne fait pas attention et qu'on récupère les données n'importe comment, nous pouvons faire passer une quantité importante de données inutiles et nous ralentirions toute la procédure. Heureusement, le client object model de SharePoint va nous permettre d'indiquer précisément les données que nous voulons récupérer.

Dans une application s'exécutant sur le serveur, quand nous appelons une méthode sur un objet SharePoint, celle-ci est immédiatement exécutée et le résultat renvoyé. Avec le client object model, cela sera différent. Effectivement, la ou les requêtes que nous voulons exécuter ne le seront que lorsque nous déciderons de le faire. Ainsi, nous pouvons appeler plusieurs méthodes à la suite, sans qu'elles soient exécutées, et ensuite demander l'exécution de toute ces méthodes d'une seule fois, cela évite ainsi les aller retour entre le client et le serveur. Nous allons tout de suite passer à la pratique pour voir cela. Tapez le code suivant dans le constructeur MainWindow directement après l'appel à la fonction InitializeComponent :



La première ligne de ce code permet de récupérer le contexte du site SharePoint qui se trouve à l'URL passée en paramètre. Sur le serveur, nous pourrions utiliser l'objet SPContext pour réaliser la même chose. Notre objet context va donc nous permettre de contenir le contexte du site SharePoint et c'est grâce à cet objet que nous pourrons accéder aux propriétés du site. Notre premier exemple va simplement nous afficher le titre du site web.

Nous continuons le code en récupérant la référence au site web en lui même dans l'objet web. Comme vous le voyez, ici nous utiliser un objet du type SP.Web, c'est donc bien la classe du client object model qui est utilisée. A ce stade de l'exécution du code, l'objet web est en fait vide. Effectivement, nous pouvons effectuer des opérations et récupérer ses propriétés mais tant que nous ne demandons pas à l'application d'exécuter la requête, tous les objets utilisés ne contiendrons aucune référence. Ce qui est formidable, c'est que malgré cela, il sera possible de les utiliser tout à fait normalement. Le processus d'exécution saura comment gérer cela.

Nous appelons ensuite la méthode Load du contexte pour charger les propriétés de l'objet. Vous allez voir que la méthode Load et LoadQuery seront très souvent utilisées. Repensons maintenant à la contrainte dont nous avons parlé précédemment. Celle concernant la taille des données transférées sur le réseau. Dans notre exemple, nous ne voulons qu'afficher le titre du site en cours, il serait donc totalement inutile d'envoyer une requête nous renvoyant l'objet web avec toutes ses propriétés étant donné que nous n'en utiliserons qu'une seule. Nous allons donc utiliser une expression lambda qui va nous permettre de définir quelle propriété nous voulons récupérer.

La méthode Load attend donc deux paramètres. Le premier sera l'objet qui permettra de contenir la référence de l'objet chargé par la méthode. Le deuxième paramètre est une expression lambda qui va définir comment et quelles propriétés de l'objet seront récupérées. Ici, grâce à cette expression lambda, nous disons que nous n'allons récupérer que la propriétés Title de l'objet. Nous appelons alors la méthode ExecuteQuery qui va nous permettre d'envoyer la requête au serveur pour qu'il l'exécute. Le processus est tellement magnifique nous n'allons pas avoir besoin de traiter la réponse JSON car celle-ci sera automatiquement traitée et placer dans les objets adéquats, ici, l'objet web. Ainsi, quand nous affichera la MessageBox, voici ce qui apparaitra :



La propriété Title de l'objet web est donc bien accessible. Nous allons maintenant un peu regarder du coté du débogueur pour voir un petit peu ce qui se passe en arrière plan. Mettez donc un point d'arrêt aux quatre dernières lignes de votre code (en cliquant dans la marge à gauche) et exécutez votre programme. Nous allons ici regarder l'état de l'objet web au fur et à mesure de notre exécution. Voici l'affichage du débogueur lorsqu'ils rencontre la ligne :





Pour l'instant, rien d'étonnant, l'objet vaut null car on ne l'a pas encore initialisé ni déclaré. Exécutez maintenant la ligne de code montrée précédemment en appuyant sur F5 et regardez le déboguer :



Cette fois l'objet web contient bien une référence, mais si vous y regardez de plus près vous remarquerez que toutes les propriétés de cet objet lance une exception de type 'Microsoft.SharePoint.Client.PropertyOrFieldNotInitializedException'. Comme son nom l'indique, cette exception signifie que bien que l'objet web contient une référence, aucun des propriété de celui-ci n'a été initialisée. Ceci étant évidemment parce qu'à l'heure actuelle, il n'y a toujours aucune requête envoyée au serveur.

Si nous avançons encore d'une ligne d'exécution, rien ne changera dans le débogueur. Effectivement, la fonction Load va juste permettre d'indiquer la manière de charger l'objet web. Exécutions maintenant la ligne provoquant l'exécution de la requête. Le débogueur affichera maintenant :



A première vue rien n'a changé, toute les propriétés lanceront encore la même exception que toute à l'heure. Mais si on y regarde d'un peu plus près, vous remarquerez que la propriété Title, elle, ne va plus lancer cette exception et qu'elle contient bien la bonne valeur. Cela est donc réalisé grâce à la fonction Load qui a indiqué que seule la propriété Title de l'objet web devra être récupérée. Cela est un gain de performance énorme par rapport au renvoi de toute les propriétés de l'objet. La réponse JSON est beaucoup plus légère, elle est donc envoyée beaucoup plus rapidement et traitée plus efficacement.

Récupération d'une collection



Actuellement, nous n'avons vu que la manière de récupérer une propriété d'un objet, nous allons maintenant voir comment récupérer une collection d'élément. Remplacez donc le code précédent par celui-ci :



Si vous exécutez ce code, vous obtiendrez :



Nous récupérons donc toutes les listes du site et nous les affichons accompagnées de leur Id. La première question qui peut vous venir à l'esprit serait de vous demander pourquoi nous n'utilisons pas la fonction Load pour charger l'objet web. En fait, nous utilisons l'objet web uniquement pour récupérer les listes qui y sont attachées. Techniquement, nous ne récupérons pas une propriété de cet objet mais plutôt une collection d'objet. Nous n'avons donc pas besoin de charger l'objet web mais bien cette collection. Ainsi, nous commençons par stocker la collection de listes dans un objet du type ListCollection. Nous continuons ensuite en appelant la fonction Load pour indiquer de quelle manière seront chargées les listes.

Ici, comme il s'agit d'une collection d'objet, nous ne pouvons pas directement dire que nous récupérons la propriétés lists.Title, nous devons utiliser la fonction Include qui va permettre d'indiquer quelle propriétés des objets de la collection seront récupérées. Ici, dans cette fonction, nous indiquons que seules les propriétés Title et Id devront être renvoyées.

Nous appelons alors la fonction ExecuteQuery pour envoyer la requête au serveur. Une fois que nous la recevons, nous bouclons sur la collection de liste et nous affichons le titre et l'Id de chaque élément. C'est aussi simple que ça.

Définition de critère de recherche



Étant donné que nous pouvons utiliser des expressions lambda, il semble logique de pouvoir utiliser la fonction Where pour choisir quelle données seront récupérées. Effectivement, c'est possible, voyez par exemple le code suivant :



Ce code est le même que dans l'exemple suivant à part que nous utilisons, en plus de la fonction Include, la fonction Where pour définir que seules les listes qui ne sont pas invisibles seront récupérées. Effectivement, la propriété Hidden indique que la liste est visible (false) ou non (true). Vous remarquerez que bien que les valeurs des propriétés ne sont pas initialisée avant la commande ExecuteQuery, il est quand même possible d'effectuer des comparaisons dessus. Une chose qui est impossible, c'est effectuer des opérations dessus. Ainsi, vous pourriez vous dire que le code suivant :



Renverrait toutes les listes dont le nom contient plus de 5 caractères. En fait, ce que ce code renverrait serait ceci : "The 'Length' member cannot be used in the expression.". Effectivement, si la valeur de la propriété Title n'est pas encore initialisée, il n'est pas possible d'en récupérer la longueur via la propriété Length, puisque celle-ci ne sera pas initialisée.

Récupération de données en chaine



Imaginons maintenant que nous voulions récupérer le titre d'une liste ainsi que le titre de tous les champs qu'elle contient. L'idée première serait de charger la liste puis ensuite de charger les éléments. Le problème que cela nous pose est que cela nécessite l'envoi de 2 requêtes, nous allons donc utiliser le code suivant pour réaliser cette opération en une seule :



Nous commençons par déclarer un objet nommé web qui contiendra une référence au site web actuel. Nous allons ensuite récupérer la référence à une liste qui se nommée "Test Linq" (nous l'avons utilisé dans un autre cours). Une fois que nous avons cette liste, nous allons définir quelles données de celle-ci nous voulons récupérer. Nous indiquons d'abord que nous voulons récupérer sa propriété Title. Ensuite, le plus simplement du monde, nous indiquons que nous voulons récupérer la collection de champ qu'elle contient en passant par la collection Fields. Et comme tout à l'heure, nous indiquons que nous ne voulons pas récupérer toutes les propriétés des champs mais juste leur titre, nous utilisons donc la fonction Inlcude pour indiquer la propriété que nous voulons récupérer. Nous utilisons même la fonction Where pour indiquer que nous ne voulons récupérer que les champs qui ne sont pas cachés. Ensuite, nous utilisons simplement l'objet list pour afficher le nom de la liste ainsi que le nom de tous ses champs, ce qui, au final, donnera ceci :



Toutes les données requises sont bien récupérées.

LoadQuery



Nous avons déjà vu que pour charger des objets, nous devions utiliser la méthode Load. Il existe également la méthode LoadQuery qui remplit la même fonction mais proposant une utilisation légéremment différente. La première différence etre ces deux méthodes reside dans les paramètres qu'elles attendent. Effectivement, LoadQuery n'attend qu'une expression lambda alors que Load attend un objet recevant les propriétés chargées et une expression lambda.

La deuxième différence découle de la première. Étant donné que l'objet recevant les données renvoyées par le serveur n'est plus en paramètre, il doit forcément être la valeur de retour de la fonction. La grosse utilité de la fonction est que celle-ci va renvoyer les données non pas en un objet du client object model mais en une collection de type IEnumerable. Regardez par exemple ce code :



Ce code aurait très bien pu être écrit de cette manière :



Comme vous le constatez, la fonction LoadQuery renvoi bien une collection d'objet de type IEnumerable. Remarquez également que du coup, la collection context.Web.Lists.GetByTitle("Test Linq").Fields ne sera pas iniitialisée, pour le faire, il faudra utiliser la fonction Load.

Récupération d'éléments dans une liste



Nous allons maintenant nous pencher sur la meilleure manière de récupérer des éléments dans une liste. Voici un premier exemple pour récupérer tous les éléments d'un liste :



Nous allons devoir stocker la liste des éléments dans une collection du type ListItemCollection. Pour récupérer cette liste, nous allons utiliser la fonction GetItems de la liste à laquelle nous devons faire passer une requête CAML. Ici, nous faisons passer la requête la plus simple pour récupérer tous les éléments. Ensuite, nous chargeons la collection exactement de la même manière que tout à l'heure, nous appelons la fonction Load et nous indiquons que nous ne voulons récupérer que leur titre. Enfin, nous exécutons la requête et nous affichons le résultat dans la liste :



La requête récupère bien tous les éléments de la liste et les affiche. Étant donné que vous adorez les expressions lambda et que vous détestez le CAML, vous vous dites certainement que pour récupérer tous les éléments dont le nom est "akargi", il suffit de faire ceci :



Premièrement ce code ne fonctionne pas et deuxièmement, il fonctionnerait même que cela serait une très mauvaise façon de faire la chose. Effectivement, avec une telle requête, deux extractions d'élements seraient exécutées. Effectivement, la première consisterait à récupérer les éléments correspondant à la requête CAML. Une fois ces éléments récupérés, il faudrait alors que l'expression lambda soit évaluée et ainsi extraire tous les éléments renvoyés par la requête CAML ayant un titre égal à "akargi". En terme de performance, c'est désastreux. La bonne technique à adopter est donc la suivante :



Exécutez maintenant votre programme pour voir le résultat suivant apparaître :



L'élément portant le nom de akargi est donc correctement et efficacement récupéré.

Ce qu'il faut retenir de ce cours est... tout. Effectivement, utiliser une application en la connectant à SharePoint implique de passer par le réseau, qui est une ressource "limitée". Il faut donc sans cesse penser à optimiser nos requêtes en ne récupérant que les données nécessaires. Il faut également veillez à ne pas exécuter plus de requêtes que nécessaire. Comme nous l'avons vu dans ce cours, il serait dommage de faire 2 requêtes pour récupérer une liste et ses champs alors que cela peut être fait en une seule requête.