Gérer les événements d’élément de liste dans le complément hébergé par le fournisseur

Cet article est le dixième d’une série sur les concepts de base du développement de compléments SharePoint hébergés par un fournisseur. Vous devez tout d’abord avoir pris connaissance de la rubrique Compléments SharePoint et des articles précédents de la série, disponibles dans la rubrique Commencer à créer des compléments hébergés par un fournisseur pour SharePoint.

Notes

Si vous avez consulté cette série sur les compléments hébergés par un fournisseur, votre solution Visual Studio vous sera utile pour continuer à parcourir cette rubrique. Vous pouvez également télécharger le référentiel sur SharePoint_Provider-hosted_Add-Ins_Tutorials et ouvrir le fichier RER.sln.

Vous avez vu dans un article antérieur de cette série que lorsqu’une commande est passée, elle est ajouté à la table Commandes dans la base de données d’entreprise et un élément correspondant est automatiquement ajouté à la liste Livraisons attendues. À son arrivée dans le magasin local, un utilisateur définit la colonne Arrivé sur Oui. La modification d’une valeur de champ pour un élément crée un événement d’élément mis à jour pour lequel vous pouvez ajouter un gestionnaire personnalisé.

Dans cet article, vous créez un gestionnaire pour cet événement d’élément de liste et le déployez par programme dans la logique de première exécution du complément SharePoint. Votre gestionnaire ajoute l’élément dans la table Inventaire de la base de données d’entreprise. Il définit ensuite la colonne Ajouté à l’inventaire de la liste Livraisons attendues sur Oui. Vous découvrez également comment empêcher ce deuxième événement d’élément mis à jour de déclencher une série infinie d’événements d’élément mis à jour.

Déployer par programmation la liste des Livraisons attendues.

Notes

Les paramètres pour les projets de démarrage dans Visual Studio ont tendance à revenir aux paramètres par défaut chaque fois que la solution est rouverte. Suivez toujours ces étapes immédiatement après la réouverture de l’exemple de solution dans cette série d’articles :

  1. Cliquez avec le bouton droit sur le nœud de la solution en haut de l’Explorateur de solutions, puis sélectionnez Définir les projets de démarrage.
  2. Vérifiez que les trois projets sont définis sur Démarrer dans la colonne Action.
  1. Dans l’Explorateur de solutions, ouvrez le fichier Utilities\SharePointComponentDeployer.cs dans le projet ChainStoreWeb. Ajoutez la méthode suivante à la classe SharePointComponentDeployer.

     private static void CreateExpectedShipmentsList()
    {
       using (var clientContext = sPContext.CreateUserClientContextForSPHost())
       {
       var query = from list in clientContext.Web.Lists
               where list.Title == "Expected Shipments"
               select list;
       IEnumerable<List> matchingLists = clientContext.LoadQuery(query);
       clientContext.ExecuteQuery();
    
       if (matchingLists.Count() == 0)
       {
           ListCreationInformation listInfo = new ListCreationInformation();
           listInfo.Title = "Expected Shipments";
           listInfo.TemplateType = (int)ListTemplateType.GenericList;
           listInfo.Url = "Lists/ExpectedShipments";
           List expectedShipmentsList = clientContext.Web.Lists.Add(listInfo);
    
           Field field = expectedShipmentsList.Fields.GetByInternalNameOrTitle("Title");
           field.Title = "Product";
           field.Update();
    
           expectedShipmentsList.Fields.AddFieldAsXml("<Field DisplayName='Supplier'" 
                                   + " Type='Text' />", 
                                   true,
                                   AddFieldOptions.DefaultValue);
           expectedShipmentsList.Fields.AddFieldAsXml("<Field DisplayName='Quantity'" 
                                   + " Type='Number'" 
                                   + " Required='TRUE' >" 
                                   + "<Default>1</Default></Field>",
                                   true, 
                                   AddFieldOptions.DefaultValue);
           expectedShipmentsList.Fields.AddFieldAsXml("<Field DisplayName='Arrived'" 
                                  + " Type='Boolean'"
                                  + " ShowInNewForm='FALSE'>"
                                  + "<Default>FALSE</Default></Field>",
                                   true, 
                                   AddFieldOptions.DefaultValue);
           expectedShipmentsList.Fields.AddFieldAsXml("<Field DisplayName='Added to Inventory'" 
                                   + " Type='Boolean'" 
                                   + " ShowInNewForm='FALSE'>"
                                   + "<Default>FALSE</Default></Field>", 
                                   true, 
                                   AddFieldOptions.DefaultValue);
    
           clientContext.ExecuteQuery();
       }
        }
    }
    

    Ce code n'introduit aucune fonctionnalité que vous n'avez pas déjà vue dans un article de cette série, mais notez les points suivants :

    • Il définit l'attribut Required du champ Quantité Sur TRUE, c'est pourquoi le champ doit toujours comporter une valeur. Il définit ensuite la valeur par défaut sur 1.

    • Les champs Arrivé et Ajouté à l'inventaire sont masqués sur le formulaire Nouvel élément.

    • Dans l’idéal, le champ Ajouté à l'inventaire devrait être également masqué sur le formulaire de modification d’élément, car il doit être modifié uniquement sur Oui lorsque le gestionnaire d’événements d’élément mis à jour a d’abord ajouté l’élément à la table Inventaire d’entreprise. Pour des raisons techniques que nous allons vous présenter dans une étape ultérieure, un champ doit être visible dans le formulaire de modification d’élément si vous voulez écrire par programme sur celui-ci dans un gestionnaire d’événements d’élément mis à jour.

  2. Dans la méthode DeployChainStoreComponentsToHostWeb, ajoutez la ligne suivante, juste au-dessus de la ligne RemoteTenantVersion = localTenantVersion.

      CreateExpectedShipmentsList();
    

