Microsoft Office XP 和 .NET Web 服务的应用---启用 XML 服务的 Office 文档(*****)

                           已启用 XML Web 服务的 Office 文档

下载示例代码 下载此专栏的样例代码

Microsoft Office XP 和 .NET Web 服务已经联姻,您准备好了吗?在 B2B 电子商务网络世界中,为什么不将业务处理流程同人们在计算上所做的一切结合起来,从而将 Web 服务的强大动力传递到最终用户?我在谈论什么?哦,我在谈论一个看起来有些象图 1 所示的 Excel 电子表格。

已启用 Web 服务的 Excel 电子表格

图 1:已启用 Web 服务的 Excel 电子表格

这可不是一份普通的电子表格。它使用 UDDI 来查找公司的地址,并使用目录 Web 服务来查找产品信息。当您单击“发送”按钮后,它还会对 XML 电子表格格式进行 XML 转换,生成一个 RosettaNet(英文)PIP 3 A4 订单申请格式。

键入要购买其商品的公司名称后,单击“查找”按钮,电子表格下的某个 VBA 代码将调用 UDDI 并完成地址部分其余的内容。例如,键入 Microsoft,单击“查找”,您将在“卖方”域中看到下面的内容:

“卖方”域

图 2:“卖方”域

键入数量,例如 23,并在说明域中键入 Pear,然后按 Tab,某个 VBA 代码将查询 SOAP 目录 Web 服务,以查看是否有匹配的产品,并列出详细信息。在本例中,我已将目录 Web 服务连接到 Northwind 数据库,因此它将返回以下信息:

电子表格订购部分的详细情况

图 3:电子表格订购部分的详细情况

在本例中,还列出了说明并将其转换为一个连接到详细介绍此产品的 HTML 页面的链接。

如果找到多个产品但没有一项完全满足您键入的条件,则将提供一个选项下拉列表。例如,如果键入豆腐,您将看到下列选项:

未找到完全匹配项时所提供的多个选项的示例

图 4:未找到完全匹配项时所提供的多个选项的示例

如果您选择其中一项,则将提供此项的详细信息。

完成后,单击“发送”按钮,即生成 RosettaNet PIP 3 A4 XML 订单格式,订单也同时发出。

如何实现?

通过访问“工具”菜单,选择“宏”,然后选择“Visual Basic 编辑器”,您可以浏览 VBA 代码。ThisWorkbook 下的某些代码会对电子表格中的变更做出响应,特别是当您删除说明时,Workbook_SheetChange 事件将清除一个项目行;当您将“说明”域移入“SKU”域时,Workbook_SheetSelectionChange 事件将调用 FindProduct()。如果 FindProduct 返回 XMLNode,将从该节点拉出相关的域,以填充项目行的其他详细信息。

有关 UDDI find_business 调用的工作方式,您可以查阅我的另一篇文章 UDDI:一种 XML Web 服务。如果找到一项业务,在 UDDI 响应的 /businessInfo/contacts/contact/address/ 部分找到的 addressLines 将被用来填充“卖方”地址块。

目录 Web 服务

Catalogs 模块中的 FindProduct 函数将使用包含搜索项的 URL 参数来调用目录服务 URL。它会得到一个 SOAP 响应并首先检查是否与 /Envelope/Body/Fault 匹配,如果返回的不是 Fault,它将继续打开 <CatalogQueryResult>,以检查返回项目中的 ProductName 属性是否与给定的范围相匹配。它还会在可视区域以外的页面创建下拉选项列表。您可以在“数据”菜单中选择“验证”来查看下拉列表的工作方式。

目录 Web 服务十分简单。.aspx 入口点将创建一个 CatalogSearch 对象,该对象在 search.cs 中定义,并调用 Execute 按下列方式传递 HttpResponse 输出流:

