云协作

通过 Silverlight Web 部件将 SharePoint 连接至 Windows Azure

Steve Fox

对于稳定的开发人员平台这一称誉,Microsoft SharePoint 2010 是受之无愧的。通过对话框框架和 Silverlight 提供新的服务、API、数据可编程性和 UI 支持,这一改进的平台有很多优势可以吸引开发人员。

随着人们对云计算的兴趣不断提高,越来越多的用户向我询问开发人员如何将 SharePoint 应用程序和基于云的技术集成起来。上述很多功能都可以通过某种方式与作为平台的 Windows Azure 相集成。此外,通过 OData、REST、Web 2.0 社交应用程序(如 Twitter 或 Facebook)API 等其他技术,也可以实现 SharePoint 与云的集成,当然,通过采用 SOAP 或 Windows Communication Foundation (WCF) 服务的服务型体系结构也可以实现。

我们已经知道,云和 SharePoint 之间存在诸多集成可能,在本文中,我将介绍一下 SharePoint 和 Windows Azure 之间的一些具体集成点。同时,我会介绍实现集成的步骤。

平台基础知识

Windows Azure 平台由三部分组成。第一部分是 Windows Azure 本身,负责提供数据和管理功能。第二部分是 SQL Azure,负责在云中提供高可用性的事务数据。第三部分是 Windows Azure AppFabric,负责提供能够实现更高级直接服务调用方案的服务总线。

使用 Windows Azure,您可以实现多种集成方式。例如,您可以先将构建的 WCF 服务部署到云中,然后将该服务集成到 SharePoint 中。也可以从 Windows Azure 中获取数据,然后在 SharePoint 中对数据进行建模。另外,您还可以使用 Windows Azure AppFabric 服务总线来实现更复杂的服务方案,用于连接 SharePoint Online 与内部 SharePoint。

无论哪种集成,您都需要了解有哪些方法是可行的。图 1 列出了可用于实现 SharePoint 和 Windows Azure 集成的不同方式,我们将以此为起点。此表特定于 SharePoint 2010。其中某些可选方式需要比其他方式进行更多编码工作。

图 1 常见集成点

Azure 集成 方式
SharePoint 客户端 对象模型 在列表中与 Windows Azure 数据交互。
业务连接 服务 (BCS) 对 Windows Azure 的数据进行建模或向 SQL Azure 构建外部列表。
Silverlight 创建 Windows Azure 服务或数据的 UI。
沙盒解决方案/ SharePoint Online 通过 Silverlight 应用程序利用部署到网站集合的 Windows Azure。
Office 自定义客户端 直接使用 Windows Azure 的数据或使用公开数据的 BCS 列表。
标准/可视 Web 部件 利用 Windows Azure 的服务和数据。
Open XML 在文档中管理 Windows Azure 数据。
REST 使用 REST 与 SharePoint 集成的 Windows Azure 数据交互。
Office Server 服务 与 Open XML 结合使用,以便在服务器上自动生成文档(如 PDF)。
工作流/ 事件接收器 与 Windows Azure 服务、工作流或数据关联的状态或事件。
LINQ 用于查询 Windows Azure 数据对象。
搜索 联合搜索以包括 Windows Azure 数据。

无论选择何种方式,您都应该注意,本文所述的 SharePoint 在与 Windows Azure 集成后仍具备消耗性,未进行托管。换句话说,SharePoint 不是 Windows Azure 承载的服务,而是使用 Windows Azure 数据或服务的应用程序。Windows Azure 提供 SharePoint 项目(如 Web 部件或 Silverlight 应用程序)将使用的应用程序或资源。在本文中,我将通过一个具体的示例,介绍在利用 Windows Azure 中部署的自定义 WCF 服务的 SharePoint 中,如何集成 Silverlight 应用程序。

