Filtrer et récupérer des éléments d’une liste grâce à Linq et les méthodes d’extensions
Développement, SharePoint No Comments »– UPDATE —
J’ai changé ma méthode pour récupérer directement une valeur dans ma liste de configuration. Je faisais un query qui me retournais une collection que je filtrais ensuite pour récupérer ma valeur. Alors qu’il suffisait simplement de le faire directement dans mon query
– UPDATE —
Dans mes applications SharePoint, j’utilise très souvent une liste ou je stocke l’ensemble de mes éléments de configuration comme par exemple :
- Email sender pour mes workflows
- Sujet d’un email
- Corps d’un email
- Url d’un WebService
- Et bien d’autres choses encore…
Le gros avantage est que le webmaster du site peut facilement (à ses risques et périls tout de même
) mettre à jour les informations et ceci sans reset de l’application pool ou redémarrage de l’appli web sous IIS dans le cas par exemple de variables stockées dans le web.config.
La structure de ma liste est toute simple :
- Key (le champ title de base renommé);
- Value (la valeur à retourner) de type « Multiline of text » en mode Plain Text;
- Category, un champ de type « Choice » qui me permet de regrouper mes informations. Par exemple, sur une page, il peut m’arriver d’avoir à récupérer plusieurs valeurs de configuration. Ce regroupement, me permet de les récupérer via un seul CAML Query. Ceci m’évitant donc de faire un query pour chaque élément.
J’avais donc implémenté une classe qui me permettait de récupérer ces valeurs via un CAML query sur ma liste. Mais suite à un article posté par Philippe Sentenac sur les méthodes d’extensions, j’ai décidé de refaire cette classe en utilisant ces extensions et les nombreuses autres classes generics du framework .Net.
Pour commencer, j’ai étendu l’objet SPWeb pour accéder à notre Liste de configuration (dans mon cas, j’ai aussi besoin d’un paramètre « Category » pour me retourner l’ensemble des valeurs car je ne souhaite pas faire une requête pour chaque entrée). Cette méthode, me retourne une interface générique de type Generic Interface )>" href="http://msdn.microsoft.com/en-us/library/bb534291.aspx" rel=tag target=_blank>ILookup < TKey, TElement>. Cette interface, va me permettre ensuite de récupérer très facilement une entrée de ma configuration :
public static ILookup<String, String> ConfigurationList(this SPWeb spWeb, String Category) { SPList spListConfiguration; ILookup<String, String> configurationEntries = null; // La liste n'est pas accessible aux end users. // On doit donc s'y connecter sur le compte admin pour y accéder // Optionnel, si vous avez décidé de mettre la liste en lecture pour tout vos utilisateurs SPSecurity.RunWithElevatedPrivileges(delegate() { using (SPSite mySite = new SPSite(spWeb.Site.ID)) { using (SPWeb myWeb = mySite.OpenWeb(spWeb.ID)) {
Je récupère ma liste « Configuration » grâce à la méthode d’extension TryGet de Philippe :
if (myWeb.Lists.TryGet("Configuration", out spListConfiguration)) {
J’exécute ensuite mon query pour récupérer mes éléments :
SPQuery spQuery = new SPQuery(); spQuery.Query = String.Format(<Where><Eq><FieldRef Name='Category' /><Value Type='Choice'>{0}</Value></Eq></Where>", Category);
Et je stock les résultats dans un document XML Linq (XDocument) :
XDocument xml = XDocument.Parse(spListConfiguration.GetItems(spQuery).Xml);
Je définie mon namespace pour me simplifier la lecture du code :
XNamespace z = "#RowsetSchema";
Je renvoie tout ce flux dans mon interface générique en lui spécifiant :
- Ma clé : key => key.Key;
- Ma valeur à retourner: element => element.Value;
- Mon comparateur d’égalité : EqualityComparer<String>.Default (ici comme je travaille sur des Strings je n’ai pas besoin d’en définir de particulier, mais si vous souhaitez le personnaliser, il vous suffit d’écrire une classe qui implémente Generic Interface )>" href="http://msdn.microsoft.com/en-us/library/ms132151.aspx" rel=tag target=_blank>IEqualityComparer<T>. Je donne un exemple à la fin de ce post pour implémenter une version qui ne prend pas en compte la casse de la clé) ;
configurationEntries = (from xmlItems in xml.Descendants(z + "row").ToArray() select new ConfigurationEntry { Key = (String)xmlItems.Attribute("ows_Title"), Value = (String)xmlItems.Attribute("ows_Value") }) .ToLookup(key => key.Key, element => element.Value, EqualityComparer<String>.Default); }); } onfigurationEntries; }
J’utile cette classe pour stocker ma clé ainsi que la valeur associée dans mon query Linq :
public class ConfigurationEntry { public String Key; public String Value; }
Ensuite, il ne me reste plus qu’a écrire la classe qui va me rechercher mon élément dans ma collection (on peut ici choisir de lui donner un objet SPWeb ou d’utiliser le context)
public sealed class Configuration { private ILookup<String, String> configurationValues; public Configuration(SPWeb spWeb, String category) { if (spWeb == null) throw new ArgumentNullException("A SPWeb object cannot be null"); if (String.IsNullOrEmpty(category)) throw new ArgumentNullException("A category cannot be empty"); configurationValues = spWeb.ConfigurationList(category); } public Configuration(String category) { if (SPContext.Current == null) throw new ArgumentNullException("SPContext cannot be null. Please ensure that you're running a SharePoint application"); if (String.IsNullOrEmpty(category)) throw new ArgumentNullException("A category cannot be empty"); configurationValues = SPContext.Current.Web.ConfigurationList(category); } public String GetEntry(String key) { return Convert.ToString(configurationValues[key].FirstOrDefault()); } }
Notez l’utilisation du générique )>" href="http://msdn.microsoft.com/en-us/library/bb340482.aspx" rel=tag target=_blank>Generic Method (IEnumerable) FirstOrDefault, qui nous permet ici de nous retourner un String.Empty s’il n’y a pas de valeur à retourner.
Ensuite, pour l’appel, c’est très simple. Instanciez un objet Configuration et utilisez la méthode GetEntry pour récupérer vos valeurs:
Configuration configValues = new Configuration(spWeb,"SSRS"); Console.WriteLine("SSRS URL:" + configValues.GetEntry("SSRSUrl")); Console.WriteLine("SSRS Path:" + configValues.GetEntry("SSRSPath")); Console.WriteLine("SSRS WebPartPage:" + configValues.GetEntry("SSRSWebPartPage"));
On peut aussi ajouter une variante à notre méthode d’extension ConfigurationList pour récupérer directement la valeur d’une clé dans notre liste :
public static String ConfigurationList(this SPWeb spWeb, String Category, String Key) { SPList spListConfiguration; String returnValue = String.Empty; SPSecurity.RunWithElevatedPrivileges(delegate() { using (SPSite mySite = new SPSite(spWeb.Site.ID)) { using (SPWeb myWeb = mySite.OpenWeb(spWeb.ID)) { if (myWeb.Lists.TryGet("Configuration", out spListConfiguration)) { // Modification du 25 juin 2008 // J'ai pas fais attention en publiant mon post que cette méthode était stupide // En effet, j'utilisais un query qui me retournais une collection que je filtrais ensuite pour retourner ma valeur // Alors que je pouvais faire cela dans un seul CAML QuerySPQuery spQuery = new SPQuery(); spQuery.Query = String.Format("<Where><And><Eq><FieldRef Name='Title' /><Value Type='Text'>{0}</Value></Eq><Eq><FieldRef Name='Category' /><Value Type='Choice'>{1}</Value></Eq></And></Where>", Key, Category); SPListItemCollection items = spListConfiguration.GetItems(spQuery); if (items != null && items.Count > 0) returnValue = Convert.ToString(items[0]["Value"]); // SPQuery spQuery = new SPQuery(); // spQuery.Query = String.Format("<Where><Eq><FieldRef Name='Category' /><Value Type='Choice'>{0}</Value></Eq></Where>", Category); // XDocument xml = XDocument.Parse(spListConfiguration.GetItems(spQuery).Xml); // XNamespace z = "#RowsetSchema"; // configurationEntries = (from xmlItems in xml.Descendants(z + "row") // select new ConfigurationEntry { Key = (String)xmlItems.Attribute("ows_Title"), Value = (String)xmlItems.Attribute("ows_Value") }) // .ToLookup(key => key.Key, element => element.Value, EqualityComparer<String>.Default); // returnValue Convert.ToString(configurationEntries[Key].FirstOrDefault()); } } } }); return returnValue; }
La récupération de votre valeur se faisant comme ceci:
Console.WriteLine("SSRS Url:" + spWeb.ConfigurationList("SSRS", "SSRSURL"));
Comme promis, voici ici un exemple de classe qui vous permet de comparer 2 Strings sans distinction de la casse :
public class StringNoCaseSensitive : IEqualityComparer<String> { public Boolean Equals(String val1, String val2) { return (val1.ToLower() == val2.ToLower()); } public int GetHashCode(string obj) { return obj.ToLower().GetHashCode(); } }
Dans l’appel du ILookup,remplacer EqualityComparer<String>.Default par new StringNoCaseSensitive() et le tour est joué…












Recent Comments