Créer le récepteur d’éléments et d’évènements de liste

Notes

Si vous avez travaillé via cette série d’articles, vous avez déjà configuré votre environnement de développement pour le débogage de récepteurs d’événements distants. Dans le cas contraire, reportez-vous à la rubrique Configurer la solution de débogage pour un récepteur d’événements avant d’aller plus loin dans cette rubrique.

Les outils de développement Office pour Visual Studio incluent un élément Récepteur d’événements distant qui peut être ajouté à une solution de complément SharePoint. Toutefois, au moment de la rédaction de cet article, cet élément de projet part du principe selon lequel la liste (avec laquelle le récepteur sera enregistré) se trouve sur le site web de complément et par conséquent que les outils créent un site web de complément et des artefacts SharePoint dedans. Toutefois, le récepteur du complément Chain Store va être enregistré (dans une étape ultérieure) auprès de la liste Livraisons attendues sur le site web hôte, de sorte que le complément n'ait pas besoin d'un site web de complément. (Pour obtenir un rappel sur la distinction entre les sites web de complément et les sites web hôte, reportez-vous à la rubrique Compléments SharePoint.)

Notes

Les récepteurs d’événements de liste et d’élément de liste sont appelés des récepteurs d’événements distants (RER), car leur code est distant de SharePoint et se trouve soit dans le cloud, soit dans un serveur local en dehors de la batterie de serveurs SharePoint. Cependant, les événements qui les déclenchent sont dans SharePoint.

  1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le dossier Services dans le projet ChainStoreWeb, puis sélectionnez Ajouter > Service WCF.

  2. Lorsque vous y êtes invité, nommez le service RemoteEventReceiver1, puis sélectionnez OK.

  3. Les outils créent un fichier d’interface, un fichier *.svc et un fichier code-behind. Nous n’avons pas besoin du fichier d’interface IRemoteEventReceiver1.cs, donc supprimez-le. (Les outils peuvent avoir ouvert celui-ci automatiquement ; si c’est le cas, fermez-le et supprimez-le.)

    Notes

    Lorsque vous avez créé les récepteurs d’événements de complément pour les événements installés et en cours de désinstallation dans un article antérieur de cette série, les outils de développement Office pour Visual Studio ont ajouté leurs URL dans le fichier manifeste de l’application. Les récepteurs d’événements de liste et d’élément de liste ne sont pas enregistrés dans le manifeste de l’application. Au lieu de cela, ils sont enregistrés par programmation dans un complément hébergé par un fournisseur. Vous le ferez dans une étape ultérieure.

  4. Ouvrez le fichier code-behind RemoteEventReceiver1.svc.cs. Remplacez tout le contenu par le code suivant.

      using System;
    using System.Collections.Generic;
    using Microsoft.SharePoint.Client;
    using Microsoft.SharePoint.Client.EventReceivers;
    using System.Data.SqlClient;
    using System.Data;
    using ChainStoreWeb.Utilities;
    
    namespace ChainStoreWeb.Services
    {
        public class RemoteEventReceiver1 : IRemoteEventService
        {
        /// <summary>
        /// Handles events that occur before an action occurs, 
        /// such as when a user is adding or deleting a list item.
        /// </summary>
        /// <param name="properties">Holds information about the remote event.</param>
        /// <returns>Holds information returned from the remote event.</returns>
        public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
        {
            throw new NotImplementedException();
        }
    
        /// <summary>
        /// Handles events that occur after an action occurs, 
        /// such as after a user adds an item to a list or deletes an item from a list.
        /// </summary>
        /// <param name="properties">Holds information about the remote event.</param>
        public void ProcessOneWayEvent(SPRemoteEventProperties properties)
        {
    
        }
        }
    }
    

    Tenez compte des informations suivantes :

    • L’interface IRemoteEventService est définie dans l’espace de noms Microsoft.SharePoint.Client.EventReceivers.

    • Aucun événement préalable ne sera géré dans le complément Chain Store, mais la méthode ProcessEvent est requise par l'interface IRemoteEventService.

  5. Ajoutez le code suivant à la méthode ProcessOneWayEvent. Notez que l’événement ItemUpdated est le seul événement géré par cet exemple, c’est pourquoi nous aurions pu utiliser une structure if simple au lieu d’une structure switch. Toutefois, les récepteurs d’événements gèrent généralement plusieurs événements, nous souhaitons donc vous présenter le modèle que vous allez utiliser le plus souvent dans vos gestionnaires d’événements en tant que développeur de complément SharePoint.

      switch (properties.EventType)
    {
        case SPRemoteEventType.ItemUpdated:
    
        // TODO12: Handle the item updated event.
    
        break;
    }  
    
  6. Remplacez TODO12 par le code suivant. Ici encore, nous utilisons une structure switch alors qu'une structure if simple suffirait, car nous souhaitons que vous voyiez le modèle courant dans les récepteurs d'événements SharePoint.

      switch (properties.ItemEventProperties.ListTitle)
    {
        case "Expected Shipments":
    
        // TODO13: Handle the arrival of a shipment.
    
        break;
    }
    
  7. Le code qui répond à l’arrivée d’une livraison doit effectuer deux actions :

    • Ajouter l'élément qui est arrivé au magasin dans l'inventaire de l'entreprise.

    • Définir le champ Ajouté à l'inventaire de la liste Livraisons attendues sur Oui. Cela doit se produire uniquement si l'élément a été ajouté à l'inventaire.

    Ajoutez le code suivant à la place de TODO13. Les deux méthodes TryUpdateInventory et RecordInventoryUpdateLocally sont créées dans les étapes ultérieures.

      bool updateComplete = TryUpdateInventory(properties);
    if (updateComplete)
    {
        RecordInventoryUpdateLocally(properties);
    }
    
  8. La méthode ProcessOneWayEvent doit désormais se présenter de la façon suivante :

      public void ProcessOneWayEvent(SPRemoteEventProperties properties)
    {
        switch (properties.EventType)
        {
        case SPRemoteEventType.ItemUpdated:
    
            switch (properties.ItemEventProperties.ListTitle)
            {
            case "Expected Shipments":
                bool updateComplete = UpdateInventory(properties);
                if (updateComplete)
                {
                RecordInventoryUpdateLocally(properties);
                }
                break;
            }
            break;
        }          
    }
    
  9. Ajoutez la méthode suivante à la classe RemoteEventReceiver1.

      private bool TryUpdateInventory(SPRemoteEventProperties properties)
    {
        bool successFlag = false;
    
        // TODO14: Test whether the list item is changing because the product has arrived
        // or for some other reason. If the former, add it to the inventory and set the success flag
        // to true.     
    
        return successFlag;
    }
    
  10. Il existe cinq colonnes dans la liste Livraisons attendues, mais nous ne souhaitons pas que le gestionnaire ne réagisse à la plupart des types de mises à jour d’un élément. Par exemple, si un utilisateur corrige l’orthographe du nom d’un fournisseur, l’événement d’élément mis à jour est déclenché, mais notre gestionnaire ne doit rien faire. Il doit intervenir uniquement lorsque le champ Arrivé vient d'être défini sur Oui.

    Il existe une autre condition qui doit être testée. Supposons que le champ Arrivé est défini sur Oui et que le produit dans l’élément est ajouté à l’inventaire (et Ajouté à l’inventaire est défini sur Oui). Mais par la suite, un utilisateur modifie par inadvertance le champ Arrivé d’une livraison sur Non, puis corrige son erreur en le redéfinissant sur Oui. L’erreur et le correctif déclenchent l’événement d’élément mis à jour. Le gestionnaire ne réagit pas à l’erreur, car il agit uniquement quand le champ Arrivé est réglé sur Oui, mais il réagirait au correctif qui définit Arrivé sur Oui, de sorte que le produit et la même quantité seraient ajoutés une deuxième fois dans l’inventaire. Pour cette raison, le gestionnaire doit agir uniquement lorsque la valeur Ajouté à l’inventaire est Non.

    Par conséquent, le gestionnaire a besoin de connaître les valeurs de ces champs juste après que l’utilisateur a mis à jour l’élément. L’objet SPRemoteEventProperties comporte une propriété ItemEventProperties. À son tour, elle comporte une propriété AfterProperties indexée qui conserve les valeurs des champs dans l’élément mis à jour. Le code suivant utilise ces propriétés pour tester si le gestionnaire doit réagir. Mettez ceci à la place de TODO14.

     var arrived = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Arrived"]);
    var addedToInventory = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Added_x0020_to_x0020_Inventory"]);
    
    if (arrived && !addedToInventory)
    {
    
       // TODO15: Add the item to inventory
    
       successFlag = true;
    }
    
  11. Remplacez TODO15 par le code suivant.

     using (SqlConnection conn = SQLAzureUtilities.GetActiveSqlConnection())
    using (SqlCommand cmd = conn.CreateCommand())
    {
       conn.Open();
       cmd.CommandText = "UpdateInventory";
       cmd.CommandType = CommandType.StoredProcedure;
       SqlParameter tenant = cmd.Parameters.Add("@Tenant", SqlDbType.NVarChar);
       tenant.Value = properties.ItemEventProperties.WebUrl + "/";
       SqlParameter product = cmd.Parameters.Add("@ItemName", SqlDbType.NVarChar, 50);
       product.Value = properties.ItemEventProperties.AfterProperties["Title"]; // not "Product"
       SqlParameter quantity = cmd.Parameters.Add("@Quantity", SqlDbType.SmallInt);
       quantity.Value = Convert.ToUInt16(properties.ItemEventProperties.AfterProperties["Quantity"]);
       cmd.ExecuteNonQuery();
    }
    

    Il s’agit essentiellement de programmation SQL et ASP.NET, nous n’en parlons donc pas en détail, mais notez ce qui suit :

    • Nous utilisons la propriété ItemEventProperties.WebUrl pour obtenir le nom du client, qui correspond à l'URL du site web hôte.

    • Nous utilisons à nouveau la propriété AfterProperties pour obtenir les valeurs en matière de quantité et de nom du produit.

    • Nous faisons référence au champ de nom de produit en tant que « Titre », même si le nom d’affichage a été modifié en « Produit » (dans la méthode CreateExpectedShipmentsList), car il est toujours fait référence aux champs par leur nom interne.

  12. Nous n’avons pas encore terminé avec la méthode TryUpdateInventory, mais à ce stade, cela doit ressembler à ce qui suit.

     private bool TryUpdateInventory(SPRemoteEventProperties properties)
    {
       bool successFlag = false;
    
       var arrived = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Arrived"]);
       var addedToInventory = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Added_x0020_to_x0020_Inventory"]);
    
       if (arrived && !addedToInventory)
       {
       using (SqlConnection conn = SQLAzureUtilities.GetActiveSqlConnection())
       using (SqlCommand cmd = conn.CreateCommand())
       {
           conn.Open();
           cmd.CommandText = "UpdateInventory";
           cmd.CommandType = CommandType.StoredProcedure;
           SqlParameter tenant = cmd.Parameters.Add("@Tenant", SqlDbType.NVarChar);
           tenant.Value = properties.ItemEventProperties.WebUrl + "/";
           SqlParameter product = cmd.Parameters.Add("@ItemName", SqlDbType.NVarChar, 50);
           product.Value = properties.ItemEventProperties.AfterProperties["Title"]; // not "Product"
           SqlParameter quantity = cmd.Parameters.Add("@Quantity", SqlDbType.SmallInt);
           quantity.Value = Convert.ToUInt16(properties.ItemEventProperties.AfterProperties["Quantity"]);
           cmd.ExecuteNonQuery();
       }            
       successFlag = true;
       }  
       return successFlag;
    }
    
  13. Lorsque la méthode TryUpdateInventory renvoie true, notre gestionnaire appelle une méthode (pas encore écrite) qui met à jour le même élément dans la liste Livraisons attendues en définissant le champ Ajouté à l’inventaire sur Oui. Il s’agit d’un événement d’élément mis à jour, donc le gestionnaire est rappelé. (Le fait que le champ Ajouté à l’inventaire soit désormais défini sur Oui empêche le gestionnaire d’ajouter la même livraison à l’inventaire une deuxième fois, mais le gestionnaire est toujours appelé.)

    Cependant, SharePoint se comporte différemment lorsque l’événement d’élément mis à jour est déclenché par une mise à jour de programmation : il comprend uniquement, dans la propriété AfterProperties, les champs modifiés dans la mise à jour. Par conséquent, le champ Arrivé n’apparaît pas, car seul le champ Ajouté à l’inventaire a été modifié.

    La ligne...

    var arrived = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Arrived"]);

    ...génère une exception KeyNotFoundException.

    Il existe plusieurs moyens de résoudre ce problème. Dans cet exemple, nous allons maintenant intercepter l'exception et utiliser le bloc catch pour s'assurer que l'élément successFlag est défini sur false. Cette opération permet de garantir que l'élément n'est pas mis à jour une troisième fois.

    Tout placer dans la méthode qui est située entre la première ligne bool successFlag = false; et la dernière ligne return successFlag; dans un bloc try.

  14. Ajoutez le bloc catch suivant juste sous le bloc try.

     catch (KeyNotFoundException)
    {
       successFlag = false;
    }
    

    Notes

    L’exception KeyNotFoundException est également la raison pour laquelle nous avons laissé le champ Ajouté à l’inventaire visible sur le formulaire Modifier l’élément. SharePoint n’inclut pas les champs masqués du formulaire Modifier l’élément dans AfterProperties.

  15. La méthode complète doit se présenter comme suit :

     private bool TryUpdateInventory(SPRemoteEventProperties properties)
    {
       bool successFlag = false;
    
       try 
       {
       var arrived = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Arrived"]);
       var addedToInventory = Convert.ToBoolean(properties.ItemEventProperties.AfterProperties["Added_x0020_to_x0020_Inventory"]);
    
       if (arrived && !addedToInventory)
       {
           using (SqlConnection conn = SQLAzureUtilities.GetActiveSqlConnection())
           using (SqlCommand cmd = conn.CreateCommand())
           {
           conn.Open();
           cmd.CommandText = "UpdateInventory";
           cmd.CommandType = CommandType.StoredProcedure;
           SqlParameter tenant = cmd.Parameters.Add("@Tenant", SqlDbType.NVarChar);
           tenant.Value = properties.ItemEventProperties.WebUrl + "/";
           SqlParameter product = cmd.Parameters.Add("@ItemName", SqlDbType.NVarChar, 50);
           product.Value = properties.ItemEventProperties.AfterProperties["Title"]; // not "Product"
           SqlParameter quantity = cmd.Parameters.Add("@Quantity", SqlDbType.SmallInt);
           quantity.Value = Convert.ToUInt16(properties.ItemEventProperties.AfterProperties["Quantity"]);
           cmd.ExecuteNonQuery();
           }            
           successFlag = true;
       }  
       }
       catch (KeyNotFoundException)
       {
       successFlag = false;
       }
       return successFlag;
    }
    
  16. Ajoutez la méthode suivante à la classe RemoteEventReceiver1.

     private void RecordInventoryUpdateLocally(SPRemoteEventProperties properties)
    {
       using (ClientContext clientContext = TokenHelper.CreateRemoteEventReceiverClientContext(properties))
       {
       List expectedShipmentslist = clientContext.Web.Lists.GetByTitle(properties.ItemEventProperties.ListTitle);
       ListItem arrivedItem = expectedShipmentslist.GetItemById(properties.ItemEventProperties.ListItemId);
       arrivedItem["Added_x0020_to_x0020_Inventory"] = true;
       arrivedItem.Update();
       clientContext.ExecuteQuery();
       }
    }
    

    Ce modèle de code est désormais familier après les articles précédents de cette série. Mais notez une différence :

    • Le code obtient l’objet ClientContext en appelant la méthode TokenHelper.CreateRemoteEventReceiverClientContext au lieu de la méthode SharePointContext.CreateUserClientContextForSPHost puisque nous avons utilisé le code appelé dans SharePoint à partir de pages, telles que la page EmployeeAdder.

    • La raison principale expliquant l’existence de différentes méthodes pour obtenir un objet ClientContext est que SharePoint transmet les informations nécessaires à la création de tels objets différemment aux récepteurs d’événements et aux pages. Pour les récepteurs d’événements, il transmet un objet SPRemoteEventProperties, mais pour les pages, il transfère un champ spécial, appelé jeton de contexte, dans le corps de la requête qui ouvre la page du complément.

  17. Enregistrez et fermez le fichier de code du récepteur.

