Partilhar via


Manipular eventos de item de lista no suplemento hospedado pelo provedor

Este é o décimo de uma série de artigos sobre os conceitos básicos do desenvolvimento de suplementos do SharePoint hospedados pelo provedor. Primeiro, você deve estar familiarizado com suplementos do SharePoint e os artigos anteriores desta série, que podem ser encontrados em Introdução à criação de suplementos do SharePoint hospedados pelo provedor.

Observação

Se você trabalhou com esta série sobre suplementos hospedados pelo provedor, você terá uma solução do Visual Studio que pode ser usada para continuar com este tópico. Você também pode baixar o repositório em SharePoint_Provider-hosted_Add-Ins_Tutorials e abrir o arquivo BeforeRER.sln.

Você viu em um artigo anterior nesta série que, quando um pedido é colocado, ele é adicionado à tabela Pedidos no banco de dados corporativo e um item para ele é adicionado automaticamente à lista Remessas Esperadas . Quando ele chega ao repositório local, um usuário define a coluna Chegou como Sim. Alterar um valor de campo para um item cria um evento atualizado de item para o qual você pode adicionar um manipulador personalizado.

Neste artigo, você cria um manipulador para este evento de item de lista e, em seguida, implanta-o programaticamente na lógica de primeira execução do Suplemento do SharePoint. Seu manipulador adiciona o item à tabela Inventário no banco de dados corporativo. Em seguida, ele define a coluna Adicionado ao Inventário da lista Remessas Esperadas como Sim. Você também aprenderá a impedir que este segundo evento atualizado de item desative uma série infinita de eventos atualizados de item.

Implantar programaticamente a lista de remessas esperadas

Observação

As configurações de Projetos de Inicialização no Visual Studio tendem a reverter para padrões sempre que a solução é reaberta. Sempre siga estas etapas imediatamente após a reabertura da solução de exemplo nesta série de artigos:

  1. Clique com botão direito do mouse no nó da solução na parte superior do Gerenciador de Soluções e então selecione Definir Projetos de Inicialização.
  2. Verifique se todos os três projetos estão definidos como Iniciar na coluna Ação.
  1. Em Gerenciador de Soluções, abra o arquivo Utilities\SharePointComponentDeployer.cs no projeto ChainStoreWeb. Adicione o método a seguir à classe SharePointComponentDeployer.

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

    Este código não apresenta nenhuma funcionalidade que você ainda não tenha visto em um artigo anterior desta série, mas observe o seguinte:

    • Ele define o atributo Obrigatório do campo Quantidade como TRUE , portanto, o campo deve sempre ter um valor. Em seguida, ele define o valor padrão como 1.

    • Os campos Arrived e Added to Inventory estão ocultos no formulário Novo Item.

    • Idealmente, o campo Adicionado ao Inventário também estaria oculto no formulário Editar Item porque ele só deve ser alterado para Sim quando o manipulador de eventos atualizado do item tiver adicionado o item pela primeira vez à tabela inventário corporativo. Por razões técnicas que explicaremos em uma etapa posterior, um campo deve estar visível no formulário Editar Item se quisermos gravar programaticamente em um manipulador de eventos atualizado por item.

  2. No método DeployChainStoreComponentsToHostWeb , adicione a linha a seguir, logo acima da linha RemoteTenantVersion = localTenantVersion.

      CreateExpectedShipmentsList();
    

Criar o receptor de eventos do item de lista

Observação

Se você tiver trabalhado nesta série de artigos, já configurou seu ambiente de desenvolvimento para depuração de receptores de eventos remotos. Se você não tiver feito isso, consulte Configurar a solução para depuração do receptor de eventos antes de ir mais longe neste tópico.

