SharePoint 2007 : Linq et SharePoint (Comme vous faire aimer le CAML)

Dernièrement j'ai eu l'occasion de voir une video très sympa parlant d'un projet CodePlex que j'avais un peu oublié : LinqToSharePoint. Pour ceux qui ne connaitrait pas, cet outil, crée par Bart De Smet (Ancien MVP C#, Employé Microsoft depuis peu), permet de convertir des requêtes Linq en CAML.

Alors cela fait maintenant près de 8 mois que Thomas vous a parlé de ce projet plein de promesses, il est temps de voir ce qui a été fait :

Histoire de tester, on va exporter des données de la base NorthWind dans SharePoint pour obtenir ces trois listes : Products, Categories et Suppliers

export_base_northwind

L'idée étant qu'un produit est lié à un supplier et à une catégorie. Celà se traduit par une clef étrangère dans la base et par un champ de type "lookup" dans SharePoint.

Une fois l'export terminé et LinqToSharePoint installé, vous avez la possibilité de rajouter ce nouvel élément à votre projet : Linq to SharePoint file

linqtosharepoint_item

Ce fichier va vous permettre de configurer votre accès aux données grâce à un assistant.

linqtosharepoint_wizard

Vous l'aurez compris cet accès se fait par WebServices. Il vous suffit maintenant de spécifier les champs qui vous intéressent et vous pouvez vous mettre à coder !

 linqtosharepoint_wizard_bis

Prenons un exemple pratique : Supposons que je souhaite récupérer tout les produits dont l'Unit Price est supérieur à 100, en triant le tout par ordre Décroissant. Pour au final, afficher le nom du produit et son prix.

En CAML, ca donnerait ceci (grosso modo, pas de gestion d'erreurs, etc.):

System.Text.StringBuilder oSbQuery = new System.Text.StringBuilder();

 

oSbQuery.Append("        <Where>");

oSbQuery.Append("              <Gt>");

oSbQuery.Append("                   <Value Type=\"Currency\">100</Value>");

oSbQuery.Append("                   <FieldRef Name=\"UnitPrice\" />");

oSbQuery.Append("              </Gt>");

oSbQuery.Append("        </Where>");

oSbQuery.Append("        <OrderBy>");

oSbQuery.Append("              <FieldRef Name=\"UnitPrice\" Ascending=\"FALSE\" />");

oSbQuery.Append("        </OrderBy>");

 

string sResultQuery = oSbQuery.ToString();

 

System.Text.StringBuilder oSbViewFields = new System.Text.StringBuilder();

 

oSbViewFields.Append("        <FieldRef Name=\"Title\" />");

oSbViewFields.Append("        <FieldRef Name=\"UnitPrice\" />");

 

string sResultViewFields = oSbViewFields.ToString();

 

SPQuery query = new SPQuery();

query.ViewFields = sResultViewFields;

query.Query = sResultQuery;

 

using (SPWeb web = new SPSite("http://localhost/NorthWind").OpenWeb())

{

    SPList products = web.Lists["Products"];

    SPListItemCollection items = products.GetItems(query);

 

    foreach (SPListItem item in items)

    {

        Console.WriteLine(String.Format("{0} supplied by {1}", item["ProductName"], item["UnitPrice"]));

    }

}

en Linq, ça devient :

var ctx = new SPNorthWindSharePointDataContext();

var req_Products_1 = from p in ctx.Products

                           where p.UnitPrice > 100

                           orderby p.UnitPrice descending

                           select new { Product = p.ProductName, p.UnitPrice };

 

foreach (var p in req_Products_1)

{

      Console.WriteLine(String.Format("{0} costs {1}", p.Product, p.UnitPrice));

}

Desuite, ca fait une sacrée différence :). Et l'affichage des informations est bien plus simple à faire vu q'on travaille directement avec des objets typés.

Dans le même style, voilà une autre requête, où l'on souhaite récupérer tout les produits dont le ProductName commence par C. On remarquera qu'en CAML, il faudra passer le nom interne de la colonne ProductName (à savoir, Title), LinqToSharePoint fait la traduction tout seul.

<Query>

  <Where>

    <BeginsWith>

      <FieldRef Name="Title" />

      <Value Type="Text">C</Value>

    </BeginsWith>

  </Where>

  <OrderBy>

    <FieldRef Name="Title" Ascending="FALSE" />

  </OrderBy>

</Query>

<ViewFields>

  <FieldRef Name="Title" />

  <FieldRef Name="UnitPrice" />

</ViewFields>

var req_Products_2 = from p in ctx.Products

                     where p.ProductName.StartsWith("C")

                     orderby p.ProductName descending

                     select new { Product = p.ProductName, p.UnitPrice };

Bon c'est sympa, mais soyons honnête, il existe beaucoup d'outils pour simplifier la génération de CAML, qu'est que celui ci apporte en plus ?

Encore une fois, voyons par la pratique : Je souhaite récupérer tout les produits dont la catégorie est Beverages et dont le ProductName commence par C.

En CAML, si on veut faire cela proprement, cela nécessitera deux requêtes.

<Query>

  <Where>

    <Eq>

      <Value Type="Text">Beverages</Value>

      <FieldRef Name="Title" />

    </Eq>

  </Where>

</Query>

<ViewFields>

  <FieldRef Name="ID" />

</ViewFields>

et

<Query>

  <Where>

    <And>

      <BeginsWith>

        <FieldRef Name="Title" />

        <Value Type="Text">C</Value>

      </BeginsWith>

      <Eq>

        <Value Type="Lookup">1</Value>

        <FieldRef Name="Category" LookupId="TRUE" />

      </Eq>

    </And>

  </Where>

  <OrderBy>

    <FieldRef Name="Title" Ascending="FALSE" />

  </OrderBy>

</Query>

<ViewFields>

  <FieldRef Name="Title" />

  <FieldRef Name="UnitPrice" />

</ViewFields>

Ca commence à devenir un peu long à écrire tout cet XML, juste pour une reqête relativement simple non ? Heureusement avec LinqToSharePoint ca donne ça :

var req_Products_3 = from p in ctx.Products

                     where p.ProductName.StartsWith("C")

                     && p.Category.CategoryName == "Beverages"

                     orderby p.ProductName descending

                     select new { Product = p.ProductName, p.UnitPrice };

Desuite, on voit la différence ! En interne, la requête est exactement celle que vous avez vu plus haut. LinqToSharePoint s'occupe seul de faire le lien. Notamment par l'utilisation de l'élément Patch dans la requête ci-dessous.

linqtosharepoint_query_visualizer

Bon attention, vous ne pouvez pas non plus faire n'importe quoi avec. Par exemple, l'utilisation de la méthode EndsWith n'est pas supporté. Cette restriction étant du au CAML en lui même.

linqtosharepoint_notimplemented linqtosharepoint_notimplemented_bis    

Bien sur il est possible de contourner cette limitation en encastrant la requête ci-dessous dans une autre requête Linq classique mais par contre ca complexifie l'ensemble.

Et pour finir, sur cette petite démonstration de ce version ALPHA, voilà la requête qui m'a fait installer ce tool sur ma VPC.

Comment faire pour afficher le nom de tout les produits commençant par C, dont la catégorie est Beverages et en plus d'afficher le CompanyName du Supplier ? Evidemment en CAML, ca va commencer à faire une sacré tranche d'XML (dont je vous épargne le Copier/Coller) mais en Linq, cela reste relativement simple (on a même plusieurs façons de pouvoir écrire la requête).

var req_Products_4 = from p in ctx.Products

                    where p.Category.CategoryName == "Beverages"

                    && p.ProductName.StartsWith("C")

                    orderby p.ProductName descending

                    select p;

 

foreach (var p in req_Products_4)

{

    Console.WriteLine(String.Format("{0} supplied by {1}", p.ProductName, p.Supplier.CompanyName));

}

 

var req_Products_5 = from p in ctx.Products

                    where p.Category.CategoryName == "Beverages"

                    && p.ProductName.StartsWith("C")

                    orderby p.ProductName descending

                    select new { Product = p.ProductName, p.Supplier.CompanyName };

 

foreach (var p in req_Products_5)

{

    Console.WriteLine(String.Format("{0} supplied by {1}", p.Product, p.CompanyName));

}

Bien entendu, l'ensemble de ces requêtes sont optimizées afin d'éviter les appels intempestifs à SharePoint. Par exemple, Si vous avez 4 produits appartenant à la même catégorie, il n'effectuera la requête pour obtenir l'ID de la catégorie qu'une seule fois

Par contre, il n'est pas possible de mettre à jour les données via LinqToSharePoint mais apparement, ca ne saurait tardé.

LinqToSharePoint fonctionne aussi bien avec du C# 3.0 que du VB 9.0 et il est possible de se connecter aussi bien par WebServices que par le Modèle Objet.

<Philippe/>

Publié lundi 5 novembre 2007 07:00 par phil
Classé sous , , ,
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :

Commentaires

# re: SharePoint 2007 : Linq et SharePoint (Comme vous faire aimer le CAML) @ lundi 5 novembre 2007 11:15

Hummmmmmmmmmmmmm ca donne envie !!!

Je m'etais fait la meme remarque sur ma VPC a mon premier test

Mais bon, j'aime bien le CAML moi :)

Depuis le temps, ca m'est devenue un second langage ...

L'abus de Site definition je pense :D

Vivement une version MS supportée en tout cas ...

Bart a rejoins MS mais pas la division MOSS donc croisons les doigts

themit

About phil

Philippe Sentenac est Consultant SharePoint à Wygwam en région Parisienne. Il intervient essentiellement sur des missions liées à SharePoint (2007 et 2010 ) mais aussi autour du Web 2.0. Plus généralement, il s'intéresse à l'ASP.Net (MVC) , à Silverlight, et à tout ce qui est orienté Web en rapport avec les nouvelles technologies, qu'il pratique depuis 2006. Féru de développement, il est passionné par les problématiques de méthodologies et d'industrialisation du développement.

Les 10 derniers blogs postés

- Merci par Blog de Jérémy Jeanson le 10-01-2019, 20:47

- Office 365: Script PowerShell pour auditer l’usage des Office Groups de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 11:02

- Office 365: Script PowerShell pour auditer l’usage de Microsoft Teams de votre tenant par Blog Technique de Romelard Fabrice le 04-26-2019, 10:39

- Office 365: Script PowerShell pour auditer l’usage de OneDrive for Business de votre tenant par Blog Technique de Romelard Fabrice le 04-25-2019, 15:13

- Office 365: Script PowerShell pour auditer l’usage de SharePoint Online de votre tenant par Blog Technique de Romelard Fabrice le 02-27-2019, 13:39

- Office 365: Script PowerShell pour auditer l’usage d’Exchange Online de votre tenant par Blog Technique de Romelard Fabrice le 02-25-2019, 15:07

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Stream Portal par Blog Technique de Romelard Fabrice le 02-21-2019, 17:56

- Office 365: Script PowerShell pour auditer le contenu de son Office 365 Video Portal par Blog Technique de Romelard Fabrice le 02-18-2019, 18:56

- Office 365: Script PowerShell pour extraire les Audit Log basés sur des filtres fournis par Blog Technique de Romelard Fabrice le 01-28-2019, 16:13

- SharePoint Online: Script PowerShell pour désactiver l’Option IRM des sites SPO non autorisés par Blog Technique de Romelard Fabrice le 12-14-2018, 13:01