ProcessBatchData

Utilisation des requêtes batch

10/08/2009 2 444 lectures 1 commentaire 4.5/5 (2 votes) Sébastien Sougnez

La structure interne ainsi que le design d'AreaProg ont récemment été modifiés.
Suite à cela, le format de certains articles a été perturbé. Le problème est connu et en cours de résolution. Merci de votre compréhension.

Si vous avez déjà travaillé avec les listes SharePoint, vous vous êtes sûrement déjà posé la question de savoir comment réaliser des opérations en masse. Effectivement, la plupart du temps, vous n'avez besoin que de mettre à jour, ajouter ou supprimer qu'un seul élément. Cependant, il peut arriver que vous ayez des opérations à réaliser s'exécutant sur un grand nombre d'éléments. Dans ces conditions, il est impensable de faire cela en bouclant sur chaque élément. Nous devrons donc recourir à la méthode ProcessBatchData de l'objet SPWeb pour nous permettre d'effectuer des tâches batch. Ce type de tâches sont optimisées et s'exécutent beaucoup plus rapidement que des tâches normales.

Ajout d'éléments en masse

Dans ce premier exemple, nous allons tester deux manières différentes d'ajouter 2000 éléments dans une liste personnalisée. La première manière se basera sur l'object model et exécutera 2000 Items.Add sur la liste. La deuxième méthode consistera à exécuter une tache batch pour réaliser l'insertion. Pour les deux exemples, nous utiliserons un compteur permettant de mesurer la rapidité d'exécution des deux méthodes. Pour les besoins des exemples, nous disposons d'une liste personnalisée nommée Test. Commencez par créer une application console avec Visual Studio 2008. Ajoutez-y une référence à l'API SharePoint et tapez le code suivant :
using System;
using Microsoft.SharePoint;
using System.Diagnostics;

namespace AreaProgConsoleApplication {
	class Program
	{
		static void Main()
		{
			using (SPSite site = new SPSite("http://pc-de-zick"))
			{
				using (SPWeb web = site.OpenWeb())
				{
					Stopwatch sw = new Stopwatch();
					sw.Start();

					SPList list = web.Lists["Test"];

					for (int i = 0; i < 2000; ++i)
					{
						SPListItem it = list.Items.Add();

						it["Title"] = "Item #" + i;
						it.Update();
					}

					sw.Stop();
					Console.WriteLine("2000 items inserted in : " + sw.Elapsed.TotalMilliseconds);

					Console.Read();
				}
			}
		}
	}
} 
Ce code n'a rien de bien compliqué. Nous récupérons tout d'abord une référence à la liste Test pour ensuite boucler 2000 fois sur la variable i et exécuter 2000 opérations Add sur les éléments de la liste. Nous modifions également le titre de ces éléments pour enfin sauvegarder les modifications. Nous utilisons également un StopWatch, qui n'est rien d'autre qu'un chronomètre, pour voir en combien de temps ce sont effectués les 2000 ajouts. Voiciles résultats obtenus par cette méthode :
Image
Cette valeur étant en millisecondes, cela veut dire que les 2000 ajouts se sont effectués en presque 20 secondes. Remplacez maintenant le code précédent par :
using System;
using System.Text;
using Microsoft.SharePoint;
using System.Diagnostics;

namespace AreaProgConsoleApplication 
{
	class Program
	{
		static void Main()
		{
			using (SPSite site = new SPSite("http://pc-de-zick"))
			{
				using (SPWeb web = site.OpenWeb())
				{

				Stopwatch sw = new Stopwatch();
				sw.Start();

				SPList list = web.Lists["Test"];

				StringBuilder expr = new StringBuilder();

				string method = "<Method ID="{0}">" +
				"<SetVar Name="Cmd">Save</SetVar>" +
				"<SetList>{1}</SetList>" +
				"<SetVar Name="ID">New</SetVar>" +
				"<SetVar Name="urn:schemas-microsoft-com:office:office#Title">{2}</SetVar>" +
				"</Method>";

				for (int i = 0; i < 2000; ++i)
					expr.AppendFormat(method, i, list.ID, "item #" + i);

				string batch = string.Format("<?xml version="1.0" encoding="UTF-8"?><Batch OnError="Return">{0}</Batch>", expr);

				web.ProcessBatchData(batch);

				sw.Stop();
				Console.WriteLine("2000 items inserted in : " + sw.Elapsed.TotalMilliseconds);

				Console.Read();
				}
			}
		}
	}
} 
Nous expliquerons cette architecture plus tard. Pour l'instant, regardons ce qu'elle donne en terme de performance :
Image
Sur la capture d'écran ci-dessus, vous pouvez constatez que cette architecture permet d'exécuter les 2000 ajouts dans la liste en seulement 12 secondes, ce qui représente une diminution de la durée totale d'exécution de 8 secondes par rapport à la technique habituelle. Maintenant que l'efficacité de cette méthode a été démontrée, passons à l'explication de celle-ci. Une requête batch n'est rien d'autre que du XML permettant d'indiquer clairement l'opération que nous voulons effectuer. Tout d'abord, nous devons déclarer le prologue de cette requête XML :
<?xml version="1.0" encoding="UTF-8"?>
Soyez très prudent avec la syntaxe de cette ligne. Effectivement, récemment j'avais mis un espace avant le dernier "?" et cela ne voulait pas fonctionner, tout ça à cause de l'espace... 1h30 de ma vie partie en fumée ^^ Ensuite, nous devons utiliser les éléments permettant de contenir notre tâche :
<Batch OnError="Return">