As Ferramentas de Desenvolvedor do Office para Visual Studio incluem um item do Receptor de Eventos Remoto que pode ser adicionado a uma solução de suplemento do SharePoint. No entanto, no momento em que este artigo foi escrito, este item de projeto pressupõe que a lista (com a qual o receptor será registrado) está na Web de suplemento e, consequentemente, as ferramentas criam um suplemento web e alguns artefatos do SharePoint nela. Mas o receptor do suplemento Chain Store será registrado (em uma etapa posterior) com a lista Remessas Esperadas na Web do host, portanto, o suplemento não precisa de um suplemento web. (Para obter um lembrete da distinção entre webs de suplemento e webs host, consulte Suplementos do SharePoint.)

Observação

Os receptores de eventos de item de lista e lista são chamados de RER (receptores de eventos remotos) porque seu código é remoto do SharePoint, na nuvem ou em um servidor local fora do farm do SharePoint. No entanto, os eventos que os disparam estão no SharePoint.

  1. Em Gerenciador de Soluções, clique com o botão direito do mouse na pasta Serviços no projeto ChainStoreWeb e selecione Adicionar>Serviço WCF.

  2. Quando solicitado, nomeie o serviço RemoteEventReceiver1 e selecione OK.

  3. As ferramentas criam um arquivo de interface, um arquivo *.svc e um arquivo de código atrás. Não precisamos do arquivo de interface IRemoteEventReceiver1.cs, portanto, exclua-o. (As ferramentas podem tê-la aberto automaticamente; se for o caso, feche-o e exclua-o.)

    Observação

    Quando você criou os receptores de evento de suplemento para os eventos instalados e desinstaladores em um artigo anterior nesta série, as Ferramentas de Desenvolvedor do Office para Visual Studio adicionaram suas URLs ao arquivo de manifesto do aplicativo. Os receptores de eventos de lista e de item de lista não são registrados no manifesto do aplicativo. Em vez disso, eles são registrados (em um suplemento hospedado pelo provedor) programaticamente. Você fará isso em uma etapa posterior.

  4. Abra o arquivo de code-behind RemoteEventReceiver1.svc.cs. Substitua todo o conteúdo pelo código a seguir.

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

    Observe o seguinte sobre este código:

    • A interface IRemoteEventService é definida no namespace Microsoft.SharePoint.Client.EventReceivers .

    • Não haverá eventos "antes" tratados no suplemento Chain Store, mas o método ProcessEvent é exigido pela IRemoteEventService interface.

  5. Adicione o código a seguir ao método ProcessOneWayEvent . Observe que o evento ItemUpdated é o único que esse exemplo tratará, portanto, poderíamos ter usado uma estrutura simples se em vez de uma opção. Mas os receptores de eventos normalmente lidam com vários eventos, portanto, queremos que você veja o padrão que você mais comumente usará em seus manipuladores de eventos como um desenvolvedor de suplementos do SharePoint.

       switch (properties.EventType)
     {
         case SPRemoteEventType.ItemUpdated:
    
     	// TODO12: Handle the item updated event.
    
     	break;
     }  
    
  6. Substitua TODO12 pelo código a seguir. Novamente, aqui, estamos usando uma estrutura de comutador quando uma estrutura simples se faria porque queremos que você veja o padrão comum nos receptores de eventos do SharePoint.

       switch (properties.ItemEventProperties.ListTitle)
     {
         case "Expected Shipments":
    
     	// TODO13: Handle the arrival of a shipment.
    
     	break;
     }
    
  7. O código que responde à chegada de um carregamento deve fazer duas coisas:

    • Adicione o item que chegou à loja no inventário corporativo.

    • Defina o campo Adicionado ao Inventário na lista Remessas Esperadas como Sim. Mas isso só deve acontecer se o item tiver sido adicionado com êxito ao inventário.

    Adicione o código a seguir no lugar de TODO13. Os dois métodos TryUpdateInventory e RecordInventoryUpdateLocally são criados em etapas posteriores.

       bool updateComplete = TryUpdateInventory(properties);
     if (updateComplete)
     {
         RecordInventoryUpdateLocally(properties);
     }
    
  8. O método ProcessOneWayEvent agora deve se parecer com o seguinte:

       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. Adicione o método a seguir à classe RemoteEventReceiver1.

       private bool TryUpdateInventory(SPRemoteEventProperties properties)
     {
         bool successFlag = false;
    
     	// TODO14: Test whether the list item is changing because the product has arrived
     	// or for some other reason. If the former, add it to the inventory and set the success flag
     	// to true.     
    
         return successFlag;
     }
    
  10. Há cinco colunas na lista Remessas Esperadas , mas não queremos que o manipulador reaja à maioria dos tipos de atualizações a um item. Por exemplo, se um usuário corrigir a ortografia do nome de um fornecedor, o evento atualizado do item será disparado, mas nosso manipulador não deverá fazer nada. O manipulador só deve agir quando o campo Arrived tiver sido definido como Sim.

    Há outra condição que precisa ser testada. Suponha que Arrived esteja definido como Sim e o produto no item seja adicionado ao inventário (e Adicionado ao Inventário está definido como Sim). Mas, posteriormente, um usuário altera erroneamente o campo Arrived de um carregamento de volta para No e, em seguida, corrige seu erro definindo-o novamente como Sim. O erro e a correção disparam o evento atualizado do item. O manipulador não reagirá ao erro porque ele só age quando Arrived é Sim, mas reagiria à correção que define Arrived de volta para Sim, de modo que o mesmo produto e quantidade seriam adicionados ao inventário uma segunda vez. Por esse motivo, o manipulador só deve agir quando o valor Adicionado ao Inventário for Não.

    Portanto, o manipulador precisa saber quais são os valores desses campos logo após o usuário atualizar o item. O objeto SPRemoteEventProperties tem uma propriedade ItemEventProperties . E, por sua vez, ele tem uma propriedade AfterProperties indexada que contém os valores dos campos no item atualizado. O código a seguir usa essas propriedades para testar se o manipulador deve reagir. Coloque isso no 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. Substitua TODO15 pelo código a seguir.

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

    Isso é principalmente SQL e ASP.NET programação, portanto, não discutimos isso em detalhes, mas observamos:

    • Usamos a propriedade ItemEventProperties.WebUrl para obter o nome do locatário, que é a URL Web do host.

    • Usamos as AfterProperties novamente para obter os valores do nome e da quantidade do produto.

    • Nos referimos ao campo nome do produto como "Title", mesmo que o nome de exibição tenha sido alterado para "Product" (no método CreateExpectedShipmentsList ) porque os campos são sempre referidos por seus nomes internos.

  12. Ainda não terminamos com o método TryUpdateInventory , mas neste ponto ele deve se parecer com o seguinte.

     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. Quando o método TryUpdateInventory retorna true, nosso manipulador chama um método (ainda não escrito) que atualiza o mesmo item na lista Remessas Esperadas definindo o campo Adicionado ao Inventário como Sim. Esse é um evento de atualização de item, portanto, o manipulador é chamado novamente. (O fato de o campo Adicionado ao Inventário ser agora Sim impede que o manipulador adicione a mesma remessa ao inventário uma segunda vez, mas o manipulador ainda é chamado.)

    O SharePoint se comporta um pouco diferente quando o evento atualizado do item é disparado por uma atualização programática: ele inclui apenas nos AfterProperties os campos alterados na atualização. Portanto, o campo Arrived não estará presente porque apenas o campo Adicionado ao Inventário foi alterado.

    A linha...

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

    ... lança um KeyNotFoundException.

    Há mais de uma maneira de resolver esse problema. Neste exemplo, vamos capturar a exceção e usar o bloco de captura para garantir que o successFlag está definido como false. Fazer isso garante que o item não seja atualizado pela terceira vez.

    Coloque tudo no método que está entre a primeira linha bool successFlag = false; e a última linha return successFlag; em um bloco de tentativas .

  14. Adicione o seguinte bloco de captura logo abaixo do bloco de tentativa .

     catch (KeyNotFoundException)
    {
       successFlag = false;
    }
    

    Observação

    O KeyNotFoundException também é a razão pela qual temos que deixar o campo Adicionado ao Inventário visível no formulário Editar Item. O SharePoint não inclui campos ocultos no formulário Editar Item em AfterProperties.

  15. O método inteiro agora deve se parecer com o seguinte.

     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. Adicione o método a seguir à classe RemoteEventReceiver1.

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

    Até agora, esse padrão de código é familiar de artigos anteriores nesta série. Mas observe uma diferença:

    • O código obtém o objeto ClientContext chamando o método TokenHelper.CreateRemoteEventReceiverClientContext em vez do método SharePointContext.CreateUserClientContextForSPHost como usamos no código que chamou o SharePoint de páginas, como a página EmployeeAdder.

    • A principal razão para ter métodos diferentes para obter um objeto ClientContext é que o SharePoint passa as informações necessárias para criar esses objetos de forma diferente para os receptores de eventos de como ele passa para páginas. Para receptores de eventos, ele passa um objeto SPRemoteEventProperties , mas para páginas ele passa um campo especial, chamado de token de contexto, no corpo da solicitação que inicia a página de suplemento.

  17. Salve e feche o arquivo de código do receptor.

