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

Leader Technique SharePoint chez Winwise, je m'occupe de missions d'expertise autour de WSS 3.0 et MOSS 2007, tout en me tenant au courant de ce qui se fait autour de Asp.net, Silverlight et des technologies Web by Microsoft en général.

Les 10 derniers blogs postés

- [Refactoring] ReSharper pour Visual Studio 2010 (Preview) par Thomas Jaskula le il y a 11 heures et 13 minutes

- [Refactoring] Analyser vos exceptions avec ReSharper Exceptional par Thomas Jaskula le il y a 12 heures et 27 minutes

- SharePoint 2007 : patterns & practices SharePoint Guidance par Philippe Sentenac [MVP SharePoint] le 07-03-2009, 09:56

- [Visual Studio 2010] Les tests cases c’est bien, mais je vais devoir tout réécrire ? par Etienne Margraff le 07-03-2009, 09:00

- MVP[Gribouillon].AddYear par The Grib's Lair [Sébastien PICAMELOT - MVP SharePoint] le 07-03-2009, 08:45

- Clinique INSIA - Projet de fin d’Etudes (Silverlight 3 MVVM et OutOfBrowser, WCF, TFS) - Part 1 par David REI le 07-02-2009, 23:38

- C’est la crise ? Bah pourquoi cramer du budget pub alors ? par Nix's Blog le 07-02-2009, 15:31

- Soyons MVP ! par TheSaib .NET blog le 07-02-2009, 12:15

- SharePoint : Gestion des Erreurs 6398, 7076 et 6482 par Blog Technique de Romelard Fabrice le 07-02-2009, 11:53

- EF avec WPF par Matthieu MEZIL le 07-02-2009, 10:18