下载此专栏的样例代码。
Microsoft Office XP 和 .NET Web 服务已经联姻,您准备好了吗?在 B2B 电子商务网络世界中,为什么不将业务处理流程同人们在计算上所做的一切结合起来,从而将 Web 服务的强大动力传递到最终用户?我在谈论什么?哦,我在谈论一个看起来有些象图 1 所示的 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 小组的编程经理。
本文地址:http://com.8s8s.com/it/it6705.htm