Gérer les événements d’élément de liste dans le complément hébergé par le fournisseurHandle list item events in the provider-hosted add-in

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.This is the tenth in a series of articles about the basics of developing provider-hosted SharePoint Add-ins. You should first be familiar with SharePoint Add-ins and the previous articles in this series:

Note

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.If you have been working through this series about provider-hosted add-ins, you have a Visual Studio solution that you can use to continue with this topic. Vous pouvez également télécharger le référentiel sur SharePoint_Provider-hosted_Add-Ins_Tutorials et ouvrir le fichier RER.sln.You can also download the repository at SharePoint_Provider-hosted_Add-Ins_Tutorials and open the BeforeRER.sln file.

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.You saw in an earlier article in this series that when an order is placed, it is added to the Orders table in the corporate database, and an item for it is automatically added to the Expected Shipments list. À son arrivée dans le magasin local, un utilisateur définit la colonne Arrivé sur Oui.When it arrives at the local store, a user sets the Arrived column to Yes. 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é.Changing a field value for an item creates an item updated event for which you can add a custom handler.

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.In this article, you create a handler for this list item event and then programmatically deploy it in the first-run logic of the SharePoint Add-in. Votre gestionnaire ajoute l’élément dans la table Inventaire de la base de données d’entreprise.Your handler adds the item into the Inventory table in the corporate database. Il définit ensuite la colonne Ajouté à l’inventaire de la liste Livraisons attendues sur Oui.It then sets the Added to Inventory column of the Expected Shipments list to Yes. 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.You also learn how to prevent this second item updated event from setting off an infinite series of item updated events.

Déployer par programmation la liste des Livraisons attendues.Programmatically deploy the Expected Shipments list

Note

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.The settings for Startup Projects in Visual Studio tend to revert to defaults whenever the solution is reopened. Suivez toujours ces étapes immédiatement après la réouverture de l’exemple de solution dans cette série d’articles :Always take these steps immediately after reopening the sample solution in this series of 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.Right-click the solution node at the top of Solution Explorer, and then select Set startup projects.
  2. Assurez-vous que les trois projets sont définis sur Démarrer dans la colonne Action.Ensure that all three projects are set to Start in the Action column.
  1. Dans l’Explorateur de solutions, ouvrez le fichier Utilities\SharePointComponentDeployer.cs dans le projet ChainStoreWeb.In Solution Explorer, open the Utilities\SharePointComponentDeployer.cs file in the ChainStoreWeb project. Ajoutez la méthode suivante à la classe SharePointComponentDeployer.Add the following method to the SharePointComponentDeployer class.

     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 ne présente pas des fonctionnalités que vous n’avez pas déjà consultées dans un article précédent de cette série, mais notez ce qui suit :This code doesn't introduce any functionality that you haven't already seen in a previous article of this series, but note the following:

    • 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.It sets the Required attribute of the Quantity field to TRUE so the field must always have a value. It then sets the default value to 1.

    • Les champs Arrivé et Ajouté à l'inventaire sont masqués sur le formulaire Nouvel élément.The Arrived and Added to Inventory fields are hidden on the New Item form.

    • 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.Ideally, the Added to Inventory field would also be hidden on the Edit Item form because it should only be changed to Yes when the item updated event handler has first added the item to the corporate Inventory table. 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.For technical reasons that we'll explain in a later step, a field has to be visible in the Edit Item form if we want to programmatically write to it in an item updated event handler.

  2. Dans la méthode DeployChainStoreComponentsToHostWeb, ajoutez la ligne suivante, juste au-dessus de la ligne RemoteTenantVersion = localTenantVersion.In the DeployChainStoreComponentsToHostWeb method, add the following line, just above the line RemoteTenantVersion = localTenantVersion.

      CreateExpectedShipmentsList();
    

Créer le récepteur d’éléments et d’évènements de listeCreate the list item event receiver

