Developpez.com

Une très vaste base de connaissances en informatique avec
plus de 100 FAQ et 10 000 réponses à vos questions

Les Event Receivers

L'auteur

Profil ProSite personnel

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

Les Event Receivers sont des actions qui vont se déclencher à une réponse à un certain type d'évènement.

Cet article se propose de vous donner un aperçu de ce que sont les Event Receivers et comment les manipuler.

0. Tour d'horizon

Avec wss 3 / moss 2007, on va pouvoir manipuler des Event Receivers au niveau des bibliothèques de documents, mais également au niveau des listes classiques, des sites, et des actions de nos utilisateurs.

On va rencontrer 2 types d'Event Receivers ; les Event Receivers synchrones, s'exécutant avant une action, et les Event Receivers asynchrones, s'exécutant après l'action, dans un nouveau thread afin de ne pas bloquer le système.

Pour les Event Receivers synchrones, les méthodes correspondant se termineront toutes par -ing (par exemple itemAdding, itemDeleting, .), tandis que pour les Event Receivers asynchrones, les méthodes se termineront par -ed (par exemple itemAdded, itemDeleted).

En plus de pouvoir gérer les évènements au niveau de l'élément, on va également pouvoir gérer les évènements au niveau du schéma de nos objets ; au niveau de nos listes et doclibs par exemple, nous allons pouvoir capturer des évènements au moment de la création/modification/suppression d'éléments mais également au moment de la création/modification/suppression de colonnes

On va également pouvoir gérer nos Event Receivers de 2 façons, de manière déclarative ou dynamique.

I. Création d'un Event Receiver

Pour créer un Event Receiver, il nous suffit de créer une classe dérivant du type d'événement que nous souhaitons capturer.

I-A. Au niveau schéma de liste

Pour créer un Event Receiver au niveau du schéma de notre liste, nous allons créer une classe qui dérive de SPListEventReceiver.

De cette façon, nous serons capables d'intercepter tous les évènements d'ajout/modification/suppression au niveau des colonnes de notre liste.

Par exemple, le code suivant nous permet de créer un Event Receiver de liste, et y empêche toute modification de schéma.

 
Sélectionnez
//ListEventReceiver.cs
using System;
using Microsoft.SharePoint;

namespace Coforcert.Events.EventReceivers {
    public class ListEventReceiver : SPListEventReceiver {
        public override void FieldAdding(SPListEventProperties properties) {
            string msg = string.Format("{0} vous n'avez pas le droit de modifer le champ {1} de la liste {2}",properties.UserDisplayName, 
				properties.FieldName, properties.ListTitle);
            properties.Cancel = true;
            properties.ErrorMessage = msg;
        }

        public override void FieldDeleting(SPListEventProperties properties) {
            string msg = string.Format("{0} vous n'avez pas le droit de modifer le champ {1} de la liste {2}", properties.UserDisplayName, 
				properties.FieldName, properties.ListTitle);
            properties.Cancel = true;
            properties.ErrorMessage = msg;
        }
        public override void FieldUpdating(SPListEventProperties properties) {
            string msg = string.Format("{0} vous n'avez pas le droit de modifer le champ {1} de la liste {2}", properties.UserDisplayName, 
				properties.FieldName, properties.ListTitle);
            properties.Cancel = true;
            properties.ErrorMessage = msg;
        }
    }
}


La propriété ErrorMessage va nous permettre de définir le message qui sera affiché à l'utilisateur qui effectuera l'action.

I-B. Au niveau éléments de liste

Pour créer un Event Receiver au niveau des éléments de notre liste, nous allons créer une classe qui dérive de SPItemEventReceiver.

De cette façon, nous serons capables d'intercepter tous les évènements d'ajout/modification/suppression au niveau des éléments que contient notre liste.

image

I-C. Au niveau de notre site web, de l'envoi d'email et de la workflow library

Pour un Event Receiver au niveau de notre site web, il faudra dériver de la classe SPWebEventReceiver

image