Registrar o receptor

A tarefa final é informar ao SharePoint que temos um receptor personalizado que desejamos que o SharePoint chame sempre que um item na lista De remessas esperadas for atualizado.

  1. Abra o arquivo SharePointContentDeployer.cs e adicione a linha a seguir ao método DeployChainStoreComponentsToHostWeb , logo abaixo da linha que cria a lista Remessas Esperadas (adicionaremos esse método na próxima etapa). Observe que estamos passando para o método o objeto HttpRequest que a página inicial do suplemento passou para o método DeployChainStoreComponentsToHostWeb .

      RegisterExpectedShipmentsEventHandler(request);
    
  2. Adicione o método a seguir à classe SharePointComponentDeployer.

       private static void RegisterExpectedShipmentsEventHandler(HttpRequest request)
     {
         using (var clientContext = sPContext.CreateUserClientContextForSPHost())    
         {
     	var query = from list in clientContext.Web.Lists
     		    where list.Title == "Expected Shipments"
     		    select list;
     	IEnumerable<List> matchingLists = clientContext.LoadQuery(query);
     	clientContext.ExecuteQuery();
    
     	List expectedShipmentsList = matchingLists.Single();
    
     	// TODO16: Add the event receiver to the list's collection of event receivers.       
    
     	clientContext.ExecuteQuery();
         }
     }
    
  3. Substitua TODO16 pelas linhas a seguir. Observe que há uma classe CreationInformation leve para receptores de eventos, assim como há para listas e itens 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. Agora você precisa informar ao SharePoint a URL do receptor de eventos. Na produção, ele estará no mesmo domínio que as páginas remotas, com o caminho de /Services/RemoteEventReceiver1.svc. Como o manipulador está sendo registrado na lógica de primeira execução da página inicial do suplemento, o domínio está no cabeçalho do host do objeto HttpRequest para a solicitação que chamou a página. Nosso código passou esse objeto da página para o método DeployChainStoreComponentsToHostWeb , que por si só o passou para o método RegisterExpectedShipmentsEventHandler . Portanto, podemos definir a URL do receptor com o código a seguir.

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

    Infelizmente, isso não funcionará quando você estiver depurando o suplemento do Visual Studio. Quando você está depurando, o receptor é hospedado no Barramento de Serviço do Azure, não na URL localhost em que as páginas remotas estão hospedadas. Precisamos definir URLs distintas para o receptor dependendo se estamos depurando ou não, portanto, substitua TODO17 pela estrutura a seguir que usa diretivas do compilador C#. Observe que, no modo de depuração, a URL do receptor é lida de uma configuração de web.config (você criará essa configuração em uma etapa posterior).

       #if DEBUG
     		    receiver.ReceiverUrl = WebConfigurationManager.AppSettings["RERdebuggingServiceBusUrl"].ToString();
     #else
     		    receiver.ReceiverUrl = "https://" + request.Headers["Host"] + "/Services/RemoteEventReceiver1.svc"; 
     #endif
    
    
  5. Todo o método RegisterExpectedShipmentsEventHandler agora deve se parecer com o seguinte.

       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. Adicione a instrução de uso a seguir à parte superior do arquivo.

      using System.Web.Configuration;
    
  7. Para garantir que isso DEBUG seja verdadeiro se e somente se o suplemento estiver sendo depurado, execute a seguinte subprocedure:

    1. Em Gerenciador de Soluções, clique com o botão direito do mouse no projeto ChainStoreWeb e selecione Propriedades.

    2. Abra a guia Compilação das Propriedades e selecione Depurar na lista suspensa Configuração na parte superior.

    3. Verifique se a caixa de seleção Definir depuração constante está selecionada (geralmente é por padrão). A captura de tela a seguir mostra a configuração adequada.

      Figura 1. Criar sub-guia da guia Propriedades no Visual Studio

      A sub-guia Compilar da guia Propriedades no Visual Studio. A lista suspensa Configuração está definida como Depuração. A caixa de seleção para

    4. Altere a lista suspensa Configuração para Versão e, em seguida, verifique se a caixa de seleção Definir dePURAÇÃO constantenão está selecionada (geralmente não é por padrão). A captura de tela a seguir mostra a configuração adequada.

      Figura 2. Criar sub tab da guia Propriedades com a caixa de seleção desmarcada

      A subguia Compilar da guia Propriedades. O menu suspenso Configuração diz Versão de lançameto. A caixa de seleção

    5. Se você fez alterações, salve e feche a guia Propriedades .

  8. Abra o arquivo web.config e adicione a marcação a seguir como filho do elemento appSettings (obtemos o valor da configuração na próxima seção).

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

Obter a URL do receptor para depuração

Os receptores de eventos de evento de suplemento e item de lista são serviços WCF (Serviço de Comunicação do Windows) e cada serviço WCF conhece seu próprio ponto de extremidade e o armazena em vários lugares, incluindo o objeto System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri .

Quando você está depurando, o receptor de suplemento é hospedado em um ponto de extremidade Barramento de Serviço do Azure que é quase o mesmo que o ponto de extremidade do receptor de item de lista. A diferença é que a URL do ponto de extremidade do suplemento termina com "AppEventReceiver.svc", mas a URL do receptor de item de lista termina com "RemoteEventReceiver1.svc". Portanto, podemos obter a URL do ponto de extremidade no receptor de suplemento, fazer uma pequena alteração no final dele e usá-la como o valor de nossa configuração RERdebuggingServiceBusUrl web.config.

  1. Abra o arquivo AppEventReceiver.svc.cs na pasta Serviços do projeto ChainStoreWeb .

  2. Adicione o seguinte como a primeira linha no método ProcessEvent .

      string debugEndpoint = System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri.ToString(); 
    
  3. Adicione um ponto de interrupção à próxima linha do método.

  4. Selecione F5 para depurar o suplemento. Como web.config está aberto e as Ferramentas de Desenvolvedor do Office para Visual Studio alteram uma configuração nela sempre que você selecionar F5, você será solicitado a recarregá-lo. Selecione Sim.

  5. Quando o ponto de interrupção for atingido, passe o cursor sobre a debugEndpoint variável. Quando a Dica de Dados do Visual Studio for exibida, selecione a seta para baixo e selecione Visualizador de Texto.

    Figura 3. Visualizador de texto do Visual Studio com uma URL de Barramento de Serviço do Azure

    Um visualizador de texto do Visual Studio com uma URL do Barramento de Serviço do Azure.

  6. Copie o valor da cadeia de caracteres do visualizador e cole-o em algum lugar.

  7. Feche o visualizador e, em seguida, pare de depurar no Visual Studio.

  8. Exclua ou comente a linha que você adicionou na segunda etapa deste procedimento e exclua o ponto de interrupção também.

  9. Na cadeia de caracteres copiada, substitua o "AppEventReceiver.svc" no final por "RemoteEventReceiver1.svc".

  10. Copie e cole a URL modificada como o valor da chave RERdebuggingServiceBusUrl no arquivo web.config.

Observação

Copiar manualmente a URL do barramento de serviço e colar (uma versão modificada) no web.config não é a única maneira de lidar com a necessidade de uma URL diferente ao depurar um receptor de eventos remoto quando ele está em execução em produção. Poderíamos armazenar programaticamente o valor de System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri em algum lugar no SharePoint ou no banco de dados remoto e, em seguida, fazer com que nosso código de primeira execução o leia e atribua-o receiver.ReceiverUrl à propriedade. Poderíamos registrar o receptor de eventos do item de lista como parte do manipulador de eventos instalado no suplemento. Em seguida, podemos ler programaticamente System.ServiceModel.OperationContext.Current.Channel.LocalAddress.Uri, modificá-lo e atribuí-lo receiver.ReceiverUrl sem precisar armazená-lo em qualquer lugar.

Essa estratégia exige que a lista remessas esperadas também seja criada no manipulador de eventos instalado no suplemento, pois ela teria que existir antes que o manipulador pudesse ser registrado com ela.

Observe também que poderíamos combinar nosso receptor de eventos de suplemento e o receptor de eventos de item de lista em um único receptor (ou seja, os mesmos arquivos .svc e .svc.cs). Nesse caso, nenhuma modificação da URL é necessária antes de usá-la como o valor de receiver.ReceiverUrl.