Note

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.If you have been working through this series of articles, you have already configured your development environment for debugging remote event receivers. 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.If you have not done that, see Configure the solution for event receiver debugging before you go any further in this topic.

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.The Office Developer Tools for Visual Studio includes a Remote Event Receiver item that can be added to a SharePoint Add-in solution. 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.However, at the time this article was written, this project item assumes that the list (with which the receiver will be registered) is on the add-in web, and consequently the tools create an add-in web and some SharePoint artifacts in it. Mais le récepteur du complément Chain Store va être enregistré (dans une étape ultérieure) avec la liste Livraisons attendues sur le site web hôte, donc le complément n’a pas besoin d’un site web de complément.But the receiver for the Chain Store add-in is going to be registered (in a later step) with the Expected Shipments list on the host web, so the add-in does not need an add-in web. (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.)(For a reminder of the distinction between add-in webs and host webs, see SharePoint Add-ins.)

Note

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.List and list item event receivers are called remote event receivers (RER) because their code is remote from SharePoint, either in the cloud or in an on-premises server outside the SharePoint farm. Cependant, les événements qui les déclenchent sont dans SharePoint.However, the events that trigger them are in 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.In Solution Explorer, right-click the Services folder in the ChainStoreWeb project, and select Add > WCF Service.

  2. Lorsque vous y êtes invité, nommez le service RemoteEventReceiver1, puis sélectionnez OK.When prompted, name the service RemoteEventReceiver1, and then select OK.

  3. Les outils créent un fichier d’interface, un fichier *.svc et un fichier code-behind.The tools create an interface file, an *.svc file, and a code-behind file. Nous n’avons pas besoin du fichier d’interface IRemoteEventReceiver1.cs, donc supprimez-le.We don't need the interface file IRemoteEventReceiver1.cs, so delete it. (Les outils peuvent avoir ouvert celui-ci automatiquement ; si c’est le cas, fermez-le et supprimez-le.)(The tools may have opened it automatically; if so, close and delete it.)

    Note

    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.When you created the add-in event receivers for the installed and uninstalling events in an earlier article in this series, the Office Developer Tools for Visual Studio added their URLs to the app manifest file. 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.List and list item event receivers are not registered in the app manifest. En revanche, ils sont enregistrés (dans un complément hébergé par un fournisseur) par programme.Instead, they are registered (in a provider-hosted add-in) programmatically. Vous le ferez dans une étape ultérieure.You'll do that in a later step.

  4. Ouvrez le fichier code-behind RemoteEventReceiver1.svc.cs.Open the code-behind file RemoteEventReceiver1.svc.cs. Remplacez tout le contenu par le code suivant.Replace its entire contents with the following code.

      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 :Note the following about this code:

    • L’interface IRemoteEventService est définie dans l’espace de noms Microsoft.SharePoint.Client.EventReceivers.The interface IRemoteEventService is defined in the Microsoft.SharePoint.Client.EventReceivers namespace.

    • 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.There won't be any "before" events handled in the Chain Store add-in, but the ProcessEvent method is required by the IRemoteEventService interface.

  5. Ajoutez le code suivant à la méthode ProcessOneWayEvent.Add the following code to the ProcessOneWayEvent method. L’événement ItemUpdated est le seul géré dans cet exemple, pour que nous puissions utiliser une structure if simple au lieu d’une structure switch.Note that the ItemUpdated event is the only one that this sample will handle, so we could have used a simple if structure instead of a switch. Mais les récepteurs d’événements gèrent généralement plusieurs événements, donc nous voulons que vous puissiez voir le modèle que vous utiliserez plus fréquemment dans vos gestionnaires d’événements en tant que développeur de compléments SharePoint.But event receivers typically handle multiple events, so we want you to see the pattern you'll most commonly be using in your event handlers as a SharePoint add-in developer.

      switch (properties.EventType)
    {
        case SPRemoteEventType.ItemUpdated:
    
        // TODO12: Handle the item updated event.
    
        break;
    }  
    
  6. Remplacez TODO12 par le code suivant.Replace TODO12 with the following code. 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.Again, here, we are using a switch structure when a simple if structure would do because we want you to see the common pattern in SharePoint event receivers.

      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 :The code that responds to the arrival of a shipment should do two things:

    • Ajouter un élément qui est arrivé au magasin dans l’inventaire d’entreprise.Add the item that has arrived at the store into the corporate inventory.

    • 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.Set the Added to Inventory field on the Expected Shipments list to Yes. But this should only happen if the item was successfully added to the inventory.

    Ajoutez le code suivant à la place de TODO13.Add the following code in place of TODO13. Les deux méthodes TryUpdateInventory et RecordInventoryUpdateLocally sont créées dans les étapes ultérieures.The two methods, TryUpdateInventory and RecordInventoryUpdateLocally are created in later steps.

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

      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.Add the following method to the RemoteEventReceiver1 class.

      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.There are five columns on the Expected Shipments list, but we don't want the handler to react to most kinds of updates to an item. 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.For example, if a user corrects the spelling of a supplier's name, the item updated event is triggered, but our handler should do nothing. Le gestionnaire doit agir uniquement lorsque le champ Arrivé est défini sur Oui.The handler should only act when the Arrived field has just been set to Yes.

    Il existe une autre condition qui doit être testée.There's another condition that needs to be tested. 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).Suppose Arrived is set to Yes and the product in the item is added to inventory (and Added to Inventory is set to Yes). 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.But later a user mistakenly changes the Arrived field of a shipment back to No and then fixes his mistake by setting it again to Arrived. L’erreur et le correctif déclenchent l’événement d’élément mis à jour.Both the mistake and the fix trigger the item updated event. 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.The handler won't react to the mistake because it only acts when Arrived is Yes, but it would react to the fix that sets Arrived back to Yes, so the same product and quantity would get added into the inventory a second time. Pour cette raison, le gestionnaire doit agir uniquement lorsque la valeur Ajouté à l’inventaire est Non.For this reason, the handler should only act when the Added to Inventory value is No.

    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.Therefore, the handler needs to know what the values of these fields are just after the user updates the item. L’objet SPRemoteEventProperties comporte une propriété ItemEventProperties.The SPRemoteEventProperties object has an ItemEventProperties property. À son tour, elle comporte une propriété AfterProperties indexée qui conserve les valeurs des champs dans l’élément mis à jour.And, in turn, it has an indexed AfterProperties property that holds the values of the fields in the updated item. Le code suivant utilise ces propriétés pour tester si le gestionnaire doit réagir.The following code uses these properties to test whether the handler should react. Mettez ceci à la place de TODO14.Put this in place of TODO14.

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

     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 :This is mainly SQL and ASP.NET programming, so we don't discuss it in detail, but note:

    • Nous utilisons la propriété ItemEventProperties.WebUrl pour obtenir le nom du client, qui correspond à l'URL du site web hôte.We use the ItemEventProperties.WebUrl property to get the tenant name, which is the host web URL.

    • Nous utilisons à nouveau la propriété AfterProperties pour obtenir les valeurs en matière de quantité et de nom du produit.We use the AfterProperties again to get the values of the product name and quantity.

    • 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.We refer to the product name field as "Title", even though the display name was changed to "Product" (in the CreateExpectedShipmentsList method) because fields are always referred to by their internal names.

  12. Nous n’avons pas encore terminé avec la méthode TryUpdateInventory, mais à ce stade, cela doit ressembler à ce qui suit.We are not finished with the TryUpdateInventory method yet, but at this point it should look like the following.

     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 &amp;&amp; !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.When the TryUpdateInventory method returns true, our handler calls a method (not yet written) that updates the same item in the Expected Shipments list by setting the Added to Inventory field to Yes. Il s’agit d’un événement d’élément mis à jour, donc le gestionnaire est rappelé.This is itself an item update event, so the handler is called again. (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é.)(The fact that the Added to Inventory field is now Yes prevents the handler from adding the same shipment to inventory a second time, but the handler is still called.)

    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.SharePoint behaves a little differently when the item updated event is triggered by a programmatic update: it only includes in the AfterProperties the fields that changed in the update. Par conséquent, le champ Arrivé n’apparaît pas, car seul le champ Ajouté à l’inventaire a été modifié.So the Arrived field won't be present because only the Added to Inventory field changed.

    La ligne...The line...

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

    ...génère une exception KeyNotFoundException....throws a 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.There is more than one way to resolve this problem. In this sample we are going to catch the exception and use the catch block to ensure that the successFlag is set to false. Doing this ensures that the item isn't updated a third time.

    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.Put everything in the method that is between the first line bool successFlag = false; and the last line return successFlag; in a try block.

  14. Ajoutez le bloc catch suivant juste sous le bloc try.Add the following catch block just under the try block.

     catch (KeyNotFoundException)
    {
       successFlag = false;
    }
    

    Note

    L’exception KeyNotFoundException est également la raison pour laquelle nous devons laisser le champ Ajouté à l’inventaire visible sur le formulaire de modification d’élément.The KeyNotFoundException is also the reason why we have to leave the Added to Inventory field visible on the Edit Item form. SharePoint n’inclut pas les champs masqués dans le formulaire de modification d’élément dans AfterProperties.SharePoint does not include fields that are hidden on the Edit Item form in AfterProperties.

  15. La méthode complète doit désormais se présenter comme suit :The entire method should now look like the following.

     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 &amp;&amp; !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.Add the following method to the RemoteEventReceiver1 class.

     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.By now this pattern of code is familiar from earlier articles in this series. Mais notez une différence :But note one difference:

    • 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.The code gets the ClientContext object by calling TokenHelper.CreateRemoteEventReceiverClientContext method instead of the SharePointContext.CreateUserClientContextForSPHost method as we used in code that called into SharePoint from pages, such as the EmployeeAdder page.

    • 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.The primary reason for having different methods to get a ClientContext object is that SharePoint passes the information needed to create such objects differently to event receivers from how it passes it to 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.For event receivers, it passes an SPRemoteEventProperties object, but for pages it passes a special field, called a context token, in the body of the request that launches the add-in page.

  17. Enregistrez et fermez le fichier de code du récepteur.Save and close the receiver code file.