</Batch>
L'élément Batch va contenir de nombreux éléments Method qui permettront de dire quelles opérations devront être réalisées. Cet élément possède l'attribut OnError qui permet de définir le comportement de la requête dans le cas où celle-ci rencontrerait une erreur. Si cet attribut est définit sur Continue, la requête continuera de s'exécuter en ne tenant pas compte de l'erreur. Par contre, si cet attribut est définit sur Return, la requête n'ira pas plus loin. Nous avons ensuite les éléments Method. Un élément de ce type va nous permettre de définir une opération à effectuer sur une liste. Si nous revenons à notre exemple, nous avons ceci :
<Method ID="{0}">

</Method> 
Un élément Method peut contenir un attribut ID. Celui-ci sera important en termes de débogage. En effet, si une erreur se produit lors de l'exécution de votre requête, la fonction renverra un message d'erreur en XML contenant l'ID de la méthode ayant générée l'erreur. Vous pourrez ainsi la rectifier facilement. Passons maintenant au corps de l'élément Method. Ce dernier contiendra des éléments permettant d'affecter des valeurs à des variables. Une fois ces valeurs affectées, l'opération pourra s'exécuter en se basant sur ces dernières. La première variable à définir permet d'indiquer quel type d'opération nous voulons exécuter. Ici, il s'agit d'ajouter des éléments dans une liste. Nous ferons donc cela en modifiant la valeur de la variable Cmd :
<SetVar Name="Cmd">Save</SetVar>
La valeur de la variable Cmd doit être initialisée sur Save lorsque nous voulons ajouter ou mettre à jour des éléments dans une liste. La variable à définir ensuite est bien évidemment l'ID de la liste dans laquelle nous désirons travailler. Pour cela, nous devons utiliser l'élément SetList :
<SetList>

</SetList> 
La valeur devant être passée à cet élément est simplement le GUID de la liste. Ensuite, nous devons toujours définir l'ID de l'élément que nous voulons ajouter/mettre à jour. Dans le cas d'un ajout, vous ne pouvez pas connaître l'ID que devra avoir l'élément, dans ce cas, vous devrez utiliser le mot clé New :
<SetVar Name="ID">New</SetVar>
Nous utilisons donc à nouveau SetVar pour modifier la valeur de la variable ID. Enfin, la dernière étape consiste simplement à définir la valeur de nos champs. Pour cela, il suffit d'utiliser SetVar en faisant passer le nom du champ dans l'attribut Name. Notez cependant que le nom de ce champ devra être préfixé par le schéma de ce dernier, qui est généralement "urn:schemas-microsoft-com:office:office" :
<SetVar Name="urn:schemas-microsoft-com:office:office#Title">Valeur</SetVar>
Nous assignons donc simplement la valeur Valeur au champ Title. Comme vous le constatez avec notre code, l'élément Batch va contenir 2000 éléments Method et cela ne posera pas de problème. Effectivement, un élément Batch peut contenir un nombre illimité de Method a exécuter. Une fois la requête construite, il ne nous reste plus qu'à la faire passer en argument de la fonction ProcessBatchData de l'élément SPWeb contenant la liste.

Mise à jour d'éléments en masse

Pour ce qui est de la mise à jour d'éléments, et bien la syntaxe est très proche de celle permettant d'ajouter des nouveaux éléments. Vous devez savoir que dans les requêtes Batch vous ne pourrez mettre à jour des éléments que sur base de leur ID, donc il vous faudra récupérer les ID des éléments à mettre à jour avant de le faire. Une fois ceci fait, il vous suffit juste de procéder de la sorte :
<Method ID="ID_de_methode">
	<SetVar Name="Cmd">Save</SetVar>
	<SetList>ID-de-la-liste</SetList>
	<SetVar Name="ID">ID_item</SetVar>
	<SetVar Name="urn:schemas-microsoft-com:office:office#Title">Nouveau titre</SetVar>
</Method>
Nous utilisons à nouveau la valeur Save pour la variable Cmd indiquant ainsi que la méthode ajoutera ou mettra à jour un élément. Ici, étant donné que la variable ID est définie sur l'ID d'un élément existant, cela voudra dire que la méthode devra mettre cet élément à jour. Le reste de la méthode est identique à l'ajout. Il suffit de définir la nouvelle valeur pour le champ de l'élément et le tour est joué.