<%@Language="C#" src="search.cs"  Debug="true" %>
<%
   Response.ContentType = "text/xml";
   string term = Request.QueryString["term"];
   if (term != null) {
      CatalogSearch s = new CatalogSearch(term);
      s.Execute(output);
   } else {
      Response.Write("<Empty/>");
   }
%>

真正的乐趣将从 Execute 方法开始。它是一个捆绑在 XmlTextWriter 中的、十分简单的 SQL 托管提供程序代码,能够从 SQL SELECT 语句返回特定的域。因此,它基本上是一个通过 DataReader 向 XmlTextWriter 写入以下内容的 while 循环:

public void Execute(TextWriter stm)
{      
   XmlTextWriter xw = new XmlTextWriter(stm);
   xw.WriteStartElement("Envelope", "http://schemas..../envelope/");
   xw.WriteStartElement("Body", "http://schemas..../envelope/");
   try {
      String const = "server=localhost;uid=sa;pwd=;database=northwind";
      SQLConnection con = new SQLConnection(constr);
      con.Open();
      IDataReader reader;
      String query = "SELECT ProductName,UnitPrice,QuantityPerUnit," +
           "SupplierID,ProductID FROM Products WHERE " +
           "ProductName LIKE '%" + term + "%'";
      SQLCommand cmd = new SQLCommand(query, con);
      cmd.Execute(out reader);
      string funNamespace = "urn:schemas-b2b-fun:catalogs";
      xw.WriteStartElement("CatalogQueryResult", funNamespace);
      while (reader.Read())
      {
         xw.WriteStartElement("item");
         xw.WriteAttribute("ProductName", reader.GetString(0));
         xw.WriteAttrDecimal("UnitPrice", reader.GetDecimal(1));
         xw.WriteAttribute("UnitOfMeasure", reader.GetString(2));
         xw.WriteAttribute("SKU", "S"+reader.GetInt32(3)+
"-P"+reader.GetInt32(4));
         xw.WriteEndElement();
      }
      xw.WriteEndElement(); 
      con.Close();
   } catch (Exception e) {
      xw.WriteStartElement("Fault");
         xw.WriteElementString("faultcode","500");
         xw.WriteElementString("faultstring",e.ToString());
      xw.WriteEndElement();
   }
   xw.WriteEndElement();
   xw.WriteEndElement();
   xw.Close();
}

URL http://localhost/catalog/search.aspx?term=豆腐将返回以下结果:

<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
  <CatalogQueryResult xmlns="urn:schemas-b2b-fun:catalogs">
    <item ProductName="豆腐" UnitPrice="23.25" 
 UnitOfMeasure="每包装 40 - 100 克" SKU="S6-P14"/>
    <item ProductName="长保鲜期豆腐" UnitPrice="10" 
 UnitOfMeasure="每包装 5 千克" SKU="S4-P74"/>
  </CatalogQueryResult>
</Body>
</Envelope>

这可能是使用 .NET 框架从 SQL Server 获得 XML 的最有效的方式。粗略计算,在我的 Dell PowerEdge 2400 上每秒可获得其中的 80 到 90 个这样的 XML。

“发送”按钮

SendOrder() 函数可按电子表格中选定范围的单元格的 XML 表示形式加载 XML 文档。通过下列神奇的 VBA 代码行便可以实现这一切:

With ActiveSheet 
  Set sourcexml = New MSXML2.DOMDocument
  sourcexml.loadXML .Range("B1:N34").value(xlRangeValueXMLSpreadsheet)
End With

这将返回一个巨大的 XML 程序块,完整说明有关电子表格中选定范围的单元格的全部信息。以下是 XML 程序块的代码片断:

<Workbook>
    <Worksheet>
        <Table>
            <Row>
                <Cell ss:StyleID="s23"><Data ss:Type="Number">23</Data>
<NamedCell ss:Name="Item"/></Cell>
 <Cell ss:MergeAcross="4" ss:StyleID="m31209522" 