On peut également capter les mails que recevront nos listes, via les Event Receivers dérivant de la classe SPEMailEventReceiver, comme ceci :

 
Sélectionnez
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace Coforcert.Events.EventReceivers
{
    public class MailEventReceiver : SPEmailEventReceiver
    {
        public override void EmailReceived(SPList list,SPEmailMessage emailMessage, string receiverData)
        {
            //Code
        }
    }
}


Au va également pouvoir travailler au niveau des items de notre librairie qui est créée lors de l'utilisation de nos workflow de cette façon :

image

Remarquez que les méthodes sont les mêmes que lorsque l'on a utilisé le SPItemEventReceiver, et ce par le fait que SPWorkflowLibraryEventReceiver est une classe « Sealed » qui dérive de SPItemEventReceiver.

II. Liaison déclarative de notre Event Receiver avec une liste

Pour lier notre Event Receiver à une liste de manière déclarative, nous allons devoir créer une Feature.

Voici ci-dessous le code de notre fichier Feature.xml

 
Sélectionnez
<?xml version="1.0" encoding="utf-8" ?>
<Feature Id="99823340-D80D-4453-AC32-4E6801718D7E" Scope="Web" 
         Title="Empeche l'ajout/modification/suppression de colonnes sur toutes les listes Calendrier de ce site web"
  xmlns="http://schemas.microsoft.com/sharepoint/">
  <ElementManifests>
    <ElementManifest Location="Elements.xml"/>
  </ElementManifests>
</Feature>


Et ci-dessous celui de notre Elements.xml

 
Sélectionnez
<?xml version="1.0" encoding="utf-8" ?>
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
  <Receivers ListTemplateId="106">
    <Receiver>
      <Name>Ajout</Name>
      <Type>FieldAdding</Type>
      <Assembly>EventReceivers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4f93984638d88477</Assembly>
      <Class>EventReceivers</Class>
      <SequenceNumber>1000</SequenceNumber>
    </Receiver>
    <Receiver>
      <Name>Modification</Name>
      <Type>FieldUpdating</Type>
      <Assembly>EventReceivers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4f93984638d88477</Assembly>
      <Class>EventReceivers</Class>
      <SequenceNumber>1000</SequenceNumber>
    </Receiver>
    <Receiver>
      <Name>Suppression</Name>
      <Type>FieldDeleting</Type>
      <Assembly>EventReceivers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4f93984638d88477</Assembly>
      <Class>EventReceivers</Class>
      <SequenceNumber>1000</SequenceNumber>
    </Receiver>
  </Receivers>
</Elements>


Si vous souhaitez plus d'informations sur les Features, je vous conseille également la lecture de cet article, qui montre comment les mettre en ouvre.

Comme d'habitude (sous Visual Studio 2008), on va se créer notre fichier install.bat pour déployer notre Feature ; le voici :

 
Sélectionnez
@SET TEMPLATEDIR="c:\program files\fichiers communs\microsoft shared\web server extensions\12\Template"
@SET GACUTIL="C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\gacutil.exe"
%GACUTIL% -u EventReceivers.dll
%GACUTIL% -if bin\debug\EventReceivers.dll
xcopy /e /y Template\* %TEMPLATEDIR%
@SET STSADM="c:\program files\fichiers communs\microsoft shared\web server extensions\12\bin\stsadm"
%STSADM% -o installfeature -filename Coforcert.ListEventReceiver\feature.xml -force
cscript c:\windows\system32\iisapp.vbs /a "SharePoint - 80" /r


Faites attention à bien remplacer "SharePoint - 80" par votre Application Pool, ou si vous ne savez pas lequel c'est, remplacez toute la ligne par un IISRESET (moins performant en terme de durée).

Ensuite attacher votre fichier bat aux commandes Post Build lors de la réussite de la génération.

image

On peut également afficher la fenêtre de sortie.

image

. pour visualiser le résultat depuis Visual Studio après avoir compilé.

image

Maintenant, si nous allons voir les Features d'un de nos sites web, nous aurons ceci :

image