Enregistrer le récepteurRegister the receiver

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.The final task is to tell SharePoint that we have a custom receiver that we want SharePoint to call whenever an item on the Expected Shipments list is updated.

  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).Open the SharePointContentDeployer.cs file and add the following line to the DeployChainStoreComponentsToHostWeb method, just under the line that creates the Expected Shipments list (we'll add this method in the next step). Nous transmettons à la méthode l’objet HttpRequest que la page de démarrage du complément a transmis à la méthode DeployChainStoreComponentsToHostWeb.Note that we are passing to the method the HttpRequest object that the add-in's start page passed to the DeployChainStoreComponentsToHostWeb method.

      RegisterExpectedShipmentsEventHandler(request);
    
  2. Ajoutez la méthode suivante à la classe SharePointComponentDeployer.Add the following method to the SharePointComponentDeployer class.

      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.Replace TODO16 with the following lines. 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.Note that there is a lightweight CreationInformation class for event receivers just as there is for lists and list items.

    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.Now you need to tell SharePoint the URL of the event receiver. 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.In production, it's going to be at the same domain as the remote pages, with the path of /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.Because the handler is being registered in first-run logic from the add-in's start page, the domain is in the host header of the HttpRequest object for the request that called the 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.Our code has passed that object from the page to the DeployChainStoreComponentsToHostWeb method, which itself passed it to the RegisterExpectedShipmentsEventHandler method. Nous pouvons donc définir l’URL du récepteur avec le code suivant.So we can set the receiver's URL with the following code.

    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.Unfortunately, this won't work when you are debugging the add-in from 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.When you are debugging, the receiver is hosted in the Azure Service Bus, not in the localhost URL where the remote pages are hosted. 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#.We need to set distinct URLs for the receiver depending on whether we are debugging or not, so replace TODO17 with the following structure that uses C# compiler directives. 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).Note that in debug mode the receiver's URL is read from a web.config setting (you will create this setting in a later step).

      #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.The entire RegisterExpectedShipmentsEventHandler method should now look like the following.

      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.Add the following using statement to the top of the file.

      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 :To ensure that DEBUG is true if and only if the add-in is being debugged, carry out the following subprocedure:

    1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet ChainStoreWeb et sélectionnez Propriétés.In Solution Explorer, right-click the ChainStoreWeb project and select Properties.

    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.Open the Build tab of the Properties, and then select Debug from the Configuration drop-down at the top.

    3. Vérifiez que la case à cocher Définir la constante DEBUG est activée (elle l’est généralement par défaut).Ensure that the Define DEBUG constant check box is selected (it usually is by default). La capture d’écran ci-dessous présente le paramétrage correct.The following screen shot shows the proper setting.

      Figure 1. Sous-onglet Générer de l’onglet Propriétés dans Visual StudioFigure 1. Build sub-tab of the Properties tab in Visual Studio

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

    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).Change the Configuration drop-down to Release, and then ensure that the Define DEBUG constant check box is not selected (it usually is not by default). La capture d’écran ci-dessous présente le paramètre correct.The following screenshot shows the proper setting.

      Figure 2. Sous-onglet Générer de l’onglet Propriétés avec case à cocher désactivéeFigure 2. Build sub-tab of the Properties tab with check box cleared

      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.If you made any changes, save and then close the Properties tab.

  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).Open the web.config file, and add the following markup as a child of the appSettings element (we get the value of the setting in the next section).

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

