在提供程序托管的加载项中处理加载项事件

这是关于开发 SharePoint 托管的 SharePoint 加载项的基础知识系列文章中的第 7 篇文章。你应该首先熟悉 SharePoint 加载项以及本系列中之前的文章(可在创建提供程序托管的 SharePoint 加载项入门中找到相关内容)。

注意

如果已完成有关提供商托管加载项的本系列文章之一,便已生成 Visual Studio 解决方案,可以在继续阅读本主题的过程中使用。 也可以从 SharePoint_Provider-hosted_Add-Ins_Tutorials 下载存储库,再打开 BeforeAdd-inEventHandlers.sln 文件。

本文将介绍如何自定义对 SharePoint 中一种类型事件(称为“加载项事件”)的处理。 具体来说,将创建加载项安装和卸载事件处理程序。 还可以自定义对列表和列表项事件的处理;本系列后面的一篇文章将对此进行详细介绍。 虽然所有这些事件都是在 SharePoint 中触发,但处理每个事件的自定义代码却位于远程 Web 应用中。 通过向 SharePoint 事件注册处理程序 URL,可以将 SharePoint 配置为调用自定义处理程序。

以编程方式将 SharePoint 组件部署到的两个位置

预期是让连锁店加载项自动创建并部署“本地员工”和“按预期到货”列表。 加载项可以随时部署 SharePoint 组件(如自定义列表)。 不过,如果加载项依赖特定组件(如自定义列表),确实应部署组件,然后用户再开始使用加载项。 对于此类关键组件,可以在下列两个位置上添加自定义部署逻辑:

  • 在加载项安装事件处理程序中。
  • 在外接程序在 SharePoint 中首次启动时执行的首次运行逻辑中。

决定对于指定外接程序最合适的部署逻辑是一个高级主题。 在本文中,我们仅进行一些比较:

  • 自定义安装处理程序必须在 30 秒内完成。 对于首次运行逻辑需要的时间没有限制。

  • 如果在加载项安装期间出现任何问题,SharePoint 会回滚它在安装过程中已完成的所有操作。 由于自定义安装处理程序在 SharePoint 完成安装加载项所需的全部操作运行,因此自定义处理程序可能会参与这种机制。

    例如,如果自定义逻辑抛出异常,可以指示 SharePoint 回滚整个加载项安装过程。 不过,如果自定义首次运行逻辑出错,加载项仍处于安装状态,但可能无法正常运行。

  • 如果不得不回滚加载项安装,SharePoint 也不会放弃安装。 它会立即再次尝试安装。 最多可尝试四次(两次尝试必须间隔 30 秒)。 每次重试时,自定义安装处理程序都会再次从头开始运行。 例如,如果处理程序在回滚前已设法安装了列表(举个例子),它会在重试时尝试再次安装同一列表。

    为了防止发生这种情况,必须将安装处理程序中的代码编写为,除非先检查相应操作是否已完成,否则不会执行任何操作(如部署组件)。 这样一来,安装处理程序的逻辑就比首次运行逻辑更加复杂,因为首次运行逻辑不会进行重试(除非专门编码为这样做)。 此外,检查组件是否已部署通常需要通过 Internet 从远程处理程序向 SharePoint 发出调用,这是个非常耗时的过程。 真正部署组件还需要执行第二次调用(如果组件尚未部署的话)。

连锁店加载项整合了这些策略。 本文将介绍如何创建安装处理程序,从而在企业数据库中将主机 Web 注册为租户,再设置信号来指明加载项是否已在主机 Web 上运行。

本系列后面的一篇文章中将介绍如何向加载项起始页的“Page_Load”方法添加首次运行逻辑。 此逻辑部署两个自定义列表,也执行其他一些操作。

将解决方案配置为支持调试事件接收器

调试事件接收器需要使用 Azure 服务总线。 请按照在 SharePoint 加载项中调试并排查远程事件接收器中的说明操作。 由于要将 SharePoint Online 网站用作测试网站,请务必按照远程测试网站的相关说明操作。 若要更好地理解本系列的其余内容,需要已成功配置调试。

创建安装处理程序

注意