如果是初次体验,您需要确保已安装了适当的开发环境。开发环境至少需要包含以下软件:

  • Visual Studio 2010
  • Windows Azure SDK 和工具
  • SharePoint Server 2010
  • Office 2010 Professional Plus
  • SQL Server 2008 R2
  • Silverlight 运行时、SDK 和工具

对于 Windows Azure,您还需要确保已设置开发人员帐户,以便创建一个开发人员门户来管理云应用程序、数据和服务。microsoft.com/windowsazure/getstarted 提供了构建这些应用程序和服务所需的所有 Windows Azure 工具。请注意,您可以将上述软件安装在现有开发计算机上,也可以从 tinyurl.com/33bgpy6 下载一个预配置了上述软件(Windows Azure 工具除外)的虚拟机。(另外,您还可以选择安装 code.msdn.microsoft.com/vsixforsp 提供的 Silverlight Web 部件 Visual Studio 扩展。)

设置开发环境后,您就可以开始开发第一个集成项目了。在本文中,我将执行三个步骤:

  1. 创建和部署自定义的 Windows Azure WCF 服务。
  2. 创建一个可以使用自定义 Windows Azure 服务且支持 Silverlight 的 Web 部件。
  3. 在 SharePoint 网站中部署和使用支持 Silverlight 的 Web 部件。

下面,我们逐步完成这些步骤。

创建 WCF 服务

假设,您希望将某个服务部署到整个销售部门,同时希望在云中托管该服务。该服务将检索竞争对手信息,它支持两种方法:获取特定 竞争对手信息,以及返回包含所有 竞争对手信息的列表。您将创建这两种方法,不过只实现批量返回竞争对手信息。阅读本文之后,您可以扩展这些代码,对特定竞争对手信息的检索请求进行响应。

若要创建 WCF 服务,请打开 Visual Studio 2010,启动新项目。在新建项目向导中,选择“云”模板。提供一个项目名称(我的名称是 Competitors),然后单击“确定”。选择“WCF 服务 Web 角色”项目,单击“确定”。

Visual Studio 会创建一个新的解决方案,其中包含大量资源,包括 Windows Azure 角色配置文件和 WCF 服务代码和约定。在本示例中,将使用驻留内存的对象,因此,请右键单击 WCF 项目,选择“添加”,然后选择“类”。提供类名称 (Competitor),将以下代码添加到类文件中:

namespace WCFServiceWebRole1 {
  public class Competitor {
    public string svcCompeteID { get; set; }
    public string svcCompeteName { get; set; }
    public string svcCompeteFY09 { get; set; }
    public string svcCompeteFY10 { get; set; }
  }
}

代码包含四个属性(竞争对手 ID、名称及 2009 财年和 2010 财年的销售业绩)。

Silverlight 还将用作表示层,因此,需要在项目中添加一个客户端访问策略文件,以支持 Silverlight 跨域调用 Windows Azure 服务。 为此,请右键单击 WCF 项目,选择“添加”,然后再单击“新建项目”。 在“新建项目”对话框中,选择“数据”类别,然后选择“XML”。 将新文件命名为 clientaccesspolicy.xml,然后单击“添加”。 用下面的代码替换新文件中的 XML 代码:

<?xml version="1.0" encoding="utf-8"?>
<access-policy>
  <cross-domain-access>
    <policy>
      <allow-from http-request-headers="SOAPAction">
        <domain uri="*"/>
      </allow-from>
      <grant-to>
        <resource path="/" include-subpaths="true"/>
      </grant-to>
    </policy>
  </cross-domain-access>
</access-policy>

然后,为 WCF 服务创建服务约定。 在本示例中,为简单起见,只创建两个操作约定。 第一个操作用于获取单个竞争对手(getACompetitor 方法),第二个操作用于获取所有竞争对手(getAllCompetitors 方法)。 请注意,如果要获取特定竞争对手记录,需要传递 ID (custID)。 图 2 是这两个约定的摘要。