Obtenir l’URL du récepteur pour réaliser le débogageGet the receiver URL for debugging

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.The add-in event and list item event receivers are Windows Communication Service (WCF) services, and every WCF service knows its own endpoint and stores it in multiple places, including the **System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri object.

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.When you are debugging, the add-in receiver is hosted at an Azure Service Bus endpoint that is almost the same as the endpoint for the list item receiver. 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 ».The difference is that the URL of the add-in endpoint ends with "AppEventReceiver.svc", but the list item receiver's URL ends with "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.So we can get the URL of the endpoint in the add-in receiver, make a small change to the end of it, and then use it as the value of our web.config RERdebuggingServiceBusUrl setting.

  1. Ouvrez le fichier AppEventReceiver.svc.cs dans le dossier Services du projet ChainStoreWeb.Open the AppEventReceiver.svc.cs file in the Services folder of the ChainStoreWeb project.

  2. Ajoutez la ligne suivante en tant que toute première ligne de la méthode ProcessEvent.Add the following as the very first line in the ProcessEvent method.

      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.Add a breakpoint to the very next line of the method.

  4. Sélectionne F5 pour déboguer le complément.Select F5 to debug the add-in. É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.Because web.config is open and Office Developer Tools for Visual Studio changes a setting in it every time you select F5, you are prompted to reload it. Sélectionnez Oui.Select Yes.

  5. Une fois le point d’arrêt atteint, pointez le curseur sur la variable debugEndpoint.When the breakpoint is hit, hover the cursor over the debugEndpoint variable. Lorsque la bulle d’informations de Visual Studio s’affiche, sélectionnez la flèche vers le bas, puis Visualiseur de texte.When the Visual Studio Data Tip appears, select the down arrow, and then select Text Visualizer.

    Figure 3. Visualiseur de texte Visual Studio contenant une URL Azure Service BusFigure 3. Visual Studio text visualizer with an Azure Service Bus URL

    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.Copy the string value from the visualizer and paste it somewhere.

  7. Fermez le visualiseur, puis arrêtez le débogage dans Visual Studio.Close the visualizer, and then stop debugging in 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.Delete or comment out the line you added in the second step of this procedure, and then delete the breakpoint as well.

  9. Dans la chaîne que vous avez copiée, remplacez « AppEventReceiver.svc » qui se trouve à la fin par « RemoteEventReceiver1.svc ».In the string you copied, replace the "AppEventReceiver.svc" at the end with "RemoteEventReceiver1.svc".

  10. Copiez et collez l'URL modifiée en tant que valeur de la clé RERdebuggingServiceBusUrl dans le fichier web.config.Copy and paste the modified URL as the value of the RERdebuggingServiceBusUrl key in the web.config file.