Si nous activons la fonctionnalité et qu'ensuite nous créons une liste Calendar, puis que nous tentons d'ajouter une colonne par exemple, nous sommes redirigés vers la page d'erreur, qui nous affiche le message d'erreur que vous avons défini dans la propriété ErrorMessage.

image

Remarquez que j'ai pourtant effectué une opération d'ajout de colonne avec le compte administrateur de la ferme SharePoint.

La méthode réalisée ci-dessus est assez facile à mettre en ouvre quand on a l'habitude de travailler avec les Features, ce qui doit être le cas de tous les développeurs sur la plateforme wss 3 / moss 2007, cependant, elle présente plusieurs inconvénients :

  • Dans notre fichier Elements.xml, nous devons ajouter autant de noud Receiver que nous avons de Event Handler, ce qui peut devenir assez rapidement fastidieux si vous implémentez souvent des Event Receivers,
  • Cette Feature doit obligatoirement avoir un scope déclaré à Web, et rien d'autre ; ce qui signifie que vous devrez activer la Feature sur chacun des sites web concernés, 1 par 1,
  • Le Event Receiver doit obligatoirement être lié à un Template de liste, et donc sera appliqué à toutes les instances découlant de ce Template de liste du site web sur lequel la Feature est activée ; il nous est impossible de spécifier une liste particulière :s

Nous pouvons très facilement faire le test en créant une nouvelle liste « MonCalendrier », basée sur le Template Calendrier. Nous obtenons le message d'erreur ci-dessous :

image

Heureusement comme toujours, le modèle objet vient à notre secours.

III. Liaison dynamique de notre Event Receiver avec une liste

Une alternative à la méthode déclarative est de passer par le modèle objet de SharePoint.

Cette méthode est de loin la plus puissante, car elle va nous permettre de contrôler liste par liste, les évènements que nous souhaitons capturer.

Voici par exemple le code qui va nous permettre de faire exactement la même chose que précédemment.

 
Sélectionnez
string listName = "Calendrier";
SPList currentList = SPContext.Current.Web.Lists[listName];
string ReceiverAssemblyName = "EventReceivers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4f93984638d88477";
string ReceiverClassName = "Coforcert.Events.EventReceivers.ListEventReceiver";
currentList.EventReceivers.Add(SPEventReceiverType.FieldAdding, ReceiverAssemblyName, ReceiverClassName);
currentList.EventReceivers.Add(SPEventReceiverType.FieldDeleting, ReceiverAssemblyName, ReceiverClassName);
currentList.EventReceivers.Add(SPEventReceiverType.FieldUpdating, ReceiverAssemblyName, ReceiverClassName);        


Ici, nous récupérons notre liste, et nous lui ajoutons des évènements dynamiquement depuis le modèle objet. Ce bout de code peut être positionné n' importe où. On pourrait donc par exemple développer une webpart qui se chargerait de lier des évènements prédéfinis à une liste choisie, n'importe où dans notre ferme.

Pour réaliser la même opération que précédemment, à savoir activer notre Event Receiver lors de l'activation d'une Feature, il nous suffit pour cela de créer une Feature, et de rajouter du code dans le handler « FeatureActivated »

 
Sélectionnez
public override void FeatureActivated(SPFeatureReceiverProperties properties) {
  string listName = "Calendrier";
  SPList currentList = SPContext.Current.Web.Lists[listName];
  string ReceiverAssemblyName = "EventReceivers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4f93984638d88477";
  string ReceiverClassName = "Coforcert.Events.EventReceivers.ListEventReceiver";
  if (!currentList.Equals(null)) {
    currentList.EventReceivers.Add(SPEventReceiverType.FieldAdding, ReceiverAssemblyName, ReceiverClassName);
    currentList.EventReceivers.Add(SPEventReceiverType.FieldDeleting, ReceiverAssemblyName, ReceiverClassName);
    currentList.EventReceivers.Add(SPEventReceiverType.FieldUpdating, ReceiverAssemblyName, ReceiverClassName);
    currentList.Update();
  }
}