Inscription du récepteur

La dernière tâche consiste à indiquer à SharePoint que nous disposons d'un récepteur personnalisé que SharePoint doit appeler chaque fois qu'un élément de la liste Livraisons attendues est mis à jour.

  1. Ouvrez le fichier SharePointComponentDeployer.cs et ajoutez la ligne suivante à la méthode DeployChainStoreComponentsToHostWeb, juste en dessous de la ligne qui crée la liste Livraisons attendues (vous ajouterons cette méthode dans l’étape suivante). Nous transmettons à la méthode l’objet HttpRequest que la page de démarrage du complément a transmis à la méthode DeployChainStoreComponentsToHostWeb.

      RegisterExpectedShipmentsEventHandler(request);
    
  2. Ajoutez la méthode suivante à la classe SharePointComponentDeployer.

      private static void RegisterExpectedShipmentsEventHandler(HttpRequest request)
    {
        using (var clientContext = sPContext.CreateUserClientContextForSPHost())    
        {
        var query = from list in clientContext.Web.Lists
                where list.Title == "Expected Shipments"
                select list;
        IEnumerable<List> matchingLists = clientContext.LoadQuery(query);
        clientContext.ExecuteQuery();
    
        List expectedShipmentsList = matchingLists.Single();
    
        // TODO16: Add the event receiver to the list's collection of event receivers.       
    
        clientContext.ExecuteQuery();
        }
    }
    
  3. Remplacez TODO16 par les lignes suivantes. Notez qu'il existe une classe CreationInformation non activable pour les récepteurs d'événements, comme pour les listes et les éléments de liste.

    EventReceiverDefinitionCreationInformation receiver = new EventReceiverDefinitionCreationInformation();
    receiver.ReceiverName = "ExpectedShipmentsItemUpdated";
    receiver.EventType = EventReceiverType.ItemUpdated;
    
     // TODO17: Set the URL of the receiver.
    
    expectedShipmentsList.EventReceivers.Add(receiver);
    
    
  4. Désormais, vous devez indiquer à SharePoint l’URL du récepteur d’événements. En production, elle va se trouver dans le même domaine que les pages à distance, avec le chemin d’accès de /Services/RemoteEventReceiver1.svc. Étant donné que le gestionnaire est en cours d’enregistrement dans la logique de première exécution de la page de démarrage du complément, le domaine se situe dans l’en-tête d’hôte de l’objet HttpRequest de la requête qui appelé la page. Notre code a transmis cet objet de la page à la méthode DeployChainStoreComponentsToHostWeb, qui l’a elle-même transmis à la méthode RegisterExpectedShipmentsEventHandler. Nous pouvons donc définir l’URL du récepteur avec le code suivant.

    receiver.ReceiverUrl = "https://" + request.Headers["Host"] + "/Services/RemoteEventReceiver1.svc";

    Malheureusement, cela ne fonctionne pas lorsque vous déboguez le complément à partir de Visual Studio. Lors du débogage, le récepteur est hébergé dans Azure Service Bus, pas dans l’URL d’hôte local hébergeant les pages à distance. Nous devons définir des URL distinctes pour le récepteur selon que nous procédons au débogage ou non, il est donc nécessaire de remplacer TODO17 par la structure suivante qui utilise des directives du compilateur C#. En mode de débogage, l’URL du récepteur est lue à partir d’un paramètre web.config (vous créerez ce paramètre dans une étape ultérieure).

      #if DEBUG
                receiver.ReceiverUrl = WebConfigurationManager.AppSettings["RERdebuggingServiceBusUrl"].ToString();
    #else
                receiver.ReceiverUrl = "https://" + request.Headers["Host"] + "/Services/RemoteEventReceiver1.svc"; 
    #endif
    
    
  5. L’ensemble de la méthode RegisterExpectedShipmentsEventHandler doit maintenant ressembler à ce qui suit.

      private static void RegisterExpectedShipmentsEventHandler(HttpRequest request)
    {    
        using (var clientContext = sPContext.CreateUserClientContextForSPHost())
        {
        var query = from list in clientContext.Web.Lists
                    where list.Title == "Expected Shipments"
                    select list;
        IEnumerable<List> matchingLists = clientContext.LoadQuery(query);
        clientContext.ExecuteQuery();
    
        List expectedShipmentsList = matchingLists.Single();
    
        EventReceiverDefinitionCreationInformation receiver = new EventReceiverDefinitionCreationInformation();
        receiver.ReceiverName = "ExpectedShipmentsItemUpdated";
        receiver.EventType = EventReceiverType.ItemUpdated;
    
    #if DEBUG
        receiver.ReceiverUrl = WebConfigurationManager.AppSettings["RERdebuggingServiceBusUrl"].ToString();
    #else
        receiver.ReceiverUrl = "https://" + request.Headers["Host"] + "/Services/RemoteEventReceiver1.svc"; 
    #endif
        expectedShipmentsList.EventReceivers.Add(receiver);
        clientContext.ExecuteQuery();
        }
    }
    
  6. Ajoutez l'instruction using suivante en haut du fichier.

      using System.Web.Configuration;
    
  7. Pour vous assurer que DEBUG est défini sur true si, et seulement si, le complément est en cours de débogage, menez à bien la sous-procédure suivante :

    1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet ChainStoreWeb et sélectionnez Propriétés.

    2. Ouvrez l’onglet Générer de l’élément Propriétés, puis sélectionnez Déboguer dans la liste déroulante Configuration en haut.

    3. Vérifiez que la case à cocher Définir la constante DEBUG est activée (elle l’est généralement par défaut). La capture d’écran ci-dessous présente le paramétrage correct.

      Figure 1. Sous-onglet Générer de l’onglet Propriétés dans Visual Studio

      Sous-onglet Générer de l’onglet Propriétés dans Visual Studio La liste déroulante Configuration est définie sur Déboguer. La case à cocher « Définir la constante DEBUG » est activée.

    4. Modifiez la liste déroulante Configuration en Mise en production, puis assurez-vous que la case à cocher Définir la constante DEBUG est désactivée (généralement, elle est activée par défaut). La capture d’écran ci-dessous présente le paramètre correct.

      Figure 2. Sous-onglet Générer de l’onglet Propriétés avec case à cocher désactivée

      Sous-onglet Générer dans l’onglet Propriétés. La liste déroulante Configuration est définie sur Version finale. La case « Définir la constante DEBUG » n’est pas sélectionnée.

    5. Si vous avez apporté des modifications, enregistrez et fermez l'onglet Propriétés.

  8. Ouvrez le fichier web.config et ajoutez le balisage suivant comme enfant de l’élément appSettings (nous obtiendrons la valeur du paramètre dans la section suivante).

      <add key="RERdebuggingServiceBusUrl" value="" />
    