Note

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.Manually copying the service bus URL and pasting (a modified version of) it into the web.config is not the only way of dealing with the need for a different URL when debugging a remote event receiver when it is running in 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.We could programmatically store the value of System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri somewhere in SharePoint or the remote database, and then have our first-run code read it and assign it to the receiver.ReceiverUrl property. 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.We could register the list item event receiver as part of the add-in installed event handler. 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ù.We could then programmatically read System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri, modify it, and assign it to receiver.ReceiverUrl without having to store it anywhere.

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.This strategy requires that the Expected Shipments list also be created in the add-in installed event handler because it would have to exist before the handler could be registered with it.

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).Note also that we could combine our add-in event receiver and list item event receiver into a single receiver (that is, the same .svc and .svc.cs files). Dans ce cas, aucune modification de l’URL n’est nécessaire avant de l’utiliser en tant que valeur de receiver.ReceiverUrl.In that case, no modification of the URL is necessary before using it as the value of receiver.ReceiverUrl.

Exécutez le complément et testez le récepteur d’élément de listeRun the add-in and test the list item receiver

  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.Open the Site Contents page of the Hong Kong store's website, and remove the Expected Shipments list if there is one.

  2. Utilisez la touche F5 pour déployer et exécuter votre complément.Use the F5 key to deploy and run your add-in. Visual Studio héberge l’application web distante dans IIS Express et héberge la base de données SQL dans SQL Express.Visual Studio hosts the remote web application in IIS Express and hosts the SQL database in SQL Express. Il effectue une installation temporaire du complément sur votre site SharePoint de test et exécute immédiatement celui-ci.It also makes a temporary installation of the add-in on your test SharePoint site and immediately runs the add-in. Vous êtes invité à accorder des autorisations pour le complément avant l’ouverture de sa page de démarrage.You are prompted to grant permissions to the add-in before its start page opens.

  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.When the add-in's start page opens, select the Back to Site button on the chrome control at the top.

  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.From the home page of the Hong Kong store, go to the Site Contents page and open the Expected Shipments list.

  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.Create an item, and on the new item form, notice that the Arrived and Added to Inventory fields do not appear.

  6. Une fois l’élément créé, rouvrez-le à des fins de modification.After the item is created, reopen it for editing. Activez la case à cocher Arrivé et enregistrez l’élément.Select the Arrived check box and save the item. Cela déclenche l’événement d’élément mis à jour.This triggers the item updated event. 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).The item is added to inventory and the value of the Added to Inventory field changes to Yes (you may have to refresh the page to see the change to Added to Inventory).

  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.Use the browser's back button until you are back at the start page for the Chain Store add-in, and then select the Show Inventory button. L’élément que vous avez marqué comme Arrivé est maintenant répertorié.The item you marked as Arrived is now listed.

  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.Go back to the Expected Shipments list and add another item with exactly the same product name and supplier name, but a different quantity.

  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.After the item is created, reopen it for editing. Change the value of Arrived to Yes and save the item.

  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.Use the browser's back button until you are back at the start page for the Chain Store add-in, and then select the Show Inventory button. 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.There is still just one item for the product name and supplier, but the quantity is now the total of the two items on the Expected Shipments list.

  11. Pour terminer la session de débogage, fermez la fenêtre du navigateur ou arrêtez le débogage dans Visual Studio.To end the debugging session, close the browser window or stop debugging in Visual Studio. Quand vous appuyez sur F5, Visual Studio retire la version précédente du complément et installe la dernière.Each time you select F5, Visual Studio retracts the previous version of the add-in and installs the latest one.

  12. Vous allez travailler avec ce complément et la solution Visual Studio dans d’autres articles. Nous vous recommandons donc de retirer le complément une dernière fois quand vous avez terminé de travailler et que vous ne comptez pas le réutiliser pendant un moment.You will work with this add-in and Visual Studio solution in other articles, and it's a good practice to retract the add-in one last time when you are done working with it for a while. Cliquez avec le bouton droit sur le projet dans l’Explorateur de solutions et sélectionnez Retirer.Right-click the project in Solution Explorer and select Retract.

Étapes suivantesNext steps

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.To learn how to publish your add-in to a SharePoint site, see Deploying and installing SharePoint Add-ins: methods and options. Vous pouvez également poursuivre les tâches de développement des compléments SharePoint dans les pages suivantes :You can also pursue advanced work in SharePoint add-in development on the following pages: