Compartir a través de


Controlar eventos de elementos de lista en el complemento hospedado por el proveedor

Este es el décimo de una serie de artículos sobre los conceptos básicos de desarrollo de un Complementos de SharePoint hospedados por el proveedor. Primero debe familiarizarse con Complementos de SharePoint y con los anteriores artículos de esta serie, los cuales puede encontrar en Introducción a la creación de complementos de SharePoint hospedados por el proveedor.

Nota:

Si a lo largo de esta serie ha estado trabajando sobre los complementos hospedados por el proveedor, tiene una solución de Visual Studio que puede usar para continuar con este tema. También puede descargar el repositorio que encontrará en SharePoint_Provider-hosted_Add-Ins_Tutorials y abrir el archivo BeforeRER.sln.

En un artículo anterior de esta serie vio que cuando se coloca un pedido, se agrega a la tabla Pedidos en la base de datos corporativa y se agrega automáticamente un elemento para ella a la lista de Envíos esperados. Cuando llegue a la tienda local, un usuario establece la columna Entregado en . Cambiar el valor de un campo de un elemento crea un evento actualizado que puede agregar un controlador personalizado.

En este artículo, cree un controlador para el evento del elemento de la lista y, después, lo implementará mediante programación en la lógica de primera ejecución del complemento de SharePoint. El controlador agrega el elemento a la tabla Inventario en la base de datos corporativa. Entonces, establece la columna Agregado al inventario de la lista de Envíos esperados a . También aprenderá a impedir que un segundo evento actualizado de un elemento active una serie infinita de eventos actualizados de elemento.

Implementar con programación la lista de envíos esperados

Nota:

La configuración de inicio de proyectos en Visual Studio tiende a revertir a los valores predeterminados cada vez que la solución se vuelve a abrir. Siempre debe seguir estos pasos inmediatamente después de abrir de nuevo la solución de ejemplo de esta serie de artículos:

  1. Haga clic con el botón derecho en el nodo de la solución en la parte superior del Explorador de soluciones y después seleccione Establecer proyectos de inicio.
  2. Asegúrese de que los tres proyectos estén establecidos en Iniciar en la columna Acción.
  1. En el Explorador de soluciones, abra el archivo Utilities\SharePointComponentDeployer.cs en el proyecto ChainStoreWeb. Agregue el método siguiente a la clase 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();
     	}
          }
      }
    

    Este código no presenta funcionalidades que ya se han visto en un artículo anterior de esta serie, pero tenga en cuenta lo siguiente:

    • Establece el atributo Required del campo Cantidad en TRUE, por lo que el campo siempre debe tener un valor. A continuación, se establece el valor predeterminado 1.

    • Los campos Entregado y Agregado al inventario están ocultos en el formulario Nuevo elemento.

    • Lo ideal es que el campo Agregado al inventario también se ocultara en el formulario Editar elemento porque solo debe cambiarse a cuando el controlador de eventos actualizado del elemento ha agregado primero el elemento a tabla corporativa Inventario. Por razones técnicas que le explicaremos en un paso posterior, un campo debe estar visible en el formulario Editar elemento si queremos escribir en él mediante programación en un controlador de eventos actualizado de elemento.

  2. En el método DeployChainStoreComponentsToHostWeb, agregue la siguiente línea justo encima de la línea RemoteTenantVersion = localTenantVersion.

      CreateExpectedShipmentsList();
    

Crear el receptor de eventos de elementos de lista

Nota:

Si ha estado trabajando en esta serie de artículos, ya ha configurado su entorno de desarrollo para depurar receptores de eventos remotos. Si no lo ha hecho, consulte Configurar la solución para la depuración de receptores de eventos antes de continuar en este tema.