只要重新打开解决方案,Visual Studio 中的“启动项目”设置往往会还原为默认值。 重新打开本系列文章中的示例解决方案后,请务必立即按照以下步骤操作:

  1. 右键单击“解决方案资源管理器”最上面的解决方案节点,再选择“设置启动项目”
  2. 确保三个项目都在“操作”列中设置为“启动”
  1. 在“解决方案资源管理器”中,选择“ChainStore”项目,以在 Visual Studio 的“属性”窗格中查看它的属性。

  2. 将“处理加载项安装”的值设置为“True”(可能仍称为“处理应用安装”)。 这会执行以下两项操作:

    • 在“ChainStoreWeb”项目(而不是“ChainStore”项目)中创建“服务”文件夹,并向文件夹中添加以下两个文件:AppEventReceiver.svc 文件和 AppEventReceiver.svc.cs 代码隐藏文件(文件名以字符串“App”开头,因为加载项过去常常称为“应用”;请勿重命名这些文件,因为 Visual Studio 的 Office 开发人员工具假定文件一直使用这些名称)。

    • 在加载项清单中注册处理程序 URL。 这一部分的清单在清单设计器中不可见。 若要查看,请右键单击 AppManifest.xml 文件,再选择“查看代码”。 此时,“属性”元素的新子元素如下所示。

         <InstalledEventEndpoint>~remoteAppUrl/Services/AppEventReceiver.svc</InstalledEventEndpoint>
      

      这个标记指示 SharePoint 在完成与安装加载项相关的所有自身工作后,调用此服务的 ProcessEvent 方法。 自定义处理程序是在安装过程最后运行的。 字符串 ~remoteAppUrl 是可供 Visual Studio 的 Office 开发人员工具替换为服务主机 URL 的占位符。 调试时,它就是 Azure 服务总线 URL。 创建要部署到生产环境的包时,它就是生产 URL。

  3. 打开 AppEventReceiver.svc.cs 文件。

  4. 可以看到,Visual Studio 的 Office 开发人员工具已创建 ProcessEvent 方法的示例实现。 此方法的所有实现都是从初始化 SPRemoteEventResult 对象开始,并都以将此对象返回到 SharePoint 结束。 此对象指示 SharePoint 是否应在自定义处理逻辑失败时回滚事件等。

    在此方法中,工具可能还添加了“using”块,用于创建 ClientContext 对象。 由于连锁店加载项中的自定义处理程序不会回调 SharePoint,因此请删除这个 using 块。 此时,方法应如下所示。

       public SPRemoteEventResult ProcessEvent(SPRemoteEventProperties properties)
     {
         SPRemoteEventResult result = new SPRemoteEventResult();
    
         return result;
     }
    
  5. 由于三个可能的加载项事件均可调用事件接收器,因此请将以下“switch”结构添加到“ProcessEvent”方法中,位置为创建和返回 result 对象的代码行之间(事件名称中包含字符串“App”,因为加载项过去常常称为“应用”)。

      switch (properties.EventType)
    {
        case SPRemoteEventType.AppInstalled:
    
            // TODO2: Custom installation logic goes here.
    
            break;
        case SPRemoteEventType.AppUpgraded:
            // This sample does not implement an add-in upgrade handler.
            break;
        case SPRemoteEventType.AppUninstalling:
    
            // TODO3: Custom uninstallation logic goes here.         
    
            break;
    }
    
  6. 安装逻辑会调用 SQL 存储过程,将香港店注册为远程 Web 应用中的租户。 如果此过程失败,处理程序会指示 SharePoint 回滚加载项安装。因此,请务必添加以下“try/catch”块来替换 TODO2

      try
    {
        CreateTenant(tenantName);
     }
    catch (Exception e)
    {
         // Tell SharePoint to cancel and roll back the event.
        result.ErrorMessage = e.Message;
        result.Status = SPRemoteEventServiceStatus.CancelWithError;
    }
    

    关于此代码,请注意以下几点:

    • 将在后续步骤中创建 tenantName 对象和 CreateTenant 方法。
    • SPRemoteEventResult 对象的 Status 属性具有三个可能的值: Continue(默认值)、 CancelNoErrorCancelWithError。 后两个值通知 SharePoint 回退事件。
  7. 主机 Web URL(即示例的租户鉴别器)是 SharePoint 通过 SPRemoteEventProperties 参数传递给接收器的一部分信息。 向 ProcessEvent 方法添加以下代码行,位置为初始化 SPRemoteEventResult 对象的代码行的正下方。

      string tenantName = properties.AppEventProperties.HostWebFullUrl.ToString();
    
  8. 现在,代码需要处理有点奇妙的 AppEventProperties.HostWebFullUrl 属性。 在其他大多数上下文中,SharePoint 在主机 Web URL 的结尾处都会添加结束字符 "/",因此示例代码逻辑假定此字符存在。 不过,当且仅当主机 Web 是网站集的根网站时,SharePoint 才会在 HostWebFullUrl 值的结尾处添加此字符。 因为香港网站是网站集的子网站,所以需要添加此字符,以确保在整个示例中使用相同的租户名称字符串。

    在初始化 tenantName 对象的代码行下面,添加以下代码。

      if (!tenantName.EndsWith("/"))
    {
        tenantName += "/";
    }
    
  9. 将以下“using”语句添加到文件最上面。

      using System.Data.SqlClient;
      using System.Data;
      using ChainStoreWeb.Utilities;
    
  10. 将下列方法添加到 AppEventReceiver 类。 本文不会对此进行详细介绍,因为本系列文章旨在介绍 SharePoint 加载项编程,而不是 SQL Server/Azure 编程。

      private void CreateTenant(string tenantName)
    {
        // Do not catch exceptions. Allow them to bubble up and trigger roll back
        // of installation.
    
        using (SqlConnection conn = SQLAzureUtilities.GetActiveSqlConnection())
        using (SqlCommand cmd = conn.CreateCommand())
        {
            conn.Open();
            cmd.CommandText = "AddTenant";
            cmd.CommandType = CommandType.StoredProcedure;
            SqlParameter name = cmd.Parameters.Add("@Name", SqlDbType.NVarChar);
            name.Value = tenantName;
            cmd.ExecuteNonQuery();
        }//dispose conn and cmd
    }
    

    此方法在“租户”数据库表中创建一行。 除“名称”列以外,此表还有默认值设置为“0000.0000.0000.0000”的“版本”列。 本系列后面的一篇文章将介绍如何创建首次运行逻辑,此逻辑就是通过这个值来确定加载项是否已在主机 Web 上安装的。 如果版本是 0000.0000.0000.0000,代码会部署“本地员工”和“按预期到货”列表,并递增版本号。