Obtenir l’URL du récepteur pour réaliser le débogage

Les récepteurs d’événements de complément et d’événements d’élément de liste sont des services Service WCF (Windows Communication), et chaque service WCF connaît son propre point de terminaison et le stocke dans plusieurs emplacements, y compris l’objet System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri.

Lors du débogage, le récepteur de complément est hébergé dans un point de terminaison Azure Service Bus qui est presque identique au point de terminaison du récepteur d’élément de liste. La différence est que l’URL du point de terminaison du complément se termine par « AppEventReceiver.svc », alors que l’URL du récepteur d’élément de liste se termine par « RemoteEventReceiver1.svc ». Afin que nous puissions obtenir l’URL du point de terminaison dans le récepteur de complément, apportez une petite correction à la fin de celui-ci, puis utilisez-le comme valeur de notre paramètre web.config RERdebuggingServiceBusUrl.

  1. Ouvrez le fichier AppEventReceiver.svc.cs dans le dossier Services du projet ChainStoreWeb.

  2. Ajoutez la ligne suivante en tant que toute première ligne de la méthode ProcessEvent.

      string debugEndpoint = System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri.ToString(); 
    
  3. Ajoutez un point d’arrêt sur la ligne suivante de la méthode.

  4. Sélectionne F5 pour déboguer le complément. Étant donné que le fichier web.config est ouvert et que les outils de développement Office pour Visual Studio modifient un de ses paramètres chaque fois que vous appuyez sur F5, vous serez invité à le recharger. Sélectionnez Oui.

  5. Une fois le point d’arrêt atteint, pointez le curseur sur la variable debugEndpoint. Lorsque la bulle d’informations de Visual Studio s’affiche, sélectionnez la flèche vers le bas, puis Visualiseur de texte.

    Figure 3. Visualiseur de texte Visual Studio contenant une URL Azure Service Bus

    Visualiseur de texte Visual Studio contenant une URL Azure Service Bus.

  6. Copiez la valeur de chaîne du visualiseur et copiez-la quelque part.

  7. Fermez le visualiseur, puis arrêtez le débogage dans Visual Studio.

  8. Supprimez ou commentez la ligne que vous avez ajoutée lors de la deuxième étape de cette procédure, puis supprimez également le point d'arrêt.

  9. Dans la chaîne que vous avez copiée, remplacez l'élément final « AppEventReceiver.svc » par « RemoteEventReceiver1.svc ».

  10. Copiez et collez l'URL modifiée en tant que valeur de la clé RERdebuggingServiceBusUrl dans le fichier web.config.