Office Developer Tools para Visual Studio incluye un elemento de Receptor de eventos remotos que se puede agregar a una solución de complemento de SharePoint. Sin embargo, en el momento en que se escribió este artículo, este elemento de proyecto supone que la lista (con la que el receptor se registrará) se encuentra en la web del complemento y, por consiguiente, las herramientas crean una web de complemento y algunos objetos de SharePoint en ella. Pero el receptor para el complemento Chain Store va a registrarse (en un paso posterior) con la lista Envíos esperados en la web del host, por lo que el complemento no necesita un complemento web. (Para recordar la distinción entre web de host y web de complemento, consulte Complementos de SharePoint).

Nota:

Los receptores de eventos de lista y elemento de lista se denominan receptores de eventos remotos (RER) porque su código es remoto desde SharePoint, ya sea en la nube o en un servidor local fuera de la granja de servidores de SharePoint. Sin embargo, los eventos que los desencadenan son de SharePoint.

  1. En el Explorador de soluciones, haga clic con el botón derecho en la carpeta Servicios del proyecto ChainStoreWeb y seleccione Agregar>Servicio WCF.

  2. Cuando se le pida, especifique el nombre RemoteEventReceiver1 para el servicio y, después, seleccione Aceptar.

  3. Las herramientas crean un archivo de interfaz, un archivo *.svc y un archivo de código subyacente. No es necesario el archivo de la interfaz IRemoteEventReceiver1.cs, por lo que puede eliminarlo. (Las herramientas podrían haberlo abierto automáticamente, si es así, ciérrelo y elimínelo).

    Nota:

    Al crear los receptores de eventos de complemento para los eventos instalados y de desinstalación en un artículo anterior de esta serie, Office Developer Tools para Visual Studio agrega sus direcciones URL al archivo de manifiesto de aplicación. Los receptores de eventos de elemento de lista y de lista no se registran en el manifiesto de aplicación. En su lugar, se registran (en un complemento hospedado por el proveedor) mediante programación. Lo hará en un paso posterior.

  4. Abra el archivo de código subyacente RemoteEventReceiver1.svc.cs. Reemplace todo el contenido por el siguiente código.

       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)
     	{
    
     	}
         }
     }
    

    Tenga en cuenta lo siguiente en relación con este código:

    • La interfaz IRemoteEventService se define en el espacio de nombres Microsoft.SharePoint.Client.EventReceivers.

    • No habrá eventos "antes" controlados en el complemento Chain Store, pero la interfaz IRemoteEventService requiere el método ProcessEvent.

  5. Agregue el siguiente código al método ProcessOneWayEvent. Tenga en cuenta que el evento ItemUpdated es el único que se controlará en este ejemplo, por lo que podríamos haber usado una estructura if sencilla en lugar de un switch. Pero los receptores de eventos normalmente controlan varios eventos, por lo que queremos que vea el patrón que normalmente se usa en los controladores de eventos como desarrollador de complementos de SharePoint.

       switch (properties.EventType)
     {
         case SPRemoteEventType.ItemUpdated:
    
     	// TODO12: Handle the item updated event.
    
     	break;
     }  
    
  6. Reemplace TODO12 por el código siguiente. De nuevo, en este caso, estamos usando una estructura switch cuando bastaría con una estructura if sencilla, porque queremos que vea el patrón común de receptores de eventos de SharePoint.

       switch (properties.ItemEventProperties.ListTitle)
     {
         case "Expected Shipments":
    
     	// TODO13: Handle the arrival of a shipment.
    
     	break;
     }
    
  7. El código que responde a la llegada de un envío tiene que realizar dos acciones:

    • Agregar el elemento que llegó a la tienda en el inventario corporativo.

    • Establecer el campo Agregado al inventario de la lista Envíos esperados en . Pero esto solo debería suceder si el elemento se agregó correctamente al inventario.

    Agregue el siguiente código en lugar de TODO13. Los dos métodos, TryUpdateInventory y RecordInventoryUpdateLocally, se crean en los pasos siguientes.

       bool updateComplete = TryUpdateInventory(properties);
     if (updateComplete)
     {
         RecordInventoryUpdateLocally(properties);
     }
    
  8. El método ProcessOneWayEvent ahora debería ser similar al siguiente.

       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. Agregue el método siguiente a la clase 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. Hay cinco columnas en la lista Envíos esperados, pero no queremos que el controlador reaccione ante la mayoría de los tipos de actualizaciones a un elemento. Por ejemplo, si un usuario corrige la ortografía del nombre de un proveedor, se desencadena el evento actualizado del elemento, pero el controlador no debe hacer nada. El controlador debe actuar solo cuando el campo Entregado se acaba de establecer en .

    Hay otra condición que debe probarse. Supongamos que Entregado está establecido en y el elemento se encuentra en el inventario (y Agregado al inventario está establecido en ). Pero más adelante un usuario cambia por error el campo Entregado de un envío a No y, a continuación, se corrige el error estableciéndolo de nuevo en . El error y la corrección desencadenan el evento actualizado del elemento. El controlador no reaccionará ante el error porque solo actúa cuando Entregado es , pero reaccionará ante la corrección que establece de nuevo Entregado en , por lo que el mismo producto y la cantidad se añadieron al inventario una segunda vez. Por este motivo, el controlador solo debe actuar cuando el valor Agregado al inventario es No.

    Por lo tanto, el controlador necesita saber cuáles son los valores de estos campos justo después de que el usuario actualice el elemento. El objeto SPRemoteEventProperties tiene una propiedad ItemEventProperties. Y, a su vez, tiene una propiedad indexada AfterProperties que contiene los valores de los campos del elemento actualizado. El siguiente código utiliza estas propiedades para probar si el controlador debe reaccionar o no. Ponga esto en lugar 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. Cambie TODO15 por el código siguiente.

     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();
    }
    

    Esto es principalmente programación de SQL y ASP.NET, por lo que no lo explicaremos en detalle, pero tenga en cuenta lo siguiente:

    • Usamos la propiedad ItemEventProperties.WebUrl para obtener el nombre del inquilino, que es la dirección URL de la web de host.

    • Usamos las AfterProperties nuevamente para obtener los valores de nombre y cantidad de producto.

    • Nos referimos al campo de nombre del producto como "Title", aunque el nombre para mostrar se cambió a "Product" (en el método CreateExpectedShipmentsList ) porque sus nombres internos siempre hacen referencia a los campos.

  12. No hemos terminado con el método TryUpdateInventory todavía, pero en este momento debe tener un aspecto parecido al siguiente.

     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. Cuando el método TryUpdateInventory devuelve true, nuestro controlador llama a un método (aún no se ha escrito) que actualiza el mismo elemento en la lista Envíos esperados al establecer el campo Agregado al inventario en . Se trata de un evento actualizado del elemento, por lo que se llama al controlador de nuevo. (El hecho de que el campo Agregado al inventario ahora es impide al controlador agregar el mismo envío al inventario por segunda vez, pero se sigue llamando al controlador).

    SharePoint se comporta de forma un poco diferente cuando el evento de actualización del elemento se desencadena mediante una actualización mediante programación: solo incluye en AfterProperties los campos que cambiaron en la actualización. Por lo tanto, el campo Llegado no estará presente porque solo ha cambiado el campo Agregado al inventario .

    La línea

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

    iniciará una excepción KeyNotFoundException.

    Existe más de una forma de resolver este problema. En este ejemplo, vamos a detectar la excepción y usar el bloque catch para asegurarse de successFlag que está establecido en false. Esto garantiza que el elemento no se actualiza una tercera vez.

    Coloque todo en el método entre la primera línea bool successFlag = false; y la última línea return successFlag; en un bloque try.

  14. Agregue el siguiente bloque catch justo debajo del bloque try.

     catch (KeyNotFoundException)
    {
       successFlag = false;
    }
    

    Nota:

    KeyNotFoundException también es el motivo por el que tenemos que dejar el campo Agregado al inventario visible en el formulario Editar elemento. SharePoint no incluye los campos que están ocultos en el formulario Editar elemento en AfterProperties.

  15. El método completo tendrá la siguiente apariencia.

     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. Agregue el método siguiente a la clase 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();
       }
    }
    

    Desde ahora esta trama de código está familiarizada con artículos anteriores de esta serie. Pero tenga en cuenta una diferencia:

    • El código obtiene el objeto ClientContext objeto al llamar al método TokenHelper.CreateRemoteEventReceiverClientContext en lugar del método SharePointContext.CreateUserClientContextForSPHost como se utilizaba en el código que llamaba a SharePoint de las páginas, como la página EmployeeAdder.

    • La razón principal para tener diferentes métodos para obtener un objeto ClientContext es que SharePoint pasa la información necesaria para crear estos objetos de forma diferente para los receptores de eventos como pasa a las páginas. Para los receptores de eventos, pasa un objeto SPRemoteEventProperties, pero para las páginas pasa un campo especial, llamado un token de contexto, en el cuerpo de la solicitud que inicia la página del complemento.

  17. Guarde y cierre el archivo del código del receptor.