N'oubliez pas bien évidemment de supprimer vos évènements lorsque vous désactiverez la feature ; dans notre cas, nous allons supprimer tous nos Event Handlers. Cependant, gardez bien en mémoire qu'il se peut que quelqu'un d'autre que vous aie rajouté d'autres Event Handlers sur la liste, et donc pensez à rajouter des tests conditionnels appropriés.

Voici le code pour désactiver nos Event Handlers

 
Sélectionnez
public override void FeatureDeactivating(SPFeatureReceiverProperties properties){
  string listName = "Calendrier";
  SPSite curSite = new SPSite("http://wss3/sites/dev");
  SPWeb curWeb = curSite.RootWeb;
  SPList currentList = curWeb.Lists[listName];
  List<SPEventReceiverDefinition> CurrentListEventReceivers = new List<SPEventReceiverDefinition>(currentList.EventReceivers.Count);
  if (!currentList.Equals(null)) {
    foreach (SPEventReceiverDefinition receiverDef in currentList.EventReceivers)
      CurrentListEventReceivers.Add(receiverDef);
    foreach (SPEventReceiverDefinition receiver in CurrentListEventReceivers)
      receiver.Delete();
  }
}


Voila. Une fois cette Feature installée et activée, l'évènement se déclenchera uniquement sur la liste portant le nom « Calendrier » ; toutes les autres listes basées sur le même type, ne seront pas concernées.

Voici le résultat obtenu sur la liste nommée calendrier

image

Cependant, sur la liste « monCalendrier », cela fonctionne.

image

IV. Déploiement d'un Event Receiver

Pour activer nos Event Receiver, nous devons déployer notre assembly dans le GAC, et déployer nos fichiers dans le répertoire Features.

C'est ce que nous avons réalisé dans notre fichier install.bat

Pour aller jusqu'au bout des choses, nous allons également créer une solution pour déployer notre Event Receiver et bénéficier d'une gestion centralisée et automatisée de notre composant.

Voici le code du manifest.xml

 
Sélectionnez
><Solution SolutionId="FBEA352E-375A-47b7-BAFB-E7CA20F3C383" xmlns="http://schemas.microsoft.com/sharepoint/">
  <FeatureManifests>
    <FeatureManifest Location="Coforcert.DynamicListEventReceiver\feature.xml" />
    <FeatureManifest Location="Coforcert.ListEventReceiver\feature.xml" />
  </FeatureManifests>
  <Assemblies>
    <Assembly DeploymentTarget="GlobalAssemblyCache" Location="EventReceivers.dll"/>
  </Assemblies>
</Solution>

Et celui du cab.ddf

other
Sélectionnez
;
.OPTION EXPLICIT
.Set CabinetNameTemplate=Coforcert.Events.EventReceivers.wsp    
.set DiskDirectoryTemplate=CDROM
.Set CompressionType=MSZIP
.Set UniqueFiles="ON"
.Set Cabinet=on
.Set DiskDirectory1=Package

Solution\manifest.xml manifest.xml
TEMPLATE\FEATURES\Coforcert.DynamicListEventReceiver\feature.xml Coforcert.DynamicListEventReceiver\feature.xml
TEMPLATE\FEATURES\Coforcert.ListEventReceiver\feature.xml Coforcert.ListEventReceiver\feature.xml
TEMPLATE\FEATURES\Coforcert.ListEventReceiver\elements.xml Coforcert.ListEventReceiver\elements.xml
bin\Debug\EventReceivers.dll EventReceivers.dll


Et enfin le fichier deploy.bat

 
Sélectionnez
;
makecab /f Solution\cab.ddf

@SET SPDIR="c:\program files\fichiers communs\microsoft shared\web server extensions\12"


%SPDIR%\bin\stsadm -o addsolution -filename PACKAGE\Coforcert.Events.EventReceivers.wsp 
%SPDIR%\bin\stsadm -o execadmsvcjobs
%SPDIR%\bin\stsadm -o deploysolution -name Coforcert.Events.EventReceivers.wsp -immediate -allowGacDeployment -force
%SPDIR%\bin\stsadm -o execadmsvcjobs