Execute o suplemento e teste o receptor do item de lista

  1. Abra a página Conteúdo do Site do site da loja de Hong Kong e remova a lista Remessas Esperadas se houver uma.

  2. Use a tecla F5 para implantar e executar o suplemento. O Visual Studio hospeda o aplicativo Web remoto no IIS Express e hospeda o banco de dados SQL no SQL Express. Ele também faz uma instalação temporária do suplemento em seu site de teste do SharePoint e executa imediatamente o suplemento. Você é solicitado a conceder permissões ao suplemento antes que sua página inicial seja aberta.

  3. Quando a página inicial do suplemento for aberta, selecione o botão Voltar para Site no controle cromado na parte superior.

  4. Na página inicial da loja de Hong Kong, acesse a página Conteúdo do Site e abra a lista Remessas Esperadas .

  5. Crie um item e, no novo formulário de item, observe que os campos Chegou e Adicionado ao Inventário não aparecem.

  6. Depois que o item for criado, reabra-o para edição. Selecione a caixa de seleção Chegou e salve o item. Isso dispara o evento atualizado do item. O item é adicionado ao inventário e o valor do campo Adicionado ao Inventário muda para Sim (talvez seja necessário atualizar a página para ver a alteração para Adicionado ao Inventário).

  7. Use o botão de volta do navegador até que você esteja de volta na página inicial do suplemento Chain Store e selecione o botão Mostrar Inventário . O item marcado como Arrived agora está listado.

  8. Voltar à lista Remessas Esperadas e adicione outro item com exatamente o mesmo nome de produto e nome do fornecedor, mas uma quantidade diferente.

  9. Depois que o item for criado, reabra-o para edição. Altere o valor de Chegou para Sim e salve o item.

  10. Use o botão de volta do navegador até que você esteja de volta na página inicial do suplemento Chain Store e selecione o botão Mostrar Inventário . Ainda há apenas um item para o nome do produto e o fornecedor, mas a quantidade agora é o total dos dois itens na lista Remessas Esperadas .

  11. Para encerrar a sessão de depuração, feche a janela do navegador ou interrompa a depuração no Visual Studio. Sempre que você seleciona F5, o Visual Studio retrai a versão anterior do suplemento e instala a mais recente.

  12. Você lidará com esse suplemento e com a solução do Visual Studio em outros artigos, e recomenda-se retirar o suplemento uma última vez quando for deixar de trabalhar com ele por algum tempo. Clique com botão direito do mouse no projeto no Gerenciador de Soluções e escolha Retirar.

Próximas etapas

Para saber como publicar seu suplemento em um site do SharePoint, confira Implantar e instalar suplementos do SharePoint: métodos e opções. Você também pode buscar trabalho avançado no desenvolvimento de suplementos do SharePoint nas seguintes páginas: