公司事件外接程序与 SharePoint 的集成

BusinessApps.CorporateEventsApp 示例介绍了如何将集中公司事件管理系统实施为提供程序托管的外接程序,以便与现有的业务线 (LOB) 应用程序相集成。

更具体来说,BusinessApps.CorporateEventsApp 示例介绍了如何将与 SharePoint 交互的 ASP.NET Web 应用程序实施为 LOB 实体的数据存储。 并且还介绍了如何使用单个提供程序托管的外接程序在复杂的业务任务中实施多个步骤。

此示例应用实施了一个由 SharePoint 实体(列表和内容类型)组成的集中式管理系统。 对于每个新的内容类型,都将在 ASP.NET Web 应用程序中创建相应的 LOB 实体。 Web 应用程序的组件在 SharePoint 界面内作为远程托管的外接程序部件运行,也作为完全在远程 Web 主机上运行的页面运行。 外接程序会重写 SharePoint 站点的默认欢迎页面,可以在站点主页上显示自定义品牌的界面。

使用 BusinessApps.CorporateEventsApp 示例

配置示例

启动 BusinessApps.CorporateEventApp 示例应用程序时,主页上将提供一个选项以便你配置示例。 它还为你列出了多个资源,你可从中获取更多信息。

选择“启动配置”时,将你转到配置页面,如下图所示。 当你在“配置”页面上选择“初始化数据存储”时,示例将部署支持示例的 SharePoint 实体和示例数据。

显示初始化数据屏幕的屏幕截图


初始化数据存储后,可以返回到网站查看新的欢迎页面(EventsHome.aspx 页面),其中填充了外接程序部署的两个 Web 部件,如下图所示。 在左列中,可以看到由外接程序安装的四个新列表。 企业事件列表由示例数据填充。

显示部署了 Web 部件的外接程序起始页的屏幕截图


每个 Web 部件都包含指向每个显示事件的链接,你可以从中查看事件的详细信息。 选择链接时,事件详细信息页面将在远程主机上单独运行,如下图所示。 可以选择页面上的“返回网站”以返回到 SharePoint 网站并注册该事件。

显示具有显示事件详细信息的公司事件屏幕的外接程序 UI 的屏幕截图


注册页面在远程主机上单独运行,并包含返回到 SharePoint 主机网站的链接。 完成事件注册后,你的姓名将显示在新安装的“事件注册”列表中。

显示外接程序公司事件注册屏幕的屏幕截图


Models/DataInitializer.cs 文件

Models/DataInitializer.cs 文件包含选择此按钮时运行的代码。 该文件中的代码会创建并部署四个新的 SharePoint 列表以及四种相应的内容类型:

  • 公司事件
  • 事件注册
  • 事件扬声器
  • 事件会话

此文件中的代码使用与 Core.ModifyPages 示例中类似的方法将自定义页面添加到站点。

            // Create default wiki page.
            web.AddWikiPage("Site Pages", "EventsHome.aspx");
AddWikiPage is an extension method from the Core.DevPnPCore project to add a new page to the site. This new page also becomes the new WelcomePage for the site. It also prepares to add the web parts to this page.
            var welcomePage = "SitePages/EventsHome.aspx";
            var serverRelativeUrl = UrlUtility.Combine(web.ServerRelativeUrl, welcomePage);

            File webPartPage = web.GetFileByServerRelativeUrl(serverRelativeUrl);

            if (webPartPage == null) {
                return;
            }

            web.Context.Load(webPartPage);
            web.Context.Load(webPartPage.ListItemAllFields);
            web.Context.Load(web.RootFolder);
            web.Context.ExecuteQuery();

            web.RootFolder.WelcomePage = welcomePage;
            web.RootFolder.Update();
            web.Context.ExecuteQuery();


Models\DataInitializer.cs 文件还为新欢迎页面上显示的两个 Web 部件定义了 XML,然后将其分别添加到页面。 以下示例显示这对于“特色事件”Web 部件是如何工作的。