Suppression en masse d'éléments

Pour la suppression d'éléments en masse, ca n'est vraiment pas très compliqué, il suffit de faire ainsi :
<Method ID="ID_de_methode">
	<SetVar Name="Cmd">Delete</SetVar>
	<SetList>ID-de-la-liste</SetList>
	<SetVar Name="ID">ID_item</SetVar>
</Method> 
Comme vous pouvez le constater, la syntaxe de la méthode est encore plus simple quand il s'agit de la suppression. La valeur de la variable Cmd devra cette fois être initialisée sur Delete pour indiquer qu'il s'agit d'une suppression d'élément. Bien entendu, nous devons toujours spécifier l'ID de la liste pour indiquer de quelle liste les éléments seront supprimés. Enfin, nous devons spécifier l'ID de l'élément à supprimer grâce à la variable ID. Ces trois éléments sont suffisant pour déclencher une suppression d'éléments.

Création en masse de dossiers

Pour créer des dossiers en masse dans une liste, c'est légèrement plus complexe. En fait, il faut définir un champ supplémentaire pour indiquer que le type de l'élément sera un dossier. Il faut également définir le titre de l'élément différemment car, par défaut, le titre affiché du dossier est en fait son nom. Voici donc la manière de faire :
<Method ID="ID">
	<SetVar Name="Cmd">Save</SetVar>
	<SetList>ID-de-la-liste</SetList>
	<SetVar Name="urn:schemas-microsoft-com:office:office#ContentType">Folder</SetVar>
	<SetVar Name="owsfileref">/Lists/Test/Dossier 1</SetVar>
	<SetVar Name="ID">New</SetVar>
</Method> 
Pour ce qui est de la variable ID et Cmd, cela ne change pas. Il faut également toujours définir l'ID de la liste sur laquelle on travaille grâce à SetList. La différence réside premièrement dans le fait que nous devons modifier le champ ContentType de l'élément sur Folder pour indiquer qu'il s'agit d'un dossier. Ensuite, si vous modifiez le champ Title de cet élément, cela ne changera pas son titre car, comme nous l'avons vu, c'est le nom du dossier et non son titre qui est affiché sous la colonne Titre de la liste. Nous allons donc modifier la valeur du champ owsfileref pour définir un nom de dossier correct. Sa forme est assez simple, il suffit de définir le chemin d'accès à la liste (/Lists/Test/) suivi du nom que nous voulons donner au dossier (Dossier 1). Pour ce qui est de créer un dossier dans un autre dossier, il suffit juste de le rajouter au chemin, par exemple : Lists/Test/Dossier 1/Dossier 2.

Mise à jour en masse de dossiers

Pour mettre à jour le nom d'un dossier dans une liste, l'astuce reste globalement la même :
<Method ID="ID">
	<SetVar Name="Cmd">Save</SetVar>
	<SetList>ID-de-la-liste</SetList>
	<SetVar Name="ID">Id_du_dossier</SetVar>
	<SetVar Name="owsfileref">/Lists/Test/Dossier 1</SetVar>
	<SetVar Name="owsnewfileref">/Lists/Test/Dossier 2</SetVar>
	<SetVar Name="urn:schemas-microsoft-com:office:office#ContentType">Folder</SetVar>
</Method> 
Nous devons bien entendu utiliser l'ID du dossier pour le renommer mais nous devons également utiliser owsfileref pour le localiser. C'est le chemin d'accès relatif du dossier. Nous devons également définir owsnewfileref pour définir le nouveau nom du dossier. Enfin, nous précisons que l'opération est à effectuer sur un dossier grâce au champ ContentType que nous définissions sur Folder.

Suppression en masse de dossiers

La suppression en masse de dossier se passe exactement de la même façon que celle des éléments :
<Method ID="ID">
	<SetVar Name="Cmd">Delete</SetVar>
	<SetList>ID-de-la-liste</SetList>
	<SetVar Name="ID">Id_du_dossier</SetVar>
</Method> 
Cette requête se base donc uniquement sur l'ID du dossier pour effectuer la suppression.

Noter

Veuillez vous identifier ou vous inscrire pour donner une note à cet article.

Commentaires / Questions

Mickaël (17/01/2011 - 15:33)

Très bon article.

Je regrette cependant de ne pas avoir d'aide sur la meilleure façon de gérer le message d'erreur renvoyé par la méthode ProcessBatchData.

Une idée ?

Veuillez vous identifier ou vous inscrire pour réagir à cet article.

Avatar

Sébastien Sougnez

Envoyer un mail Site web Windows live messenger LinkedIn Twitter Facebook MVP Administrateur

25752 points