图 2 服务约定

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCFServiceWebRole1 {
  [ServiceContract]
  public interface IService1 {
    [OperationContract]
    string[] getACompetitor(string custID);

    [OperationContract]
    List<Competitor> getAllCompetitors();
    }
}

完成约定后,就可以添加一些代码来实现约定了。 与服务约定对应的服务代码如图 3 所示。 这段代码包含一个用于获取单个竞争对手记录的方法 (getACompetitor)、一个用于获取所有竞争对手信息的方法 (getAllCompetitors),以及一个用来生成竞争对手信息的方法 (generateCompeteData)。 这些代码很简单,就是利用驻留内存的数据结构(如列表集合和数组)和 LINQ 创建数据,然后将数据传递给调用应用程序。 在本示例中,调用应用程序是将要部署在 SharePoint 中的 Silverlight 应用程序。

图 3 服务代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WCFServiceWebRole1 {
  public class Service1 : IService1 {
    List<Competitor> myCompetitors = new List<Competitor>();

    public string[] getACompetitor(string custID) {
      generateCompeteData();

      string[] returnCompete = new string[4];

      var returnListOfData = (
        from compete in myCompetitors
        where  compete.svcCompeteID  == custID
        select compete).ToArray();

      foreach (var competeRecord in returnListOfData) {
        returnCompete[0] = competeRecord.svcCompeteID;
        returnCompete[1] = competeRecord.svcCompeteName;
        returnCompete[2] = competeRecord.svcCompeteFY09;
        returnCompete[3] = competeRecord.svcCompeteFY10; 

      };

      return returnCompete;
    }

    public List<Competitor> getAllCompetitors() {
      generateCompeteData();
      List<Competitor> returnlistOfCompetitors = 
        new List<Competitor>();

      var returnListOfData = (
        from customer in myCompetitors
        select customer).ToArray();

      foreach (var compete in returnListOfData) {
        Competitor tempCompeteRecord = new Competitor();
        tempCompeteRecord.svcCompeteID = compete.svcCompeteID;
        tempCompeteRecord.svcCompeteName = compete.svcCompeteName;
        tempCompeteRecord.svcCompeteFY09 = compete.svcCompeteFY09;
        tempCompeteRecord.svcCompeteFY10 = compete.svcCompeteFY10;
        returnlistOfCompetitors.Add(tempCompeteRecord);
      };

      return returnlistOfCompetitors;
    }

    private void generateCompeteData() {
      Competitor compete1 = new Competitor();
      compete1.svcCompeteID = "BR-CAN-8909";
      compete1.svcCompeteName = "Bauer - Canada";
      compete1.svcCompeteFY09 = "$45,093,028.00";
      compete1.svcCompeteFY10 = "$50,493,820.00";
      myCompetitors.Add(compete1);

      Competitor compete2 = new Competitor();
      compete2.svcCompeteID = "NK-USA-8665";
      compete2.svcCompeteName = "Nike - USA";
      compete2.svcCompeteFY09 = "$50,492,331.00";
      compete2.svcCompeteFY10 = "$52,019,828.00";
       myCompetitors.Add(compete2);

      Competitor compete3 = new Competitor();
      compete3.svcCompeteID = "GF-EU-9912";
      compete3.svcCompeteName = "Graf - Europe";
      compete3.svcCompeteFY09 = "$24,403,920.00";
      compete3.svcCompeteFY10 = "$24,001,926.00";
      myCompetitors.Add(compete3);

      Competitor compete4 = new Competitor();
      compete4.svcCompeteID = "CCM-USA-8843";
      compete4.svcCompeteName = "CCM Hockey";
      compete4.svcCompeteFY09 = "$12,209,105.00";
      compete4.svcCompeteFY10 = "$10,092,813.00";
      myCompetitors.Add(compete4);

    }
  }
}

此时,就创建好了 WCF 服务,差不多可以开始 SharePoint 部分的集成工作了。不过,在此之前,您需要将该服务部署到 Windows Azure。