创建卸载处理程序

最佳做法通常是,只要处理安装事件,就处理卸载事件。 基本原则是,卸载处理程序应删除或回收安装处理程序部署的内容。 不过,也有很多例外情况。因此,确实需要了解加载项的用例。 例如,使用加载项部署和填充的列表可能在加载项本身已卸载后仍有值。在这种情况下,并不希望在卸载事件处理程序中卸载此列表。

与预期一样,当用户将加载项从“网站内容”页删除时,卸载事件并不会运行。 这样做只会将加载项移到网站的回收站。 虽然用户可以还原加载项,但还原操作并不会重新运行安装事件处理程序。因此,需要确保在加载项还原后,使用安装事件处理程序部署的所有内容都还在。 可以将 SharePoint 组件从回收站移到第二阶段回收站。 仅当加载项从第二阶段回收站删除时,才发生卸载事件。如果用户这样做,加载项将无法再还原。因此,需要确保届时从企业数据库中删除香港店的租赁。

  1. 将“处理加载项卸载”的值设置为“True”(可能仍称为“处理应用卸载”)。 这会在 AppManifest.xml 文件中注册处理程序,就像之前注册安装处理程序一样。 如果查看文件,便会发现它们的 URL 完全相同。 适用于 Visual Studio 的 Office 开发人员工具假定你使用相同的 *.svc 文件。 此示例就采用了这种标准做法。

  2. 在 AppEventReceiver.svc.cs 文件中,将 TODO3 替换为以下代码。

      try
    {
        DeleteTenant(tenantName);
     }
    catch (Exception e)
    {
         // Tell SharePoint to cancel and roll back the event.
        result.ErrorMessage = e.Message;
        result.Status = SPRemoteEventServiceStatus.CancelWithError;
    }
    

    关于此代码,请注意以下几点:

    • 将在下一步中添加 DeleteTenant 方法。
    • 回滚加载项卸载会将它保留在第二阶段回收站,仍可以从中还原加载项。
  3. 将下列方法添加到 AppEventReceiver 类。

      private void DeleteTenant(string tenantName)
    {
        // Do not catch exceptions. Allow them to bubble up and trigger roll back
        // of un-installation (removal from 2nd level Recycle Bin).
    
        using (SqlConnection conn = SQLAzureUtilities.GetActiveSqlConnection())
        using (SqlCommand cmd = conn.CreateCommand())
        {
            conn.Open();
            cmd.CommandText = "RemoveTenant";
            cmd.CommandType = CommandType.StoredProcedure;
            SqlParameter name = cmd.Parameters.Add("@Name", SqlDbType.NVarChar);
            name.Value = tenantName;
            cmd.ExecuteNonQuery();                
        }//dispose conn and cmd
    }
    

注意

在本系列前面的一篇文章中,已将项目配置为在每次按 F5 时重建企业数据库。 这会清空“租户”表。

运行加载项并测试安装处理程序

  1. 按 F5 键部署并运行加载项。 Visual Studio 在 IIS Express 中托管远程 Web 应用,并在 SQL Express 中托管 SQL 数据库。 此外,它还在测试 SharePoint 网站上临时安装加载项,运行安装事件处理程序,并立即运行加载项。 在加载项的起始页打开前,将会看到向加载项授予权限的提示。

  2. 在加载项的起始页打开后,依次选择最上面部件版式控制中的齿轮图标和“帐户设置”

  3. 在“帐户设置”页上,选择“显示加载项版本”按钮。 版本显示为 0000.0000.0000.0000。

    图 1. 帐户设置页

    “帐户设置”页,显示标题“帐户设置”、“显示加载项版本”按钮以及下方的一行文本“已注册版本: 0000.0000.0000.0000”。

  4. 若要结束调试会话,请关闭浏览器窗口或停止在 Visual Studio 中进行调试。 每次按 F5,Visual Studio 都会撤回旧版加载项并安装最新版本。

  5. 将在其他文章中使用此加载项和 Visual Studio 解决方案,因此最好在使用一段时间后,再最后撤回一次加载项。 在“解决方案资源管理器”中,右键单击此项目,再选择“撤回”

后续步骤

本系列的下一篇文章向提供商托管加载项添加首次运行逻辑将介绍如何向加载项添加首次运行逻辑,以编程方式部署“本地员工”列表和自定义功能区按钮。