Registrar el receptor

La tarea final es informar a SharePoint de que tenemos un receptor personalizado al que queremos que SharePoint llame cada vez que se actualiza un elemento en la lista Envíos esperados.

  1. Abra el archivo SharePointComponentDeployer.cs y agregue la siguiente línea al método DeployChainStoreComponentsToHostWeb, justo debajo de la línea que crea la lista Expected Shipments (este método se creará en el paso siguiente). Tenga en cuenta que pasamos al método el objeto HttpRequest que página de inicio del complemento pasó al método DeployChainStoreComponentsToHostWeb.

      RegisterExpectedShipmentsEventHandler(request);
    
  2. Agregue el método siguiente a la clase 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. Cambie TODO16 por las líneas siguientes. Tenga en cuenta que existe una clase ligera CreationInformation tal como existe para las listas y los elementos de lista.

     EventReceiverDefinitionCreationInformation receiver = new EventReceiverDefinitionCreationInformation();
     receiver.ReceiverName = "ExpectedShipmentsItemUpdated";
     receiver.EventType = EventReceiverType.ItemUpdated;
    
      // TODO17: Set the URL of the receiver.
    
     expectedShipmentsList.EventReceivers.Add(receiver);
    
    
  4. Ahora, deberá indicar a SharePoint la dirección URL del receptor de eventos. En la producción, va a estar en el mismo dominio que las páginas remotas, con la ruta de /Services/RemoteEventReceiver1.svc. Debido a que el controlador se ha registrado en la lógica de primera ejecución de la página de inicio del complemento, el dominio está en el encabezado de host del objeto HttpRequest para la solicitud que llama a la página. Nuestro código ha pasado ese objeto desde la página al método DeployChainStoreComponentsToHostWeb, el cual pasa por sí mismo al método RegisterExpectedShipmentsEventHandler. Por lo que podemos establecer la URL del receptor con el siguiente código.

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

    Desafortunadamente, esto no funciona al depurar el complemento de Visual Studio. Al depurar, el receptor se hospeda en Azure Service Bus, no en la dirección URL de host local que hospeda las páginas remotas. Es necesario establecer URL distintas para el receptor según si estamos depurando o no, por lo que reemplace TODO17 con la siguiente estructura que usa directivas de compilación de C#. Tenga en cuenta que en modo de depuración la dirección URL del receptor se lee desde una configuración de web.config (creará esta configuración en un paso posterior).

       #if DEBUG
     		    receiver.ReceiverUrl = WebConfigurationManager.AppSettings["RERdebuggingServiceBusUrl"].ToString();
     #else
     		    receiver.ReceiverUrl = "https://" + request.Headers["Host"] + "/Services/RemoteEventReceiver1.svc"; 
     #endif
    
    
  5. Todo el método RegisterExpectedShipmentsEventHandler debería ser similar al siguiente.

       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. Agregue la siguiente instrucción using a la parte superior del archivo.

      using System.Web.Configuration;
    
  7. Para asegurarse de que DEBUG es verdadero (y solo si se depura el complemento), realice el siguiente procedimiento secundario:

    1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto ChainStoreWeb y seleccione Propiedades.

    2. Abra la pestaña Crear de Propiedades y, después, seleccione Depurar en la lista desplegable Configuración de la parte superior.

    3. Asegúrese de que la casilla Definir constante DEBUG está seleccionada (normalmente lo está de forma predeterminada). La siguiente captura de pantalla muestra la configuración adecuada.

      Figura 1. Subpestaña Compilar de la pestaña Propiedades en Visual Studio

      La subpestaña Compilar de la pestaña Propiedades de Visual Studio. La lista desplegable Configuración se establece en Depurar. La casilla de verificación

    4. Cambie la lista desplegable de Configuración a Liberar y, después, asegúrese de que la casilla Definir constante DEBUGno esté activada (de forma predeterminada, no suele estar activada). En la siguiente captura de pantalla se muestra la configuración adecuada.

      Figura 2. Subpestaña Compilar de la pestaña Propiedades con la casilla desactivada

      La pestaña secundaria Compilar de la pestaña Propiedades. En la lista desplegable Configuración se indica la versión. La casilla para

    5. Si realizó algún cambio, guarde y cierre la ficha Propiedades.

  8. Abra el archivo web.config y agregue el marcado siguiente como elemento secundario del elemento appSettings (en la sección siguiente, obtendremos el valor de la configuración).

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

Obtener la URL del receptor para depuración

Los receptores de eventos de elemento de lista y de eventos de complemento son servicios Windows Communication Service (WCF) y cada servicio WCF conoce su propio punto de conexión y lo almacena en varios lugares, como el objeto System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri.

Al depurar, el receptor del complemento está hospedado en un punto de conexión de Azure Service Bus que es casi lo mismo que el punto de conexión para el receptor del elemento de la lista. La diferencia es que la dirección URL del punto de conexión del complemento termina con "AppEventReceiver.svc", pero la dirección URL del receptor del elemento de lista termina con "RemoteEventReceiver1.svc". Por lo tanto, podemos obtener la dirección URL del punto de conexión en el receptor del complemento, realizar un pequeño cambio al final del mismo y, a continuación, usarlo como el valor de nuestra web.config configuración RERdebuggingServiceBusUrl .

  1. Abra el archivo AppEventReceiver.svc.cs en la carpeta Servicios del proyecto ChainStoreWeb.

  2. Agregue lo siguiente como primera línea en el método ProcessEvent.

      string debugEndpoint = System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri.ToString(); 
    
  3. Agregue un punto de interrupción a la siguiente línea del método.

  4. Seleccione F5 para depurar el complemento. Como web.config está abierto y Office Developer Tools para Visual Studio cambia un valor cada vez que se selecciona F5, se le solicita que lo vuelva a cargar. Seleccione .

  5. Cuando se alcance punto de interrupción, sitúe el cursor sobre la variable debugEndpoint. Cuando aparezca la sugerencia de los datos de Visual Studio, seleccione la flecha hacia abajo y, a continuación, seleccione Visualizador de texto.

    Figura 3. Visualizador de texto de Visual Studio con una dirección URL de Azure Service Bus

    Un visualizador de texto de Visual Studio donde se muestra una dirección URL de Azure Service Bus.

  6. Copie el valor de cadena desde el visualizador y péguelo en algún otro lugar.

  7. Cierre el visualizador y, a continuación, detenga la depuración en Visual Studio.

  8. Elimine o comente la línea que agregó en el segundo paso de este procedimiento y, a continuación, elimine el punto de interrupción también.

  9. En la cadena que copió, cambie “AppEventReceiver.svc” al final por “RemoteEventReceiver1.svc”.

  10. Copie y pegue la dirección URL modificada como valor de la clave RERdebuggingServiceBusUrl en el archivo web.config.