若要部署该服务,您必须设置 Windows Azure 开发人员帐户。完成后,只需右键单击 Windows Azure 项目,然后选择“发布”。

发布服务时,会出现对话框,供您提供凭据。如图 4 所示,您可以选择只创建服务包(Visual Studio 在本地文件夹中创建两个需要添加到 Windows Azure 开发人员门户的核心文件),或通过预配置信息自动部署服务。在本示例中,单击“仅创建服务包”,然后单击“确定”。

图 4 服务发布选项

此操作会创建两个文件,即 Competitors 和 ServiceConfiguration。Competitors 是服务包文件(实质上是资源存档),ServiceConfiguration 是 XML 配置文件。

现在,可以导航到 Windows Azure 开发人员门户,将这些文件添加到服务。为此,请导航到服务,单击“部署”(如果已经部署过服务,要对服务进行升级,则单击图 5 所示的“升级”)。然后,可以浏览到这两个文件,单击“确定”。上传服务文件可能需要几分钟时间。

图 5 将服务手动部署到 Windows Azure

出现“就绪”消息后,可以单击该网页上显示的链接来测试服务端点。请注意,您可能需要在服务 URL 的末尾添加服务名称,如:

http://serviceendpoint.azure.com/Service1.svc.

此时,就可以抛开 Windows Azure 去处理 SharePoint 了。

创建支持 Silverlight 的 Web 部件

您可以通过两种方式为 SharePoint 创建 Silverlight Web 部件。一种方式是,在 Visual Studio 中只创建 Silverlight 应用程序,然后将 XAP 文件部署到 SharePoint(例如将其上传到文档库),最后使用 SharePoint 2010 的本机 Silverlight Web 部件加载 Silverlight 应用程序。这是在 SharePoint 上部署 Silverlight 应用程序最快的方式,涉及的编码也较少。

另一种方法稍微有趣一些,即,使用 Silverlight 和 SharePoint Web 部件项目模板 (code.msdn.microsoft.com/vsixforsp)。这种方法会自动将 Silverlight 应用程序打包到 Web 部件中,这就意味着,您只需创建 Silverlight 应用程序,然后将它以 Web 部件的形式部署到 SharePoint。这种方式使您能够更好地控制代码,此外,还可将真正的 Web 部件部署到 SharePoint。

若要使用模板,请导航到 Codeplex 网站,单击 Silverlight 和 SharePoint VSIX 链接,然后下载并解压缩文件。解压缩文件后,只需通过双击安装 .vsix 文件,然后重新启动 Visual Studio 2010。

安装 Silverlight Web 部件模板后,返回 Visual Studio 解决方案,依次单击“文件”、“添加”、“新建项目”,然后在“解决方案”字段中选择“添加到解决方案”。导航到 SharePoint 2010 文件夹,然后选择 Silverlight Web 部件项目模板。提供 Web 部件名称(我使用的是 CompetitorSPWebPart),然后单击“确定”。

单击“确定”后,系统会提示您输入 SharePoint 网站 URL。在此处添加网站 URL,然后选择“部署为场解决方案”。此时,系统会提示输入多个项,包括 Silverlight 项目的名称、版本、XAP 文件的部署位置,以及 Web 部件的标题和说明(请参见图 6)。完成这部分向导后,单击“完成”。(请注意,您可以使用 Silverlight 3 或 4 来处理项目模板,Microsoft 目前正在升级模板,准备在 Codeplex 上重新发布。)

图 6 配置 Silverlight Web 部件

现在,有了含 Silverlight 应用程序的 SharePoint Web 部件,可以使用 Silverlight 应用程序来构建 Web 部件的 UI 和功能。

首先,在 Silverlight 项目中,右键单击“引用”,选择“添加服务引用”,添加对 Windows Azure 服务的引用。为服务提供命名空间名称(我使用的是 GetCompeteAzureService),然后单击“确定”。这和在应用程序中使用任何其他服务引用的方法是一样的,只是在本例中,端点指向的是 Windows Azure 服务。

此时,可以针对 Windows Azure 服务编写代码。如前所述,您需要在 SharePoint 应用程序中使用 getAllCompetitors 方法。

应用程序需要一个 UI。我创建的是一个简单的 UI,可以呈现 Windows Azure 服务调用命令返回的数据。其核心控件是一个列表框控件,其中包含一些图像以显生动。有关详细信息,请参阅本文的代码下载部分。

接下来,将自定义类 Competitor 添加到 Silverlight 应用程序。Competitor 有四个属性,它们对应于图 3 所示服务代码定义的竞争对手数据:

namespace CompetitorDisplayApp {
  public class Competitor {
    public string competeID { get; set; }
    public string competeName { get; set; }
    public string competeFY09 { get; set; }
    public string competeFY10 { get; set; }
  }
}

现在,可以在 XAML 隐藏代码中添加一些代码来实现 getAllCustomers 方法。 在图 7 中,可以看到,我是用列表集合 myCompetitors 来存储 Windows Azure 服务调用命令返回的数据的。 这里没有什么繁重的任务;代码使用 Competitor 对象填充要与列表框 (competeList) 绑定的 myCompetitors 列表集合。

图 7 自定义 Competitor 对象

using CompetitorDisplayApp.GetCompeteAzureService;

namespace CompetitorDisplayApp {
  public partial class MainPage : UserControl {
    public string SiteUrl { get; set; }

    List<Competitor> myCompetitors = new List<Competitor>();

    public MainPage() {
      InitializeComponent();

      this.Loaded += new RoutedEventHandler(MainPage_Loaded);
    }

    void MainPage_Loaded(object sender, RoutedEventArgs e) {
      LoadAzureData();
    }

    private void LoadAzureData() {
      GetCompeteAzureService.Service1Client azureSvcProxy = 
        new Service1Client();
      azureSvcProxy.getAllCompetitorsAsync();
      azureSvcProxy.getAllCompetitorsCompleted += 
        new EventHandler<getAllCompetitorsCompletedEventArgs>(
        azureSvcProxy_getAllCompetitorsCompleted);
    }

    void azureSvcProxy_getAllCompetitorsCompleted(
      object sender, getAllCompetitorsCompletedEventArgs e) {

      var competeData = e.Result;
      foreach (var item in competeData) {
        Competitor tempRecord = new Competitor();
        tempRecord.competeID = item.svcCompeteID;
        tempRecord.competeName = item.svcCompeteName;
        tempRecord.competeFY09 = item.svcCompeteFY09;
        tempRecord.competeFY10 = item.svcCompeteFY10;
        myCompetitors.Add(tempRecord);
      }

      competeList.ItemsSource = myCompetitors;
    }
  }
}

此时,编码任务就完成了。 不过,大致看一下通过 Silverlight Web 部件模板创建的默认代码,了解为什么这样的方式比只使用 SharePoint 提供的默认 Silverlight Web 部件更有效,会对我们有所帮助。

图 8 是在 Visual Studio 创建 Silverlight Web 部件项目时创建的默认 Web 部件代码。 可以看到,该包装代码创建了 SilverlightPluginGenerator 对象,并且为 Web 部件设置了属性。 这些属性是在设计时管理的(这与通过 SharePoint 的“工具”窗格打开 Web 部件、编辑高度和宽度的方式不同)。 另外,如果该 Web 部件部署到 Web 部件库中,您不必执行复制和粘贴操作,这是因为,已经有了呈现 Silverlight 应用程序的功能。

图 8 默认 Web 部件代码

[ToolboxItemAttribute(false)]
public class SilverlightWebPart : WebPart {
  private SilverlightPluginGenerator _silverlightPluginGenerator = null;