Notes

Lors du débogage d'un récepteur d'événements distant, le fait de copier manuellement l'URL du bus de services et de la coller (version modifiée) dans le fichier web.config ne constitue pas la seule façon de répondre à la nécessité d'une URL différente de celle utilisée lors de l'exécution en production. Il est possible de stocker par programmation la valeur de l'élément System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri quelque part dans SharePoint ou sur la base de données distante, de la faire lire par le code de première exécution, puis de l'attribuer à la propriété receiver.ReceiverUrl. Il est possible d’enregistrer le récepteur d’événements d’élément de liste dans le cadre du gestionnaire d’événements installé pour le complément. Il est possible ensuite de lire par programme System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri, de le modifier et de l’affecter à receiver.ReceiverUrl sans avoir à le stocker n’importe où.

Cette stratégie exige que la liste Livraisons attendues soit également créée dans le gestionnaire d’événements installé pour le complément, car elle doit exister pour que le gestionnaire puisse être enregistré avec elle.

Nous pouvons également combiner notre récepteur d’événements de complément et le récepteur d’événements d’élément de liste en récepteur unique (autrement dit, les mêmes fichiers .svc et. svc.cs). Dans ce cas, aucune modification de l’URL n’est nécessaire avant de l’utiliser en tant que valeur de receiver.ReceiverUrl.