Nota:

Copiar manualmente la dirección URL del bus de servicio y pegarla (tras modificarla) en el archivo web.config no es la única manera de obtener una dirección URL diferente al depurar un receptor de eventos remotos cuando se está ejecutando en producción. Mediante programación podríamos almacenar el valor de System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri en algún lugar de SharePoint o la base de datos remota y, a continuación, hacer que nuestro código de la primera vista lo lea y asigne a la propiedad receiver.ReceiverUrl. Podríamos registrar el receptor de eventos del elemento de la lista como parte del controlador de eventos de instalado del complemento. Mediante programación, a continuación, podemos leer System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri, modificarlo y asignarlo a receiver.ReceiverUrl sin tener que almacenarlo.

Esta estrategia requiere que la lista Envíos esperados también se cree en el controlador de eventos instalado del complemento, porque ya debería existir antes de que el controlador se pueda registrar.

Tenga en cuenta también que se pueden combinar el receptor de eventos de complemento y el receptor de eventos del elemento de la lista en un único receptor (es decir, los mismos archivos .svc y .svc.cs). En ese caso, no es necesario modificar la dirección URL antes de usarlo como valor de receiver.ReceiverUrl.

Ejecutar el complemento y probar el receptor del elemento de lista

  1. Abra la página Contenidos del sitio del sitio web de la tienda Hong Kong y quite la lista Envíos esperados (en caso de haber una).

  2. Use la tecla F5 para implementar y ejecutar el complemento. Visual Studio hospeda la aplicación web remota en IIS Express y hospeda la base de datos SQL en SQL Express. También instala temporalmente el complemento en el sitio de SharePoint de prueba y ejecuta el complemento inmediatamente. Se le solicitará que conceda permisos al complemento antes de que se abra su página de inicio.

  3. Cuando se abra la página de inicio del complemento, seleccione el botón Volver al sitio en el control de cromo de la parte superior.

  4. En la página principal de la tienda Hong Kong, vaya a la página Contenidos del sitio y abra la lista Envíos esperados.

  5. Cree un elemento y, en el formulario del nuevo elemento, observe que los campos Entregado y Agregado al inventario no aparecen.

  6. Después de crear el elemento, vuelva a abrirlo para modificarlo. Seleccione la casilla Entregado y guarde el elemento. Esto desencadena el evento actualizado del elemento. El elemento se agrega al inventario y el valor del campo Agregado al inventario cambia a (puede que tenga que actualizar la página para ver los cambios en Agregado al inventario).

  7. Use el botón Atrás del explorador hasta volver a la página de inicio para el complemento Chain Store y, después, seleccione el botón Mostrar inventario. El elemento que ha marcado como Entregado aparece ahora.

  8. Vuelva a la lista Envíos esperados y agregue otro elemento que tenga exactamente el mismo nombre de producto y nombre del proveedor, pero con una cantidad distinta.

  9. Una vez creado el elemento, vuelva a abrirlo para su edición. Cambie el valor de Llegado a y guarde el elemento.

  10. Use el botón Atrás del explorador hasta volver a la página de inicio para el complemento Chain Store y, después, seleccione el botón Mostrar inventario. Todavía hay solo un elemento para el proveedor y el nombre del producto, pero la cantidad ahora es el total de los dos elementos en la lista Envíos esperados.

  11. Para finalizar la sesión de depuración, cierre la ventana del explorador o detenga la depuración en Visual Studio. Cada vez que seleccione F5, Visual Studio retira la versión anterior del complemento e instala la más reciente.

  12. Trabajará con este complemento y con la solución de Visual Studio en otros artículos. Es recomendable que vuelva a retirar el complemento cuando deje de trabajar con él por un tiempo. Haga clic con el botón derecho en el proyecto en el Explorador de soluciones y seleccione Retirar.

Pasos siguientes

Para obtener más información sobre cómo publicar su complemento en SharePoint, vea Implementar e instalar complementos de SharePoint: Métodos y opciones. También puede encontrar trabajo avanzado en el desarrollo de complemento de SharePoint en las páginas siguientes: