SharePoint 2007 : Extension Method (TryGet)

Ca fait un moment que je tripatouillais ca dans ma tete, il est temps que je vous en parle : L'utilisation des extensions de methode pour SharePoint.

Si par hasard, vous n'etes pas familier avec ce concept, vous trouverez plus d'informations sur le blog de Scott Guthrie.

Alors si vous etes comme moi (ie : Faineant), vous en avez marre de toujours devoir coder les memes choses lors de vos developpements, comme notamment ce bout de code pour afficher le nom d'une liste.

using System;

using Microsoft.SharePoint;

 

namespace SharePoint_Console_Application

{

    class Program

    {

        private const string STR_ErrorList = "ErrorList";

 

        static void Main(string[] args)

        {

            using (SPSite site = new SPSite("http://localhost"))

            {

                using (SPWeb web = site.RootWeb)

                {

                    SPList oRandomList;

                    try

                    {

                        oRandomList = web.Lists[STR_ErrorList];

                    }

                    catch (Exception ex)

                    {

                        Console.WriteLine(String.Format("La Liste {0} n'existe pas. Message : {1}", STR_ErrorList, ex.Message));

                    }

 

                    if (oRandomList != null)

                        Console.WriteLine(oRandomList.Title);

                }

            }

            Console.ReadLine();

        }

    }

}

Bien sur, vous pourriez recuperer une instance de votre liste differemment mais ce qui est important ici est de noter toute la plomberie necessaire juste pour savoir si une liste existe et eventuellement l'utiliser.

Et si, grace aux extensions de methodes, on rajoutait quelques petites fonctionnalites qui nous simplifieraient la vie ? Comme par exemple, un TryGet sur web.Lists.

using System;

using Microsoft.SharePoint;

using Phil.SharePoint.Extension;

 

namespace SharePoint_Console_Application

{

    class Program

    {

        private const string STR_ErrorList = "ErrorList";

 

        static void Main(string[] args)

        {

            using (SPSite site = new SPSite("http://localhost"))

            {

                using (SPWeb web = site.RootWeb)

                {

                    SPList oRandomList;

 

                    if (web.Lists.TryGet(STR_ErrorList, out oRandomList))

                        Console.WriteLine(oRandomList.Title);

                    else

                        Console.WriteLine(String.Format("La Liste {0} n'existe pas.", STR_ErrorList));

                }

            }

            Console.ReadLine();

        }

    }

}

De suite, c'est plus leger :)

L'extension de method TryGet ressemble a ca :

using System;

using System.Diagnostics;

using Microsoft.SharePoint;

 

namespace Phil.SharePoint.Extension

{

    public static class SPExtension

    {

        public static bool TryGet(this SPListCollection oListCollection, string name, out SPList result)

        {

            result = null;

            try

            {

                result = oListCollection[name];

                return true;

            }

            catch (Exception ex)

            {

                Trace.Write(String.Format("Extension Method TryGet : {0}", ex.Message), "Monitorable");

                return false;

            }

        }

    }

}

Alors bien sur l'implementation du TryGet est sujet a discussion, on aurait pu aussi bien faire ceci :

        public static bool TryGet(this SPListCollection oListCollection, string name, out SPList result)

        {

            result = null;

 

            foreach (SPList list in oListCollection)

            {

                if (list.Title == name)

                {

                    result = list;

                    return true;

                }

            }

            return false;

        }

Ou faire appel a d'autre methodes pour recuperer la liste...

Bref, c'est sympa mais si on veut pouvoir utiliser ce genre de methode sur d'autres objets, il va falloir copier coller le code, changer le type, bref ca va etre long, lourd et surtout source d'erreur.

Alors pourquoi ne pas aller plus loin, notamment avec l'utilisation des Generics ?

En effet, il faut savoir que la plupart des objets que l'on manipule dans SharePoint heritent de SPBaseCollection, on peut donc rajouter une methode qui sera ainsi accessible a tout les objets qui nous interessent (SPListCollection, SPUsers, SPWebs, etc..)

        public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)

        {

            result = default(T);

            try

            {

                MethodInfo methodInfo = null;

                Type spCollectionType = spBase.GetType();

 

                methodInfo = spCollectionType.GetMethod("get_Item", new Type[] { typeof(string) });

 

                if (methodInfo == null)

                    return false;

 

                result = (T)methodInfo.Invoke(spBase, new object[] { name });

                return true;

            }

            catch (Exception ex)

            {

                Trace.Write(String.Format("Extension Method TryGet : {0}", ex.InnerException.Message), "Monitorable");

                return false;

            }

        }

Comme il n'y a pas d'interface qui lie les objets qui nous interessent a SPBaseCollection, on ne peut pas utiliser le polymorphisme et on passe donc par la reflection.

L'utilisation de la reflection reste relativement basique ici, on recupere le Type reel de l'objet spBase puis la methodInfo qui nous interesse pour ensuite l'invoquer.

Hors comme vous le savez sans doute, la reflection a un cout non negligeable en terme de performances et l'idee de ces extensions de methodes est de simplifier la vie du developpeur, pas de la compliquer en ralentissant le traitement de son code.