定义 Web 部件 XML

            var webPart1 = new WebPartEntity(){
                WebPartXml = @"<webParts>
  <webPart xmlns='http://schemas.microsoft.com/WebPart/v3'>
    <metaData>
      <type name='Microsoft.SharePoint.WebPartPages.ClientWebPart, Microsoft.SharePoint, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' />
      <importErrorMessage>Cannot import this web part.</importErrorMessage>
    </metaData>
    <data>
      <properties>
        <property name='Description' type='string'>Displays featured events</property>
        <property name='FeatureId' type='System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'>3a6d7f41-2de8-4e69-b4b4-0325bd56b32c</property>
        <property name='Title' type='string'>Featured Events</property>
        <property name='ProductWebId' type='System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'>12ae648f-27db-4a97-9c63-37155d3ace1e</property>
        <property name='WebPartName' type='string'>FeaturedEvents</property>
        <property name='ProductId' type='System.Guid, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'>3a6d7f41-2de8-4e69-b4b4-0325bd56b32b</property>
        <property name='ChromeState' type='chromestate'>Normal</property>
      </properties>
    </data>
  </webPart>
</webParts>",
                WebPartIndex = 0,
                WebPartTitle = "Featured Events",
                WebPartZone = "Rich Content"
            };


将 Web 部件添加到页面

        var limitedWebPartManager = webPartPage.GetLimitedWebPartManager(Microsoft.SharePoint.Client.WebParts.PersonalizationScope.Shared);
        web.Context.Load(limitedWebPartManager.WebParts);
        web.Context.ExecuteQuery();

        for (var i = 0; i < limitedWebPartManager.WebParts.Count; i++) {
            limitedWebPartManager.WebParts[i].DeleteWebPart();
        }
        web.Context.ExecuteQuery();

        var oWebPartDefinition1 = limitedWebPartManager.ImportWebPart(webPart1.WebPartXml);
        var oWebPartDefinition2 = limitedWebPartManager.ImportWebPart(webPart2.WebPartXml);
        var wpdNew1 = limitedWebPartManager.AddWebPart(oWebPartDefinition1.WebPart, webPart1.WebPartZone, webPart1.WebPartIndex);
        var wpdNew2 = limitedWebPartManager.AddWebPart(oWebPartDefinition2.WebPart, webPart2.WebPartZone, webPart2.WebPartIndex);
        web.Context.Load(wpdNew1);
        web.Context.Load(wpdNew2);
        web.Context.ExecuteQuery();


在 Web 项目的“模型”目录中,你将注意到此 MVC ASP.NET Web 应用程序包含四个与外接程序安装的列表和内容类型相对应的类名称:

  • Event.cs(公司事件)
  • Registration.cs(事件注册)
  • Session.cs(事件会话)
  • Speaker.cs(事件扬声器)

这四个类及其相应的 SharePoint 内容类型共同构成了此外接程序中使用的四个 LOB 实体。

DataInitializer.cs 文件通过创建与“公司事件”内容类型以及添加到“公司事件”列表中的外接程序相对应的示例“事件”对象,来添加“公司事件”列表的示例数据。

注册事件时,外接程序会创建与“事件注册”内容类型以及添加到“事件注册”列表的外接程序相对应的“注册”对象。 该示例尚未完全实现会话扬声器对象,因此该外接程序目前不适用于这些对象。

下表列出了需由从 BaseListItem 抽象类继承的类实施的属性。

成员 说明
ContentTypeName 获取与项目相关联的内容类型。 如果为 null,则将其保存时,将为项目分配默认的库内容类型。
FieldInternalNames 用于在保存前检查字段数据时,可以缓存以改进性能的字段名称列表。
ListTitle 获取列表的标题(区分大小写)。

下表列出了需由从 BaseListItem 抽象类继承的类实施的方法。 这些方法返回应该设置为可直接复制到本机结构中的类型的参数,以便它们可以在多个平台上使用。

方法 说明
ReadProperties(ListItem) 使用 BaseGetBaseGetEnum 方法读取 ListItem 对象中的属性,并向其分配值子类的属性。
SetProperties(ListItem) 使用抽象类的 BaseSetBaseSetTaxonomyField 方法,设置 ListItem 对象上的属性。

下表列出了 BaseListItem 类中的帮助程序方法,其子类需要这些方法来实施 ReadPropertiesSetProperties 方法。

帮助程序方法 说明
BaseGet(ListItem item, string internalName) 获取 ListItem 中的 internalName 参数定义的属性,并返回其通用类型 T
BaseSet(ListItem item, string internalName, object value) 设置 internalName 参数定义的 ListItem 属性。
BaseSetTaxonomyField(ListItem item, string internalName, string label, Guid termId) 设置 internalNametermId 参数定义的 ListItem 分类字段。
BaseGetEnum(ListItem item, string internalName, T defaultValue) 获取 internalName 参数定义的枚举属性的值。 如果未设置属性,则返回 defaultValue 参数的值。

Event.cs 文件包含 ReadPropertiesSetProperties 方法的下列实施。

ReadProperties

        protected override void ReadProperties(ListItem item) {
            RegisteredEventId = BaseGet<string>(item, FIELD_REGISTERED_EVENT_ID);
            Description = BaseGet<string>(item, FIELD_DESCRIPTION);
            Category = BaseGet<string>(item, FIELD_CATEGORY);
            EventDate = BaseGet<DateTime?>(item, FIELD_DATE);
            Location = BaseGet<string>(item, FIELD_LOCATION);
            ContactEmail = BaseGet<string>(item, FIELD_CONTACT_EMAIL);
            Status = BaseGetEnum<EventStatus>(item, FIELD_STATUS);
            var imageUrl = BaseGet<FieldUrlValue>(item, FIELD_IMAGE_URL);

            if (imageUrl != null)
                ImageUrl = imageUrl.Url;
        }

SetProperties

        protected override void SetProperties(ListItem item) {
            BaseSet(item, FIELD_REGISTERED_EVENT_ID, RegisteredEventId);
            BaseSet(item, FIELD_DESCRIPTION, Description);
            BaseSet(item, FIELD_CATEGORY, Category);
            BaseSet(item, FIELD_DATE, EventDate);
            BaseSet(item, FIELD_LOCATION, Location);
            BaseSet(item, FIELD_CONTACT_EMAIL, ContactEmail);
            BaseSet(item, FIELD_STATUS, Status.ToEnumDescription());
            BaseSet(item, FIELD_IMAGE_URL, ImageUrl);
        }

下列代码示例显示基础的 BaseGetBaseSet 方法在 BaseListItem.cs 中如何定义。

BaseGet

protected T BaseGet<T>(ListItem item, string internalName){
            var field = _fields[internalName.ToLowerInvariant()];
            var value = item[field.InternalName];
            return (T)value;
        }


BaseSet

protected void BaseSet(ListItem item, string internalName, object value) {
            if (_fields.ContainsKey(internalName)) {
                var field = _fields[internalName.ToLowerInvariant()];

                if (field is FieldUrl &amp;&amp; value is string) {
                    var urlValue = new FieldUrlValue() {
                        Url = value.ToString()
                    };
                    value = urlValue;
                }
            }
            item[internalName] = value;
        }

BaseListItem 类还包含一个 Save 方法,用于保存外接程序创建和操作的每个 LOB 实体。 此方法加载列表并确定当前项目是否具有大于 0 的 ID。 如果 ID 不大于 0,则认为它无效并创建一个新的列表项。 它使用 SetProperties 方法在 ListItem 上设置属性,然后使用 ReadProperties方法设置子类的属性。

public void Save(Web web) {
            var context = web.Context;
            var list = web.GetListByTitle(ListTitle);
            if (!IsNew &amp;&amp; Id > 0) {
                ListItem = list.GetItemById(Id);
            }
            else {
                var listItemCreationInfo = new ListItemCreationInformation();
                ListItem = list.AddItem(listItemCreationInfo);
            }

            // Ensure that the fields have been loaded.
            EnsureFieldsRetrieved(ListItem);

            // Set the properties on the list item.
            SetProperties(ListItem);
            BaseSet(ListItem, TITLE, Title);

            // Use if you want to override the created/modified date.
            //BaseSet(ListItem, CREATED, Created);
            //BaseSet(ListItem, MODIFIED, Modified);

            ListItem.Update();

            if (!string.IsNullOrEmpty(ContentTypeName)) {
                var contentType = list.GetContentTypeByName(ContentTypeName);
                if (contentType != null)
                    BaseSet(ListItem, "ContentTypeId", contentType.Id.StringValue);
            }

            ListItem.Update();

            // Execute the batch.
            context.ExecuteQuery();

            // Reload the properties.
            ListItem.RefreshLoad();
            UpdateBaseProperties(ListItem);
            ReadProperties(ListItem);
        }

另请参阅