Exécutez le complément et testez le récepteur d’élément de liste

  1. Ouvrez la page Contenu du site du site web du magasin de Hong Kong et supprimez la liste Livraisons attendues s’il en existe une.

  2. Utilisez la touche F5 pour déployer et exécuter votre complément. Visual Studio héberge l'application web distante dans IIS Express et héberge la base de données SQL dans SQL Express. Il réalise également une installation temporaire du complément sur votre site SharePoint de test et l'exécute immédiatement. Vous êtes invité à accorder des autorisations au complément avant l'ouverture de sa page d'accueil.

  3. Lorsque la page d’accueil du complément s’ouvre, sélectionnez le bouton Retour au site sur le contrôle Chrome dans la partie supérieure.

  4. À partir de la page d’accueil de la boutique de Hong Kong, accédez à la page Contenu du site et ouvrez la liste Livraisons attendues.

  5. Créez un élément sur le formulaire des nouveaux éléments et notez que les champs Arrivé et Ajouté à l’inventaire n’y figurent pas.

  6. Une fois l’élément créé, rouvrez-le à des fins de modification. Activez la case à cocher Arrivé et enregistrez l’élément. Cela déclenche l’événement d’élément mis à jour. L’élément est ajouté à l’inventaire et la valeur du champ Ajouté à l’inventaire devient Oui (vous devrez peut-être actualiser la page pour afficher la modification apportée à Ajouté à l’inventaire).

  7. Utilisez le bouton de retour du navigateur jusqu’à vous retrouver sur la page d’accueil du complément Chain Store, puis sélectionnez le bouton Afficher l’inventaire. L’élément que vous avez marqué comme Arrivé est maintenant répertorié.

  8. Revenez à la liste Livraisons attendues et ajoutez un autre élément avec exactement le même nom de produit et le même nom de fournisseur, mais avec une quantité différente.

  9. Une fois que l'élément est créé, ouvrez-le à nouveau pour le modifier. Définissez le champ Arrivé sur Oui et enregistrez l'élément.

  10. Utilisez le bouton de retour du navigateur jusqu’à vous retrouver sur la page d’accueil du complément Chain Store, puis sélectionnez le bouton Afficher l’inventaire. Il existe toujours simplement un élément pour le nom du produit et le fournisseur, mais la quantité représente désormais le total des deux éléments de la liste Livraisons attendues.

  11. Pour mettre fin à la session de débogage, fermez la fenêtre du navigateur ou arrêtez le débogage dans Visual Studio. Chaque fois que vous appuyez sur F5, Visual Studio retire la version précédente du complément et installe la plus récente.

  12. Vous allez travailler avec ce complément et la solution Visual Studio dans d'autres articles. Il est donc recommandé de retirer le complément une dernière fois lorsque vous avez terminé de travailler et n'allez pas le réutiliser pendant un moment. Cliquez avec le bouton droit sur le projet dans l' Explorateur de solutions et sélectionnez Retirer.

Étapes suivantes

Pour en savoir plus sur la publication de votre complément dans un site SharePoint, consultez la rubrique Déploiement et installation de compléments SharePoint : méthodes et options. Vous pouvez également poursuivre les tâches de développement des compléments SharePoint dans les pages suivantes :