C'est pourquoi j'ai apporte quelques modifications au code precedent :

        private static Dictionary<Type, MethodInfo> s_GetItemMethodInfos = new Dictionary<Type, MethodInfo>();

        public static bool TryGet<T>(this SPBaseCollection spBase, string name, out T result)

        {

            result = default(T);

            try

            {

                MethodInfo methodInfo = null;

                Type spCollectionType = spBase.GetType();

 

                if (s_GetItemMethodInfos.ContainsKey(spCollectionType) == false)

                {

                    methodInfo = spCollectionType.GetMethod("get_Item",

                                    BindingFlags.InvokeMethod | BindingFlags.ExactBinding | BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly,

                                    null,

                                    new Type[] { typeof(string) },

                                    null);

                    s_GetItemMethodInfos.Add(spCollectionType, methodInfo);

                }

                else

                    methodInfo = s_GetItemMethodInfos[spCollectionType];

 

                if (methodInfo == null)

                    return false;

 

                result = (T)methodInfo.Invoke(spBase, new object[] { name });

                return true;

            }

            catch (Exception ex)

            {

                Trace.Write(String.Format("Extension Method TryGet : {0}", ex.InnerException.Message), "Monitorable");

                return false;

            }

        }

On stocke les methodInfo dans un dictionnaire en static ce qui nous permettra d'eviter d'appeler GetMethod a chaque appel pour un meme objet.

L'appel a GetMethod se fait avec des BindingFlags qui permettent en theorie de limiter et d'optimiser au maximum cet appel (cf Improving Reflection Performance)

Ainsi a chaque appel, on verifie si on a deja la methodInfo correspondante au type sur lequel la methode est appelee et on invoque directement la methode si c'est le cas.

Bien sur, je ne suis pas en train de dire que ce code pourrait aller en production. Il faudrait que je fasse des vrais benchmarks pour voir exactement le cout/gain en terme de performance. De plus, le fait d'encapsuler a ce point la gestion d'erreur peut se reveler problematique ... mais ca laisse en tout cas songeur en terme de possibilite.

Finalement, je tenais a remercier (par ordre d'intervention) Olivier Nizet, Matthieu Mezil, Davy Frontigny et Jerome Laban qui ont apporte leur contribution a ce petit Proof of Concept bien sympatique.

Si vous avez des idees sur l'optimization de cette methode, je suis preneur :)

<Philippe/>

Publié jeudi 15 mai 2008 08: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 : Extension Method (TryGet) @ jeudi 15 mai 2008 08:09

Avec plaisir Phil. Et puis c'était sympa de faire ça dans l'avion pour Seattle. :-)

Matthieu MEZIL

# re: SharePoint 2007 : Extension Method (TryGet) @ jeudi 15 mai 2008 09:22

Clair, une bonne rangée de Geek :)

phil

# re: SharePoint 2007 : Extension Method (TryGet) @ jeudi 15 mai 2008 16:11

Clairement, ca sent le potentiel a plein

Par contre la lecture m'a mis la migraine du siécle

T'as pareil pour le CAML ? :D

themit

# re: SharePoint 2007 : Extension Method (TryGet) @ jeudi 15 mai 2008 17:44

Attend de voir les prochaines fonctions :)

Pour le CAML, c'est ta partie :)

phil

# re: SharePoint 2007 : Extension Method (TryGet) @ mardi 24 juin 2008 20:55

Terrible!

Merci du tuyeau. Je l'ai teste et c'est vraiment tres tres pratique :o)

Gael

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

- [ #Yammer ] From Mailbox to Yammer and back / De votre messagerie vers Yammer et retour ! par Le blog de Patrick [MVP SharePoint] le il y a 19 heures et 8 minutes

- [ #Office 365 ] New service settings panel / Nouveau panneau de paramétrage des services par Le blog de Patrick [MVP SharePoint] le 09-11-2014, 08:50

- Problème de déploiement pour une démo SharePoint/TFS? par Blog de Jérémy Jeanson le 09-10-2014, 21:52

- [ #Office365 ] Delve first impressions / Premières impressions sur Delve par Le blog de Patrick [MVP SharePoint] le 09-09-2014, 16:57

- [ #Office365 ] How to change Administration console language ? / Comment changer la langue de la console d’administration ? par Le blog de Patrick [MVP SharePoint] le 09-09-2014, 08:25

- [ #SharePoint 2013 ] Suppression de bases de données en état “Pas de Réponse” par Le blog de Patrick [MVP SharePoint] le 09-04-2014, 14:10

- Changer l’adresse d’une ferme Office Web Apps associée à SharePoint par Blog de Jérémy Jeanson le 09-01-2014, 22:21

- Une ferme #SharePoint 2013 dans @Azure en quelques clics (1ère partie) ! par Le blog de Patrick [MVP SharePoint] le 08-28-2014, 18:52

- SharePoint 2013: Préparation de la migration - Création des site Templates dans 2010 et 2013 par Blog Technique de Romelard Fabrice le 08-20-2014, 16:31

- [ #Yammer ] How to change interface language ? Comment changer la langue de l’interface ? par Le blog de Patrick [MVP SharePoint] le 08-20-2014, 14:21