  public SilverlightWebPart() {
    this._silverlightPluginGenerator = 
      new SilverlightPluginGenerator {

      Source = new Uri(
        "/XAPS/Silverlight/CompetitorDisplayApp/CompetitorDisplayApp.xap",
        UriKind.Relative),
        Width = new Unit(400, UnitType.Pixel),
        Height = new Unit(300, UnitType.Pixel),
        BackGround = Color.White,
        Version = SilverlightVersion.v3,
        AutoUpgrade = true,
        OnError = "onSilverlightError",
    };
  }

  protected override void CreateChildControls() {
    base.CreateChildControls();

    this.Controls.Add(new LiteralControl(
      @"<script type=""text/javascript"">" + 
      Resources.onSilverlightErrorHandler + 
      "</script>"));

    this._silverlightPluginGenerator.InitParams.Add(new InitParam(
      "SiteUrl", SPContext.Current.Site.Url));

    this.Controls.Add(new LiteralControl(
      this._silverlightPluginGenerator.ToString()));
  }

  protected override void RenderContents(HtmlTextWriter writer) {
    base.RenderContents(writer);
  }
}

最后,在执行初始配置向导时,已设置了 Web 部件的属性。 例如,如果打开 .webpart 文件,可以看到 Web 部件的名称和说明(如果需要,可以在此进行修改):

<?xml version="1.0" encoding="utf-8"?>
<webParts>
  <webPart xmlns="https://schemas.microsoft.com/WebPart/v3">
    <metadata>
      <type name="CompetitorSPWebPart.SilverlightWebPart.SilverlightWebPart, $SharePoint.Project.AssemblyFullName$" />
      <importErrorMessage>$Resources:core,ImportErrorMessage;</importErrorMessage>
    </metadata>
    <data>
      <properties>
        <property name="Title" type="string">Compete Information</property>
        <property name="Description" 
          type="string">This Web Part provides compete information.</property>
      </properties>
    </data>
  </webPart>
</webParts>

完成 Silverlight Web 部件后,就可以将 Web 部件部署到 SharePoint 上使用了。

部署 Web 部件

若要部署,请右键单击 Web 部件项目(我的示例中是 CompetitorSPWebPart),然后选择“部署”。部署 Web 部件时,Silverlight 应用程序会部署到 XAPS 文档库中,Web 部件中会自动生成指向该 XAP 文件的链接。(如果您选择使用 Silverlight 应用程序替代 Silverlight Web 部件模板,只需将 XAP 文件上载到 XAPS 文档库,然后使用 SharePoint 中的本机 Silverlight Web 部件。)

现在,打开 SharePoint 网站,导航到(或创建)新的 Web 部件页。(请注意,通过依次单击“网站操作”、“查看所有网站内容”、“创建”和“Web 部件页”,可以创建新的 Web 部件页。)依次单击“网站操作”、“编辑页”和“添加 Web 部件”。导航到“自定义”类别,选择 Web 部件(本示例中为 Compete Information),然后单击“添加”。添加后,单击“停止编辑”。结果类似于图 9 的内容。

图 9 最终用于调用 Windows Azure 服务的 Silverlight Web 部件

总结

SharePoint 和 Windows Azure 的集成是一个新事物,方式有很多种。在本例中,我介绍了如何创建自定义 Windows Azure 服务,如何通过自定义的基于 Silverlight 的 Web 部件利用该服务。通过这个简单示例可以看到,通过 Windows Azure 服务和使用该服务的 Web 部件,可以实现更复杂的解决方案。

有关更多示例和演练,请访问我的博客 blogs.msdn.com/b/steve_fox。获取更多有关如何集成 SharePoint 和 Windows Azure 的代码和文档。

Steve Fox  是 Microsoft 的一名高级推广经理。他在 IT 行业已工作了 15 年,其中 10 年在 Microsoft 从事自然语言、搜索以及 SharePoint 和 Office 开发工作。Fox 撰写了很多文章和书籍,包括最近发布的《Beginning SharePoint 2010 Development》(Wrox,2010)。

衷心感谢以下技术专家对本文的审阅:Paul Stubbs