Si vous souhaitez plus d'informations sur les solutions, je vous conseille cet article.

Vous pouvez également utiliser wspbuilder (merci Stephane ;)) pour générer vos solutions en 15sec.

V. Points Supplémentaires

V-A. Attention aux évènements qui se déclenchent eux-mêmes !

Vous pouvez vous trouver dans une situation ou vous devez par exemple ajouter d'autres éléments lors de l'insertion d'un nouvel élément dans votre liste ; ainsi, vous pouvez donc vous retrouver à écrire le code suivant :

other
Sélectionnez
public override void ItemAdding(SPItemEventProperties properties) {
            //Code
            SPList currentList = SPContext.Current.List;
            SPItem item = currentList.Items.Add();
            //Code
        }


Dans ce cas de figure, vous vous retrouver à ajouter un nouvel élément à votre liste dans la méthode qui intercepte l'ajout d'un élément, ce qui a pour but de réenclencher le handler qui a pour but de rajouter un nouvel élément, et ainsi de suite.

Pour parer facilement à ce genre de situation, il vous suffit d'utiliser les méthodes DisableEventFiring() et EnableEventFiring() de cette façon :

other
Sélectionnez
public override void ItemAdding(SPItemEventProperties properties) {
            this.DisableEventFiring();
            //Code
            SPList currentList = SPContext.Current.List;
            SPItem item = currentList.Items.Add();
            //Code
            this.EnableEventFiring();
        }

De cette façon, vous désactivez les événements le temps de l'exécution de votre code et les réactivez juste après.

V-B. Workflow et Event Receiver Part 1

Une question qui revient assez fréquemment est le choix entre mettre en ouvre un workflow et mettre en ouvre un Event Receiver.

Bien qu'il n'existe pas de réponse absolue, je pense que la réponse se trouve dans la définition du besoin à satisfaire.

Selon moi, vous devriez mettre en ouvre un workflow si :

  • Vous faites intervenir des acteurs humains dans le cadre par exemple de validation,
  • Vous avez besoin d'exécuter une action longue,
  • Votre action doit prendre en compte les contraintes de la machine ; en effet, même un reboot de la machine ne viendrait pas à bout de votre workflow !
  • Vous avez besoin de modéliser un processus.


Et vous devrez mettre en ouvre un item event receiver si :

  • Votre action ne fait intervenir aucun acteur extérieur et tout est automatisé,
  • Votre action est de courte durée,
  • Vous souhaitez exécuter une action particulière, comme annuler toute modification/insertion ou vérifier que le contenu ajouté est valide.


D'autre cas de figure peuvent influencer votre décision, mais je pense que cette (courte) liste est déjà un bon début.

V-C. Workflow et Event Receiver Part 2

En plus de vous poser des problèmes quant au choix de l'un des 2, ils vont également vous poser des problèmes si jamais vous décidez de mettre en ouvre les 2 !

Comme nous l'avons vu, lorsque vous mettez en ouvre des workflows sur une doclib, une nouvelle colonne est rajoutée au niveau de la liste ; or que se passe-t-il si vous avez implémentez un SPListEventReceiver et que vous avez overridé la méthode FieldAdding en empêchant toute modification du schéma de votre liste ?

Vous devriez être redirigé vers une belle page d'erreur ! Or ce n'est pas le cas. Votre workflow cesse tout simplement de s'exécuter, et la colonne correspondant au statut de votre workflow n'apparait pas. Il semblerait donc que le workflow soit désactivé, cependant, si vous regardez les paramètres du workflow de votre liste, ce dernier est bien présent ! Bref, un dysfonctionnement total.

Alors attention à bien planifier vos actions utilisant les workflows et les Event Receiver.

VI. Conclusion

Dans cet article, nous avons vu comment quels types d'Event Receivers nous avions à notre disposition.

Nous avons également vu comment les créer, supprimer et déployer via les Features et Solutions.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2008 Dieudonné N'TAMACK. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.