ss:HRef="http://eshop.msn.com/category.asp?catId=170">
<Data ss:Type="String">鲍勃叔叔的有机干梨
</Data></Cell>
<Cell ss:StyleID="s52">
<Data ss:Type="String">S3-P7</Data></Cell>
                <Cell ss:StyleID="s26">
<Data ss:Type="Number">30</Data>
<NamedCell ss:Name="UnitPrice"/></Cell>
                <Cell ss:StyleID="s27">
<Data ss:Type="String">每包装 1 磅(12 个)</Data></Cell>
<Cell ss:StyleID="s37">
<Data ss:Type="Number">690</Data></Cell>
                <Cell ss:StyleID="s49"/>
            </Row>
        </Table>
    </Worksheet>
</Workbook>

我们可以使用 XSL 将它转换为以下格式:

<PurchaseOrder xmlns="http://www.rosettanet.org">
   <deliverTo>
      <PhysicalAddress>
         <cityName>Seattle, WA, USA 98111</cityName>
         <addressLine1>Airport Chocolates</addressLine1>
         <addressLine2>2711 Alaskan Way</addressLine2>
         <regionName>USA</regionName>
      </PhysicalAddress>
   </deliverTo>
   <ProductLineItem>
      <ProductQuantity>23</ProductQuantity>
      <productUnit>
         <ProductPackageDescription>
            <ProductIdentification>
               <GlobalProductIdentifier>S3-P7</GlobalProductIdentifier>
            </ProductIdentification>
         </ProductPackageDescription>
      </productUnit>
      <Description>鲍勃叔叔的有机干梨</Description>
      <requestedPrice>
         <FinancialAmount>
            <GlobalCurrencyCode>USD</GlobalCurrencyCode>
            <MonetaryAmount>30</MonetaryAmount>
         </FinancialAmount>
      </requestedPrice>
   </ProductLineItem>
   <thisDocumentGenerationDateTime>
      <DateTimeStamp>2001-03-15T00:00:00.000</DateTimeStamp>
   </thisDocumentGenerationDateTime>
</PurchaseOrder>

注意:按照 RosettaNet PIP 3 A4 订单申请规范,这可能不是一份技术上完整的申请,但您可以由此得到启发。

提高此转换的强壮性的技巧在于命名要从中导出数据的重要单元格。这可以通过 XSLT 转换中下列样式的 XPath 表达式来实现:

 select="/Workbook/Worksheet/Table/Row/Cell[NamedCell[@ss:Name='City']]

此特定表达式将查找名称 City 单元格。而样式表的其余部分将被忽略。有关详细信息,请参阅 XLToPO.xsl。

尝试使用

要运行此函数,您只需安装 MSXML 3.0 并获取一份 Northwind 数据库。演示代码将按以下方式连接至 SQL Server:

   SQLConnection("server=localhost;uid=sa;pwd=;database=northwind");

如果 Northwind 数据库位于其他位置,您需要更改此代码位。

PO.xsl 电子表格假定目录服务位于:

http://localhost/catalog/search.aspx

您需要在本地计算机的名为 catalog 的虚拟目录中安装 Web 服务 search.aspx、search.cs 和 XLToPO.xsl,或者将电子表格指向别处。

要编辑电子表格,您必须关闭保护(可以使用“工具/保护”子菜单关闭)。

下一步

您可能希望将供应商的目录服务绑定保存到 UDDI 中。有一些 VBA 代码能帮您做到这一点。该代码将查找可识别的目录服务 serviceInfo(通过 serviceKey),如果找到,代码将使用包含在 serviceDetails 中的 accessPoint。我在本演示中所使用的伪目录 API 并没有作为 UDDI 中的已知服务类型进行注册。

使用 Office Smart Tag 来做类似的工作可能会是一种比较有趣的体验。有关详细信息,请参阅 http://msdn.microsoft.com/office/(英文)上的 Smart Tag SDK。

 


Chris Lovett 是 Microsoft XML 小组的编程经理。


回到顶部

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值