使用 IBM DB2 pureXML 和 ASP.NET 开发一个商店定位器应用程序

创建一个 Web 应用程序来浏览和维护商店信息并在地图上标示商店坐标

我们生活在一个互联且开放的世界中,这里数据自由流动,可以在 Web 上找到大量有用的信息。过去,如果想知道自己最喜欢的零售商离您家最近的一家商店在哪里,您可能会在电话黄页中寻找相关信息,找到该公司的电话号码,打电话询问最近一家分店的方位。尽管这不失为一种可行的解决方案,但是容易让消费者迷路,浪费时间,留下令人沮丧的经历。然而在今天,这种情况发生了根本改变。现在,您只需打开 Web 浏览器访问该公司的网站,通常就可以找到一个 “商店定位器” 特性,可帮助您找到距离最近的商店,并在地图上标示商店位置,以便更轻松地找到它。在本教程中,将学习如何使用 C# ASP.NET 和 IBM DB2® 数据库来开发这样一个特性。

Joe Lennon, 软件开发人员, Core International

Joe Lennon 是一名来自爱尔兰科克市的软件开发人员,他今年 23 岁。Joe 目前是 Core International 的 Web 应用程序和 Oracle PL/SQL 开发人员,他 2007 年毕业于 University College Cork 并获得业务信息系统学位。他和女朋友 Jill 住在科克市。



2010 年 1 月 14 日

开始之前

本文针对以下开发人员:他们想了解如何在数据库中存储 XML 格式的数据,如何从 .NET 应用程序连接到 DB2,以及如何开发利用映射 API 的应用程序。要跟随本教程,应该熟悉 .NET 框架和 C# 语言。另外,还应该拥有使用 HTML 和/或 XML 进行开发,以及使用 SQL 和数据库管理系统通信的经验。完成本教程后,您将能创建由使用 pureXML® 原生地存储 XML 的 DB2 数据库驱动的、支持映射的 .NET 应用程序。

关于本教程

IBM DB2 数据库管理系统的较新版本都包含一个称为 pureXML 的特性,该特性支持将 XML 数据原生地存储在关系数据库中。pureXML 概念很独特,它不仅支持 XML 数据的原生存储,还支持在数据库中直接对 XML 数据进行原生处理,而不是将 XML 数据转换为关系数据或将其存储为平面文本。pureXML 的美妙之处在于,它支持同时存储传统的关系数据(存储在表和列中)和面向文档的 XML 数据(通常自包含在单个 XML 文档中)。IBM DB2 提供一系列方法用于操作这种混合数据:SQL、SQL/XML 和 XQuery。

常用缩写词

  • API:应用程序编程接口
  • HTML:超文本标记语言
  • KML:Keyhole 标记语言
  • RSS:真正简单聚合
  • SQL:结构化查询语言
  • UI:用户界面
  • URL:统一资源定位符
  • XML:可扩展标记语言

开发 Web 应用程序时,XML 通常是一种不错的数据存储模型,原因是它很灵活,允许非常轻松地进行模式更改,而不会对现有数据造成不利影响。另外,由于 XML 是一种开放标准,您可以轻松地在几乎任意平台和开发框架上使用 XML 数据。而且,许多 Web 服务和 API 都使用 XML 格式提供数据,当 XML 存储应用于 Web 时,您就会明白为何 XML 存储如此重要了。

在本教程中,我们将使用 C# ASP.NET 开发一个 Store Locator 应用程序,它将数据存储在 IBM DB2 数据库中。底层数据存储在数据库表中,表中有一个关系 ID 列和一个 XML 列,XML 列用于存储实际的商店数据。在了解如何将一些现有 XML 文档批量导入数据库之前,我们首先创建此数据的关系视图,以便轻松使用 Visual Studio 的数据组件直接将 UI 元素连接到 DB2 数据。

先决条件

要跟随本教程的步骤,需要安装以下软件:

  • IBM DB2 Express-C 9.5 或更高版本
  • Microsoft® Visual Studio® 2008(早期版本也许有效,但可能不包含某些特性)
  • IBM DB2 Database Add-ins for Visual Studio

参考资料 部分提供了下载上述软件的链接。开发 .NET 应用程序时,您将使用 DB2 .NET 提供程序从您的代码与 DB2 服务器通信。但是这个提供程序必须先启用,然后才能在 Visual Studio 中使用。安装 DB2 和 Visual Studio 后,运行 Start>Programs>IBM DB2>[DB2 Instance Name]>Set-up Tools 中的 Configure DB2 .NET Data Provider 工具。


建立数据库

在本小节中,首先是为 Store Locator 创建一个新的 DB2 数据库。然后是创建一个表和两个视图,前者用于存储每个商店位置,后者用于提供 XML 数据的关系表示。最后,使用一个 .del 文件和 DB2 IMPORT 命令将一系列 XML 文档导入数据库中。

创建数据库

第一步是创建 DB2 数据本身。为此,从 Start>Programs> IBM DB2 >[DB2 Instance Name]>Command Line Tools>Command Editor 打开 DB2 Command Editor,输入以下命令:create database storeloc using codeset UTF-8 territory US

大约一分钟之后,DB2 将以类似于下面的消息进行响应:DB20000I The CREATE DATABASE command completed successfully

提示:可以通过同时按下 Ctrl+Enter 键快速执行 Command Editor 中的命令。

接下来,需要告知 Command Editor 连接到新创建的数据库,方法是发出这条命令:connect to storeloc

DB2 应该通过一些数据库连接信息和以下消息作出响应:A JDBC connection to the target has succeeded

接下来是创建一个表来存储商店地址数据。

创建 store 表

使用 清单 1 中的 CREATE TABLE 语句创建 store 表。

清单 1. 创建 store 表
create table store (
    store_id int not null generated by default as identity,
    info xml not null,
    primary key(store_id)
);

可以看到,store 表包含两列:store_id(主键)和 info。如您所料,store_id 是每行的唯一标识符。这个列使用了 generated by default as identity 子句,该子句将在每次插入一行时自动递增数值 1,这和 MySQL 数据库中的 auto_increment 特性非常相似。info 列为 XML 类型,这意味着它的内容将是原生 XML 数据。执行上述语句将导致以下响应:DB20000I The SQL command completed successfully

在继续下一小节之前,我们先来看一个即将存储到这个 store 表的 info 列中的 XML 文档样例(见 清单 2)。

清单 2. XML 文档样例
<store>
    <title>Boston, MA (Newbury)</title>
    <contact>
        <address>
            <street>Newbury Street</street>
            <city>Boston</city>
            <state>MA</state>
            <zip>02116</zip>
        </address>
        <phone>(617) 555-9476</phone>
        <fax>(617) 555-9477</fax>
        <email>newbury@example.com</email>
    </contact>
    <manager>Thomas Ryan</manager>
    <hours>
        <day name="Monday">09:00 to 18:00</day>
        <day name="Tuesday">09:00 to 18:00</day>
        <day name="Wednesday">09:00 to 18:00</day>
        <day name="Thursday">09:00 to 21:00</day>
        <day name="Friday">09:00 to 21:00</day>
        <day name="Saturday">10:00 to 15:00</day>
        <day name="Sunday">Closed</day>
    </hours>
    <location>
        <latitude>42.3526062695638</latitude>
        <longitude>-71.0710962764608</longitude>
        <zoom>16</zoom>
    </location>
</store>

数据库中的每个商店位置都将有一个类似于 清单 2 中的文档存储在 store 表的 info 列中。可以看到,XML 父元素是 <store>,它包含一系列子节点:<title>、<contact>、<manager>、<hours> 和 <location>,其中部分子节点下面嵌套了一些元素。接下来,将创建两个视图,用于将这个 XML 数据映射到关系列,从而简化从 .NET 组件到 DB2 数据的映射。

创建 store_view 和 store_hours_view 视图

第一个要创建的视图是 store_view,它把 XML 文档中的所有数据(营业时间信息除外)映射到一些可以使用标准 SQL 语句处理的关系列。清单 3 展示了创建这个视图的代码。

清单 3. 创建 store_view DB2 视图
create view store_view(
    store_id, title, address_street, address_city, address_state, 
    address_zip, phone, fax, email, manager, latitude, longitude, 
    zoom
) as
    select s.store_id, x.*
    from store s, xmltable('$d/store' passing s.info as "d"
        columns
        title varchar(200) path 'title',
        address_street varchar(200) path 'contact/address/street',
        address_city varchar(100) path 'contact/address/city',
        address_state varchar(2) path 'contact/address/state',
        address_zip varchar(15) path 'contact/address/zip',
        phone varchar(30) path 'contact/phone',
        fax varchar(30) path 'contact/fax',
        email varchar(255) path 'contact/email',
        manager varchar(200) path 'manager',
        latitude varchar(50) path 'location/latitude',
        longitude varchar(50) path 'location/longitude',
        zoom varchar(4) path 'location/zoom'
    ) as x

清单 3 中的代码创建了一个名为 store_view 的视图,该视图有几列,所有的列(除了 store_id 列)都映射到 info 列中包含的 XML 文档中的一个元素。这要归功于 XMLTABLE 函数,它支持使用 XPath 表达式将 XML 元素和属性映射到关系列。执行这段代码将返回一条熟悉的消息:DB20000I The SQL command completed successfully

接下来,创建第二个视图,即 store_hours_view。在前一个视图中,您可能注意到,XML 元素 hours 没有映射到关系列。要映射它们,需要为 <hours> 节点的每个子元素都创建一个单独的列,这不仅意味着从这些列创建一个视图非常繁琐,还意味着使用 SQL 从视图检索数据需要做更多工作。相反,更简单的方法是创建一个单独的视图,其中每一天都存储在单独一行。清单 4 展示了创建这个视图的代码。

清单 4. 创建 store_hours_view DB2 视图
create view store_hours_view(
    store_id, day, hours
) as
    select s.store_id, x.*
    from store s, xmltable('$d/store/hours/day' passing s.info as "d"
        columns
        day varchar(20) path '@name',
        hours varchar(50) path '.'
    ) as x

这一次,您创建的视图将为每个商店的一周的每一天包含单个行。每一行将包含 store_id,名称(位于每个 <day> 元素的 name 属性中)和小时。同样,运行这段代码将收到一个成功响应。在下一小节中,将了解如何把数据从一系列 XML 文件加载到数据库中,然后测试本小节中创建的视图是否返回正确的数据。

从一系列 XML 文件导入数据

要将 XML 数据导入数据库,需要在本教程的 源代码 中找到 xml 文件夹,将其复制到方便使用的位置。我将该文件夹复制到我的 C: 盘并将其重命名为 storeloc。这个文件夹包含一系列 XML 文件和一个名为 location.del 的文件。如果用您喜欢的文本编辑器打开 location.del,您将注意到该文件包含几行,每行都进行了编号并引用文件夹中的其他 XML 文件。现在,使用这个文件将对应 XML 文件的内容导入 DB2 数据库。

在 DB2 Command Editor 中,输入 清单 5 中的命令。

清单 5. 批量导入 XML 文档
import from "C:\storeloc\locations.del" of del
    xml from "C:\storeloc"
insert into store;

应该得到类似于下面的响应:SQL3149N "7" rows were processed from the input file. "7" rows were successfully inserted into the table. "0" rows were rejected

我们来验证一下数据是否按预期的方式插入。首先,发出以下命令检索 store 表中的内容,该表中的 XML 数据保持其原生的 XML 格式:select * from store

结果应该类似于 图 1 中的屏幕截图。

图 1. store 表的 Query Results
store 表的 Query Results 选项卡的屏幕截图(显示 STORE_ID 列和 INFO 列)

接下来,在这个窗口中单击其中一行的 More (...) 按钮。这将打开 XML Document Viewer,显示 info 列中的 XML 文档的树状结构,如 图 2 所示。

图 2. XML Document Viewer
XML Document Viewer 中 Tree View 选项卡的屏幕截图(样例信息来自清单 2)

继续之前,最好验证一下此前创建的两个视图显示的数据是否正确。首先,发出以下命令测试 store_view 主视图:select * from store_view

这个语句应该在 Query Results 选项卡中显示 7 行,每一列都填充了数据(包括电话号码、电子邮件地址、经理姓名和纬度)。图 3 展示了这些列的样例。

图 3. store_view Query Results
store_view Query Results 的屏幕截图

最后,执行以下查询:select * from store_hours_view

同样,每一列都填充有数据,但是这次应该得到 49 行 — 每个商店 7 行(7 个商店 * 每周 7 天 = 49 行)。数据库现已就绪,下一小节中将开始开发一个由这个数据库驱动的 ASP .NET 应用程序。


创建 ASP.NET 应用程序

在本小节中,将学习如何创建一个 ASP.NET Web 应用程序,该应用程序连接到一个 DB2 数据库,允许用户查看不同的商店位置,并允许管理员管理这些位置。首先,在 Microsoft Visual Studio 中打开一个新的 ASP.NET Web Site 项目。接下来,添加一个对 IBM DB2 Data Adapter 的引用,以便在您的代码中使用 DB2 数据库。最后,创建一个 Master Page,它将构成您创建的每一个带有头部和导航条的页面。

建立 ASP.NET 项目

首先打开 Microsoft Visual Studio 200。要创建一个新的 Web Site,从应用程序菜单选择 File>New Web Site。这将打开 New Web Site 对话框,如 图 4 中的屏幕截图所示。

图 4. New Web Site 对话框
New Web Site 对话框屏幕截图,带有默认和自定义模板,包含位置和语言字段

保留选中的默认选项(ASP.NET Web Site),确保从 Location 下拉列表中选择 File System,从语言列表中选择 Visual C#。最后,将位置文本框末尾的 WebSite1 更改为 Stores。单击 OK 打开项目。

添加 DB2 Data Adapter 引用

要在 C# 中与 DB2 通信,需要将一个对 DB2 Data Adapter 的引用添加到 ASP.NET 项目,操作方法有两种:

  • 手动将引用添加到项目的 web.config 文件的 <assemblies> 部分。
  • 从 Visual Studio 应用程序菜单导航到 Website>Add Reference,这将打开 Add Reference 对话框,如 图 5 所示。
图 5. Add Reference 对话框
Add Reference 对话框的屏幕截图,在 .NET 选项卡上显示 Component Name、Version、Runtime 和 Path 列

向下滚动,找到 IBM.Data.DB2 组件,单击以选择它。现在单击 OK 将引用添加到项目。在接下来创建应用程序的 Master Page 之前,花点时间验证 DB2 Data Adapter 是否已添加到项目。在 Solution Explorer(见 图 6)中,双击打开 web.config 文件。

图 6. Solution Explorer
Solution Explorer 的屏幕截图

这个 XML 文件包含大量对 ASP.NET 项目很重要的属性。找到 <compilation> 元素,该元素应该有一个子节点 <assemblies>。该元素下的一个 <add> 子节点应该看起来如下所示:<add assembly="IBM.Data.DB2, Version=9.0.0.2, Culture=neutral, PublicKeyToken=7C307B91AA13D208" />

如果这一行在 web.config 文件中,那么 DB2 Data Adapter 引用部件已经添加到 ASP.NET 项目。在下一小节中,将为项目创建 Master Page,它将容纳这个 Store Locator 应用程序的头部和导航条。

创建应用程序 Master Page

Master Page 是 ASP.NET 应用程序的一个特性,它允许您为应用程序中的一个或多个页面定义一个布局。Master Page 通常包含头部、导航条和脚注,它们在每个继承 Master Page 的内容页面中保持一致。使用 Master Page 可以避免为布局 Web 应用程序编写重复代码,同时有利于为页面定义标准的外观。

在 Visual Studio 的 Solution Explorer 中,右键单击您的项目,从上下文菜单中选择 Add New Item。这将打开 Add New Item 对话框,如 图 7 所示。

图 7. Add New Item 对话框
Add New Item 对话框屏幕截图,显示默认模板列表,其中选中了 Master Page

在这个对话框中,从 Templates 框选择 Master Page,确保选择语言 Visual C#,并勾选 Place code in a separate file。您可以保留默认名称 MasterPage.master。一切就绪后,单击 Add 创建 Master Page。MasterPage.master 将在代码编辑器窗口中打开。

首先需要将页面标题(位于起始和结束 <title> 标记之间)修改为 Store Locator。接下来,需要添加一些属性到 <body> 标记,这将删除页边空白,有利于本教程稍后加载 Bing Maps。将 <body> 标记的文本修改为如下所示:<body id="pageBody" runat="server" style="margin: 0px; padding: 0px">

接下来,找到起始 <div> 标记(在 <form> 标记下一行)并添加一个样式属性:<div style="margin: 0px; padding: 0px; font-family: Arial">

在这行下面(<asp:ContentPlaceHolder> 标记上面),添加以下 <div> 标记,为应用程序创建主标题(见 清单 6)。

清单 6. Store Locator 标题
<div style="background-color: #999966; font-weight: bold; 
  font-size: xx-large; padding: 5px, 15px; 
  border-bottom: 2px solid #000">
    <a href="ListStores.aspx" style="color: #fff; 
      text-decoration: none;">
        Store Locator
    </a>
</div>

接下来,创建导航条。在您添加的 清单 6 中的代码的正下方,添加 清单 7 中的代码,创建一个包含两个部分的导航条:一个部分用于稍后创建一个 Search by State 特性,另一个用于容纳到管理页面的链接。

清单 7. 导航条
<div style="background-color: #333300; color: #fff; 
  padding: 5px 15px; border-bottom: 1px solid #000">
    <div style="width: 47%; float: left">
        Search by State:
    </div>
    <div style="width: 50%; float: left; padding-top: 5px; 
      text-align: right;">
        <a href="AddNewStore.aspx" style="color: #fff">
            Add New Store
        </a>&nbsp;|
        <a href="ManageStores.aspx" style="color: #fff">
            Manage Stores
        </a>
    </div>
    <div style="clear: both; font-size: 1px;">&nbsp;</div>
</div>

接下来,切换到 Design 视图。此时,您的 Master Page 应该如 图 8 所示。

图 8. 未完成的 Master Page
未完成的 Master Page 的屏幕截图,包含 Search by State 字段和到 Add New Store 和 Manage Stores 的链接

从 Visual Studio Toolbox 中,拖动一个 DropDownList 控件到您的表单并将其放置到您的 Master Page 中的 Search by State 文本右侧。在 Properties 窗口中,找到 ID 属性,将其从 DropDownList1 更改为 ddlStates。接下来,拖动一个 Button 组件到您的表单上,这次将其放置到您刚才添加的 DropDownList 的右侧。将这个按钮的 ID 属性更改为 btnSearch,并将 Text 属性更改为 Go!

现在,Master Page 页面上的 DropDownList 组件还没有绑定,意味着它还没有数据源,因此,也不会显示任何项目。我们通过将其绑定到一个数据源来更正这个问题。右键单击 DropDownList 并选择 Show Smart Tag。在这个列表中选择 Choose Data Source,这将打开 Data Source Configuration Wizard。在这个对话框中,从 Select a data source 选项中选择 <New data source>,您将被询问应用程序获取数据的位置。选择 Database 并单击 OK

在下一个屏幕中,单击 New Connection 按钮打开 bAdd Connectionb 对话框。在 Data Source 旁边,单击 Change 并从弹出窗口中选择 IBM DB2, IDS and U2 Servers。回到 Add Connection 页面,输入您的 IBM DB2 连接凭证并选择 STORELOC 数据库。单击 Test Connection 以确保应用程序能够连接到 DB2,然后单击 OK 添加连接。现在应该已返回到 Choose Your Data Connection 窗口,在下拉列表中应该看到 STORELOC 数据库连接,如 图 9 所示。

图 9. Choose Your Data Connection 对话框
Choose Your Data Connection 对话框的屏幕截图

单击 Next 继续,在 Save the Connection string 屏幕上,保留默认选择并单击 Next 进入 Configure the Select Statement 屏幕。选择 Specify a custom SQL statement or stored procedure 并单击 Next。在 SELECT 选项卡下的 SQL statement 文本框中,输入以下语句:select distinct(address_state) from store_view

单击 Next 进入下一屏幕,单击 Test Query 以确保您的语句能返回一些行。一切就绪后,单击 Finish。现在应该已返回到 Choose a Data Source 屏幕,ADDRESS_STATE 应该已预先选中在您启用的两个下拉列表中(见 图 10)。单击 OK 关闭对话框,DropDownList 控件现在应该拥有文本 Databound。

图 10. Choose a Data Source 对话框
Choose a Data Source 对话框的屏幕截图

Master Page 设计完成了,最终的 Design 视图应该如 图 11 中的屏幕截图所示。

图 11. 最终的 Master Page 视图
最终的 Master Page 视图的屏幕截图,带有 Search by State 下拉列表,Go! 按钮,以及添加和管理链接

但是在继续之前,还需要定义单击 Search by State 下拉列表旁边的 Go 按钮时将会发生的操作。为此,在 Design 视图中双击 Go 按钮,这将打开代码文件 MasterPage.master.cs,且光标位于 btnSearch_Click 函数内。在这个函数中,输入以下代码:Response.Redirect("SearchResults.aspx?state=" + ddlStates.SelectedValue);

这行代码将把用户重定向到 SearchResults.aspx 页面,给 Query String 参数传递州,值设置为下拉列表中当前选中的州。

Master Page 创建好了,项目也建立好了,现在可以开始开发 Store Locator 主程序了。在下一小节中,我们将集中精力开发 Client Interface,Web 站点访问者有可能在那里浏览您公司的商店。


创建客户端用户界面

在本小节中,我们将为 Store Locator 应用程序创建客户端用户界面。您将为第一次访问 Store Locator 的访问者创建一个登陆页面,提供所有商店的清单。接下来,将创建一个 Search Results 页面,当单击 Master Page 中 Search By State 旁边的 Go! 按钮时,这个页面将被调用。最后,将创建 View Store 页面,用于列出数据库中存储的关于一个特定商店的信息。

应用程序主页

要创建应用程序主页,在 Solution Explorer 中右键单击 Project 文件夹,从上下文菜单中选择 Add New Item。从模板列表中选择 Web Form 并将文件命名为 ListStores.aspx。确保选中的语言为 Visual C#,并同时选中 Place code in a separate fileSelect master page,如 图 12 所示。

图 12. Add New Item 对话框
Add New Item 对话框的屏幕截图,Web Form 模板选中

您现在将被要求选择一个主页面。选择 MasterPage.master 并单击 OK 继续。新的 Web Form 将在 Source 视图中打开,您会注意到这段代码拥有两个 <asp:Content> 部分。第二个部分(其中 ContentPlaceHolderID 值为 ContentPlaceHolder1)是您将放置组件的地方。

切换到 Design 视图,您会注意到 MasterPage 元素为浅灰色,不能访问或修改它们。您还应该注意到,ContentPlaceHolder 是页面的白色部分,周围有紫色边框,这是您将放置 Web 组件的地方。

首先,拖动一个 Label 到这个区域中。将 Text 属性更改为 Store List,修改 Font 属性以便字体大小为 x-large,文本以粗体显示。在 Visual Studio Toolbox 的 Data 部分,找到 DataList 组件并将其拖动到刚才添加的 Store List 标签的下方。右键单击 DataList 组件并选择 Show Smart Tag,从 Choose Data Source 列表中选择 New data source。这个屏幕应该看起来似曾相识。在第一个屏幕上选择 Database,然后从 Data connection 列表选择您此前保存的 Connection String。

在 Configure the Select Statement 屏幕上,保留 Specify columns from a table or view 选中,并从列表选择 STORE_VIEW 视图。您的视图中的列现在应该已被填充。选择 STORE_IDTITLE 并单击 ORDER BY 按钮。在第一个 Sort By 列表中,选择 TITLE> 并单击 OK。现在单击 Next,在下一个屏幕中测试您的查询是否有效。如果愿意,单击 Finish 退出对话框。您的数据现在将显示在 Design 视图中,如 图 13 所示。

图 13. DataList
DataList 的屏幕截图,显示从 STORE_ID 0 到 4 的垂直列表,每个 STORE_ID 都有 TITLE

可以看到,列表显示了 Store ID 和 Title,但是都在一条直线上。并不是很美观,对不对?幸运的是,Visual Studio 允许非常轻松地设置组件样式。在 DataList 的 Smart Tag 中,选择 Auto Format。在这里选择 Mocha 并单击 OK 以应用这个样式。接下来,选择 Property Builder。在 General 选项卡中 Repeat Layout 下面的 Columns 文本框中输入 2(因为您想显示两列,如 图 15 所示)。DataList 现在看起来应该如 图 14 所示。

图 14. 设置了样式的 DataList
设置了样式的 DataList 的屏幕截图,包含表格样式的 STORE_ID 0 到 4 列表,每个 STORE_ID 都有一个 TITLE

这个视图看起来好多了,但您只想在每个条目中显示 Title,并使它链接到这个特定商店的 View Store 页面。可以通过在 Smart Tag 中使用 Edit Templates 链接来完成这个任务,但我们将在 Source 视图中进行这个更改。在 <asp:DataList> 对象中,您将找到一个标记 <ItemTemplate>,使用 清单 8 中的代码替换 <ItemTemplate> 标记中的所有代码。

清单 8. DataList 条目模板代码
<a href="ViewStore.aspx?id=<%#Eval("STORE_ID") %>">
    <%#Eval("TITLE") %>
</a><br />

我们来看看运行应用程序时这个视图看起来会怎样。启动应用程序前,在 Solution Explorer 中右键单击 ListStores.aspx,选择 Set as Start Page 将其作为项目启动时的初始页面。现在,按 Ctrl+F5 键编译您的应用程序并在 Web 浏览器中打开它,结果看起来如 图 15 所示。

图 15. Store List 页面
Store List 页面的屏幕截图,显示位于 7 个城市和州的商店列表

在下一小节中,您将创建一个与这个页面的样式类似的页面,但不同的是:根据在 Master Page 中的主下拉列表中的选择,它只显示某个特定州的行。

Search Results 页面

Search Results 页面的创建过程与 ListStores 页面非常相似。在 Solution Explorer 中右键单击 Project 文件夹,选择 Add New Item。确保您选择的选项与此前相同,但这次要将条目命名为 SearchResults.aspx。同样,当您被要求选择一个 Master Page 时,选择 MasterPage.master。创建一个标签,将其样式设置为与此前相同,将其文本设置为 Search Results。在一个新行上输入文本 Results for,并拖动一个新标签到此文本的后面。将此标签的 ID 更改为 lblState 并清除 Text 属性。同样,设置此标签为粗体显示。现在,在 Solution Explorer 中右键单击 SearchResults.aspx 并选择 View Code

文件 SearchResults.aspx.cs 的代码将会显示,您将看到一个空的 Page_Load 方法。在这个方法中,添加 清单 9 中的代码。

清单 9. 设置选中的州
if(Request.QueryString.Count > 0)
{
    lblState.Text = Request.QueryString[0];
}
else
{
    lblState.Text = "MA";
}

接下来,返回 SearchResults.aspx 文件的 Design 视图,并添加几个换行符。从 Toolbox 的 Data 部分拖动一个 GridView 控件到页面上。再次选择 New Data Source,并完成选择一个数据源的过程。这个过程与 上一小节 基本相同,但这次配置 select 语句时应该选择以下列:STORE_ID、ADDRESS_STREET、ADDRESS_CITY、ADDRESS_STATE、ADDRESS_ZIP、PHONE 和 EMAIL。此外,单击 WHERE 按钮,因为您只想显示位于选中的州的商店。

在 Add WHERE Clause 对话框中(见 图 16),选择列列表中的 ADDRESS_STATE,运算符选择 =,并选择 QueryString 作为源。在参数属性中,应该指定 state 作为 QueryString 字段,MA 作为默认值。一切就绪后,单击 Add 按钮在您的 SQL 语句中创建这个子句。

图 16. Add WHERE Clause 对话框
Add WHERE Clause 对话框的屏幕截图,各字段根据上述描述设置

同样,您可以运行 Test Query 函数来验证您的查询是否实际有效。如果对结果满意,就单击 Finish 关闭向导。但是,这个 Grid 看起来相当丑陋 — 标题为大写并带有下划线;由于没有色彩,因此外观看起来很暗淡。您可以使用 Auto Format 选项轻松美化这个网格。同样,从格式化样式列表中选择 Mocha;勾选 Enable Sorting 复选框,这将允许用户通过单击某个特定列的标题来对数据进行排序。要处理列标题问题,在 GridView 的 Smart Tag 中单击 Edit Columns,这将打开 Fields 对话框,如 图 17 所示。

图 17. Fields 对话框
选中的 STORE_ID 字段的 Fields 对话框的屏幕截图

要更改某列的标题文本,从 Selected fields 列表选择那个列,编辑 BoundField 属性部分的 HeaderText 属性。对每个列重复这个过程。这个页面上需要的最后的更改是对每一行添加一个链接,链接到这个特定商店的 View Store 页面。在 GridView 的 Smart Tag 中,选择 Add New Column,这将打开 Add Field 对话框,如 图 18 所示。

图 18. Add Field 对话框
Add Field 对话框的屏幕截图,各字段根据上述描述设置

从类型选项中选择 HyperLinkField,将其头部值设置为 Actions。在 Hyperlink text 部分,选择 Specify text 单选按钮并输入 View。最后,在 Hyperlink URL 部分,选择 Get URL from data field 按钮并选择 STORE_ID 字段。在 URL format string 文本框中,输入:ViewStore.aspx?id={0}

您现在应该可以对 Search 特性进行测试运行了。这次,没有必要修改初始页面,因为您可以从应用程序中的任何页面执行搜索。只需按下 Ctrl+F5 键就可以编译并运行应用程序。在 Search by State 下拉列表中,选择一个州并单击 Go! 以导航到该州的 Search Results。加利福尼亚州的结果看起来如 图 19 所示。

图 19. 加利福尼亚州的 Search Results
加利福尼亚州的 Search Results 的屏幕截图,列出了位于 Santa Rosa 和 Los Angeles 的商店

接下来,将处理这个客户端问题的最后一个部分 —— View Store 页面,该页面用于显示一个特定商店的所有数据。

View Store 页面

再次,添加一个新条目到您的项目,这次将其命名为 ViewStore.aspx。添加一个标签,将其文本设置为 Store Information。在该标签下面添加另一个标签,文本设置为 Store Details,文本格式设置为大字体、加粗显示。现在,拖动一个 DetailsView 控件到这个标签下面。再次创建一个新的数据源,这次选择所有字段,除 STORE_VIEW 视图中的 latitude、longitude 和 zoom 字段之外。在 STORE_ID 列上添加一个 WHERE 子句,其 QueryString 源来自 id 字段,默认值为 1,如 图 20 中的 Add WHERE Clause 对话框所示。

图 20. Add WHERE Clause 对话框
Add WHERE Clause 对话框的屏幕截图,已经完成了 ViewStore 条目

再次选择 AutoFormat 并选择 Mocha 以设置 DetailsView 的样式。使用 DetailsView Smart Tag 中的 Edit Fields 选项来更改每个字段的头部文本。更改 HeaderStyle 以便它以粗体显示。将 DetailsView 对象的 Width 属性更改为 475px,以便所有数据都能完整显示。页面的 Design 视图在这个阶段应该看起来如 图 21 所示。

图 21. ViewStore.aspx 的 Design 视图
ViewStore.aspx 的 Design 视图的屏幕截图

接下来,将把商店的营业时间添加到这个页面。在 DetailsView 下方添加一个新标签,将其 Text 属性设置为 Store Opening Hours。在这个标签下方,拖动一个 GridView 控件,使用 AutoFormat 将其设置为 Mocha 样式。选择 New data source,这次从 STORE_HOURS_VIEW 视图选择列。选择 DAY 和 HOURS 列,然后单击 WHERE 按钮以定义如何确定将为哪个商店选择营业时间。这里选择的值应该与此前使用的值相同,如 图 20 所示。使用 Edit Columns 来格式化 Grid 的标题标签,然后按 Ctrl+F5 键运行应用程序。应用程序运行后,单击一个 Store 转到该商店的 View Store 页面。您看到的结果应该类似于 图 22 中的页面(我选择的是 Seattle 商店)。

图 22. View Store 页面
View Store 页面的屏幕截图,包含 Seattle WA 商店的详细信息和营业时间

这就是 View Store 页面,现在这个应用程序的客户端界面就完成了。在下一小节中,将了解如何创建添加新商店和删除现有商店的页面。在本教程后面添加商店位置的 Bing Map 视图时,将会返回这个 View Store 页面。


创建 Admin User Interface

这个 Store Locator 的 Admin User Interface 包含两个页面:

  • 一个页面用于向数据库添加新商店。开发 Add New Store 页面的方式与目前为止您开发所有屏幕的方式区别很大。您将主要使用代码创建这个屏幕,而不是使用拖放组件和向导的方式。
  • 另一个页面用于管理 —— 或者说,删除 —— 现有商店。Manage Stores 页面将使用向导创建,与 Search Results 页面类似,但该页面将在一个网格中显示所有商店,并允许您删除商店。

Add New Store 页面

正如本教程的简介所述,您将使用一种大不相同的技术来创建 Add New Store 页面,这种技术与目前为止您创建所有其他页面的技术都不相同。现在我们就深入其中。添加一个新条目到您的项目,将其命名为 AddNewStore.aspx。这次,不要离开 Source 视图,并在页面的 ContentPlaceHolder1 部分添加以下代码(参见 清单 10)。

清单 10. Add New Store Form
<asp:Label ID="Label1" runat="server" Font-Size="x-large" 
Font-Bold="true" Text="Add New Store" /><br />
<asp:Label ID="lblMessage" runat="server" Font-Size="large" 
Font-Bold="true" Visible="False" />
<table cellspacing="0" cellpadding="4" width="450" border="0" 
style="border: 1px solid #000">
    <thead><tr><th colspan="2" style="background-color: 
#ccc; border-bottom: 1px solid #000; text-align: left">Store 
Details</th></tr></thead>
    <tbody><tr><td width="150">Store Title:</td>
      <td width="300"><asp:TextBox ID="txtStoreTitle" runat="server" 
Columns="50" /></td>
    </tr>
    <tr><td width="150">Street Address:</td>
      <td width="300"><asp:TextBox ID="txtAddrStreet" runat="server" 
Columns="50" /></td>
    </tr>
    <tr><td width="150">City:</td>
      <td width="300"><asp:TextBox ID="txtAddrCity" runat="server" 
Columns="50" /></td>
    </tr>
    <tr><td width="150">State:</td>
      <td width="300"><asp:TextBox ID="txtAddrState" runat="server" 
Columns="50"  /></td>
    </tr>
    <tr><td width="150">Zip Code:</td>
      <td width="300"><asp:TextBox ID="txtAddrZip" runat="server" 
Columns="50" /></td>
    </tr>
<tr><td width="150">Store Manager:</td>
      <td width="300"><asp:TextBox ID="txtManager" runat="server" 
Columns="50" /></td>
    </tr>
<tr><td width="150">Phone Number:</td>
      <td width="300"><asp:TextBox ID="txtPhone" runat="server" 
Columns="50" /></td>
    </tr>
<tr><td width="150">Fax Number:</td>
      <td width="300"><asp:TextBox ID="txtFax" runat="server" 
Columns="50" /></td>
    </tr>
<tr><td width="150">Email Address:</td>
      <td width="300"><asp:TextBox ID="txtEmail" runat="server" 
Columns="50" /></td>
    </tr>
</tbody></table><br />

可以看到,清单 10 中的代码为每个 store detail 列定义 TextBox 字段:Title、Address data、Manager、Phone、Fax 和 Email。接下来,我们添加一些允许输入 Opening Hours 数据的字段(参见 清单 11)。应该将这段代码添加到 清单 10 中的代码下方,结束标记 </asp:Content> 的前面。

清单 11. Add New Store Opening Hours Form
<table cellspacing="0" cellpadding="4" width="450" border="0" 
style="border: 1px solid #000">
    <thead><tr><th colspan="2" style="background-color: #ccc; 
border-bottom: 1px solid #000; text-align: left">Opening 
Hours</th></tr></thead>
    <tbody><tr><td width="150">Monday:</td>
      <td width="300"><asp:TextBox ID="txtMonday" runat="server" 
Columns="50" /></td>
    </tr>
    <tr><td width="150">Tuesday:</td>
      <td width="300"><asp:TextBox ID="txtTuesday" runat="server" 
Columns="50" /></td>
    </tr>
    <tr><td width="150">Wednesday:</td>
      <td width="300"><asp:TextBox ID="txtWednesday" runat="server" 
Columns="50" /></td>
    </tr>
    <tr><td width="150">Thursday:</td>
      <td width="300"><asp:TextBox ID="txtThursday" runat="server" 
Columns="50" /></td>
    </tr>
    <tr><td width="150">Friday:</td>
      <td width="300"><asp:TextBox ID="txtFriday" runat="server" 
Columns="50" /></td>
    </tr>
<tr><td width="150">Saturday:</td>
      <td width="300"><asp:TextBox ID="txtSaturday" runat="server" 
Columns="50" /></td>
    </tr>
<tr><td width="150">Sunday:</td>
      <td width="300"><asp:TextBox ID="txtSunday" runat="server" 
Columns="50" /></td>
    </tr>
</tbody></table><br />

最后,在页面底端创建一个按钮,单击该按钮将把表单数据保存到 DB2 数据库中(参见 清单 12)。

清单 12. 保存商店数据的按钮
<table width="450">
    <tbody><tr><td align="center">
        <asp:Button ID="btnAdd" runat="server" 
          Text="Add New Store" />
    </td></tr></tbody>
</table>

清单 12 中的代码添加到 清单 11 中的代码下方。Add New Store 表单的布局创建好了,现在需要添加一些代码将数据实际保存到 DB2 数据库中。在 Solution Explorer 中右键单击 ViewStore.aspx 文件并选择 View Code 查看页面源代码。目前应该存在的所有源代码是一个空的 Page_Load 页面,以及一些用于将 .NET 特性导入文件中的 using 语句。

在最后一个 using 语句下方添加以下代码行(参见 清单 13)。

清单 13. 将数据保存到 DB2 数据库
using IBM.Data.DB2;
using System.Xml;

接下来,需要创建一个方法,它将建立到 DB2 数据库的连接。在 Page_Load 方法声明的上方添加以下代码(参见 清单 14),但代码应该位于类声明内部。

清单 14. ConnectDb 方法
public static DB2Connection ConnectDb() {
    String dsn = "STORELOC";
    String userid = "USERID";
    String password = "PASSWORD";

    String connectString = "Database="+dsn+";UID="+userid+";PWD="+password;

    DB2Connection conn = new DB2Connection(connectString);
    try {
        conn.Open();
    } catch(Exception e) {
        Console.WriteLine(e.Message);
    }

    return conn;
}

当然,要确保使用您自己的凭证替换 USERIDPASSWORD 值。这个方法创建好了,现在可以将注意力转向处理 Add New Store 按钮被单击时将发生的事件。切换到 Design 视图并双击该按钮,这将回到代码中,并且有了一个新方法 btnAdd_Click。在代码块中添加 清单 15 中的代码。

清单 15. 准备 DB2 连接
DB2Connection conn = ConnectDb();
DB2Command cmd = conn.CreateCommand();
XmlDocument x = new XmlDocument();
try {

} catch(Exception ex) {
    Console.WriteLine(ex.Message);
} finally {
    conn.Close();
}

清单 15 中的 try 代码块中,需要根据 Web Form 中输入的值构建一个 XML 文档。首先来看主要的商店详细信息,比如标题、联系人和经理信息(参见 清单 16)。

清单 16. 构建 XML 文档
XmlElement store = x.CreateElement("store");
x.AppendChild(store);

XmlElement title = x.CreateElement("title");
store.AppendChild(title);
title.InnerXml = txtStoreTitle.Text;

XmlElement contact = x.CreateElement("contact");
store.AppendChild(contact);
XmlElement address = x.CreateElement("address");
contact.AppendChild(address);
XmlElement street = x.CreateElement("street");
XmlElement city = x.CreateElement("city");
XmlElement state = x.CreateElement("state");
XmlElement zip = x.CreateElement("zip");
address.AppendChild(street);
address.AppendChild(city);
address.AppendChild(state);
address.AppendChild(zip);
street.InnerXml = txtAddrStreet.Text;
city.InnerXml = txtAddrCity.Text;
state.InnerXml = txtAddrState.Text;
zip.InnerXml = txtAddrZip.Text;

XmlElement phone = x.CreateElement("phone");
XmlElement fax = x.CreateElement("fax");
XmXmlElement email = x.CreateElement("email");
contact.AppendChild(phone);
contact.AppendChild(fax);
contact.AppendChild(email);
phone.InnerXml = txtPhone.Text;
fax.InnerXml = txtFax.Text;
email.InnerXml = txtEmail.Text;

XmlElement manager = x.CreateElement("manager");
store.AppendChild(manager);
manager.InnerXml = txtManager.Text;

清单 16 中,首先是创建一个 XML 元素 <store>,并将其附加到 XML 文档 x。接下来,创建一个 <title> 元素并将其添加到 <store> 元素,然后是 <contact> 及其子节点,最后是 <manager> 元素。对于每个元素,您将节点的 InnerXml 值设置为 Web form 中相关文本字段的 Text 属性。

清单 16 中的代码下方,添加 清单 17 中的代码。

清单 17. 构建 XML 文档(续)
XmlElement opening_hours = x.CreateElement("opening_hours");
store.AppendChild(opening_hours);

XmlElement Monday = x.CreateElement("day");
XmlElement Tuesday = x.CreateElement("day");
XmlElement Wednesday = x.CreateElement("day");
XmlElement Thursday = x.CreateElement("day");
XmlElement Friday = x.CreateElement("day");
XmlElement Saturday = x.CreateElement("day");
XmlElement Sunday = x.CreateElement("day");
opening_hours.AppendChild(Monday);
opening_hours.AppendChild(Tuesday);
opening_hours.AppendChild(Wednesday);
opening_hours.AppendChild(Thursday);
opening_hours.AppendChild(Friday);
opening_hours.AppendChild(Saturday);
opening_hours.AppendChild(Sunday);
Monday.SetAttribute("name", "Monday");
Tuesday.SetAttribute("name", "Tuesday");
Wednesday.SetAttribute("name", "Wednesday");
Thursday.SetAttribute("name", "Thursday");
Friday.SetAttribute("name", "Friday");
Saturday.SetAttribute("name", "Saturday");
Sunday.SetAttribute("name", "Sunday");
Monday.InnerXml = txtMonday.Text;
Tuesday.InnerXml = txtTuesday.Text;
Wednesday.InnerXml = txtWednesday.Text;
Thursday.InnerXml = txtThursday.Text;
Friday.InnerXml = txtFriday.Text;
Saturday.InnerXml = txtSaturday.Text;
Sunday.InnerXml = txtSunday.Text;

清单 17 中的代码遵循此前的规则,只有一点例外:对于每个 <day> 节点,您还设置了 name 属性和 InnerXml 属性。

XML 文档准备就绪,现在可以将其添加到数据库了。在 清单 17 中的代码下方添加以下代码(参见 清单 18)。

清单 18. 执行数据库插入操作
cmd.Parameters.Add(new DB2Parameter("@XMLData", x.InnerXml));
String stmt = "INSERT INTO store(info) VALUES(@XMLData);";
cmd.CommandText = stmt;
int rowsAffected = cmd.ExecuteNonQuery();

if(rowsAffected > 0) {
    lblMessage.Text = "Store added successfully.";
} else {
    lblMessage.Text = "Could not add store.";
}

lblMessage.Visible = true;

Add New Store 页面开发到此结束。按 Ctrl+F5 键运行应用程序。单击页面顶部的 Add New Store 链接转到 Add New Store Web Form,填充表单字段,然后单击 Add New Store 按钮。图 23 展示了填充好的表单。

图 23. Add New Store Form
Add New Store Form 的屏幕截图,字段包含样例信息

接下来,将创建一个用于删除位置的页面。

Manage (Delete) Stores 页面

在 Solution Explorer 中,右键单击 Project 文件夹并选择 Add New Item,添加一个新的 ASP.NET 页面到项目,将其命名为 ManageStores.aspx。同样,在页面上创建一个 Label,将字体格式设置为粗体显示、大字体。将 Text 属性更改为 Manage Stores。在这个标签下方创建另一个标签,将其 ID 设置为 lblMessage,Font-Size 设置为 Large,Visible 属性设置为 false,并清除 Text 属性的内容。

接下来,拖动一个 GridView 组件到这些标签下方,使用 AutoFormat 将其设置为 Mocha 样式并建立一个新的数据源。配置 SELECT 语句时,选择 Specify a custom SQL statement or stored procedure。在 SELECT 选项卡中,输入以下语句:select TITLE, PHONE, EMAIL, MANAGER, STORE_ID from SCHEMA.STORE_VIEW order by TITLE

不要忘记将 SCHEMA 更改为您自己的数据库模式名。接下来,切换到 DELETE 选项卡并输入以下语句:delete from SCHEMA.STORE where STORE_ID=@STORE_ID;

在测试页面是否有效之前,首先要格式化 GridView 控件中的列标题(将大写改为小写)。另外,在 GridView 的 Smart Tag 中勾选 Enable PagingEnable SortingEnable Deletion 复选框。

接下来,切换到 Source 视图并找到 <sql:DataSource> 标记。添加以下新属性:OnDeleted="OnRecordDeleted"。另外,找到 <asp:GridView> 标记并添加以下属性:DataKeyNames="STORE_ID"

接下来,要打开该页面的 Source 视图,右键单击页面任意位置,选择 View Code。在 Page_Load 方法下添加以下方法(参见 清单 19)。

清单 19. OnRecordDeleted 方法
protected void OnRecordDeleted(object source, SqlDataSourceStatusEventArgs e) {
    lblMessage.Text = "Store was deleted successfully.";
    lblMessage.Visible = true;
}

现在按 Ctrl+F5 键编译并运行应用程序。单击 Manage Stores 查看新创建的 Manage Stores 页面。可以看到,所有商店均已显示出来(如果您拥有 10 个以上商店且启用了分页功能,则只显示前 10 个商店)。尝试删除一个商店;该商店将从 GridView 删除,您应该看到一条消息,告知您该商店已经成功删除。图 24 展示了这个页面。

图 24. Manage Stores 页面
Manage Stores 页面的屏幕截图,显示 7 个商店的信息

这个 Store Locator 应用程序的管理员界面的开发到此结束。在下一小节中,通过将 Bing Maps API 集成到 View Store 和 Add New Store 页面,给应用程序添加一些很棒的功能。


添加 Bing Maps 功能

至此,您可能注意到,这个 Store Locator 应用程序还没有使用在本教程之初与其他商店数据一起从一系列 XML 文件导入的经度和纬度值。在本小节中,您将完善应用程序,实现 Microsoft® Bing Maps API,以便在 View Store 页面上显示一个地图,指出商店的位置。另外,还将添加一个地图到 Add New Store 页面,您只要在地图上的一个点上按 Ctrl+Click 键,就可以将商店的坐标设置在那个点上。

在地图上查看商店

打开 ViewStore.aspx 页面并确保您处于 Source 视图中。首先,定义将在页面的什么位置显示地图。在文件末尾、</asp:Content> 结束标记之前添加以下代码(参见 清单 20)。

清单 20. 添加 myMap div
<asp:Label ID="lblLoc" runat="server" Font-Bold="True" 
  Font-Size="Large" Text="Store Location:"></asp:Label>
<div id="myMap" style="position: relative; width: 400px; 
height: 400px"></div>

接下来,向上滚动这个文件的 Source 视图直到到达第一个 <asp:Content> 标记(它的 ContentPlaceHolderID 本来应该设置为 head,但当前为空)。在这个标记中,插入 清单 21 中的 JavaScript 代码。

清单 21. 加载 Bing Maps
<script type="text/javascript" 
        src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2">
</script>
<script type="text/javascript">
var map = null;
var pinid = 0;

function GetMap(lat, lng, zoom) {
    map = new VEMap('myMap');
    map.LoadMap(new VELatLong(lat, lng), zoom, 'Road', false);

    var shape = new VEShape(VEShapeType.Pushpin, map.GetCenter());
    pinid++;
    map.AddShape(shape);
}
</script>

GetMap() JavaScript 函数添加到了这个页面,现在就需要在页面加载时调用这个函数。要打开 ViewStore.aspx.cs 的 Code View,在 Source 视图中右键单击并选择 View Code。通过将以下语句添加到文件顶部的 using 语句列表下方,将以下引用添加到文件:using IBM.Data.DB2;

首先,将一个新方法 ConnectDb 添加到这个文件中的 Page_Load 上方,清单 22 展示了这个方法的代码。

清单 22. ConnectDb 方法
public static DB2Connection ConnectDb() {
    String dsn = "STORELOC";
    String userid = "USERID";
    String password = "PASSWORD";

    String connectString = "Database="+dsn+";UID="+userid+";PWD="+password;

    DB2Connection conn = new DB2Connection(connectString);
    try {
        conn.Open();
    } catch(Exception e) {
        Console.WriteLine(e.Message);
    }

    return conn;
}

接下来,将 清单 23 中的代码添加到 Page_Load 方法中,以便在页面加载时调用该方法。

清单 23. Page_Load 方法内容
if(Request.QueryString.Count > 0) {
    DB2Connection conn = ConnectDb();
    DB2Command cmd = conn.CreateCommand();
    String stmt = "SELECT latitude, longitude, zoom FROM store_view WHERE store_id =
 "+Request.QueryString["id"].ToString();

    cmd.CommandText = stmt;
    DB2Record row = cmd.ExecuteRow();

    HtmlGenericControl body = (HtmlGenericControl)Master.FindControl("pageBody");
    body.Attributes.Add("onload", "GetMap('" + row.GetString(0) + "', '" + 
row.GetString(1) + "', '" + row.GetString(2) + "')");
}

现在已经准备就绪,可以编译和运行应用程序了。按 Ctrl+F5 键打开 Web 浏览器。单击任何商店转到该商店的 View Store 页面。应该看到,页面底端有一个地图,地图上标示了商店的位置,如 图 25 所示。

图 25. 使用 Bing Maps 的 Store Location
使用 Bing Maps 的 Store Location 信息屏幕截图

接下来,将学习如何在将一个新商店添加到数据库时设置该商店位置的经度、纬度和缩放级别。

使用地图设置商店的位置

这个应用程序要添加的最后一个特性是,允许通过在 Add New Store 页面的地图上按 Ctrl+Click 键来设置商店的位置。首先,在 Source 视图中打开 AddNewStore.aspx 文件,向下滚动到文件底部,在包含 <asp:Button> 组件的 <table> 标记之前插入 清单 24 中的代码。

清单 24. Map 表
<br /><table cellpadding="4" cellspacing="0" border="0" width="450"
  style="border: 1px solid #000;">
    <thead><tr><th colspan="2" style="background-color: 
#ccc; border-bottom: 1px solid #000; text-align: left">Store
 Location</th></tr></thead>
    <tbody>
    <tr><td colspan="2" align="center">Ctrl + Click to select 
a location<br />
    <div id="myMap" style="position: relative; width: 400px; height:
 400px;"></div></td></tr>
    <tr><td width="150">Latitude:</td>
    <td width="300"><asp:TextBox ID="txtLatitude" runat="server" Columns="50"
 /></td></tr>
    <tr><td>Longitude:</td>
    <td><asp:TextBox ID="txtLongitude" runat="server" Columns="50" 
/></td></tr>
    <tr><td>Zoom Level:</td>
    <td><asp:TextBox ID="txtZoomLevel" runat="server" Columns="50"
 /></td></tr>
    </tbody>
</table>

接下来,像 上一小节 那样向上滚动到文件顶部,将 清单 25 中的代码添加到第一个 <asp:Content> 标记(内容为空)中。

清单 25. 加载 Bing Maps 和鼠标事件
<script type="text/javascript" 
        src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.2">
</script>
<script type="text/javascript">
var map = null;
var pinid = 0;

function GetMap(lat, lng, zoom) {
    map = new VEMap('myMap');
    map.LoadMap();

    map.AttachEvent("onclick", MouseClick);

    function MouseClick(e) {
        if(e.ctrlKey) {
            map.DeleteAllShapes();
            var location = map.PixelToLatLong(new VEPixel(e.mapX, e.MapY));
            var shape = new VEShape(VEShapeType.Pushpin, location);
            pinid++;
            map.AddShape(shape);

            document.getElementById('<%= txtLatitude.ClientID %>')
.value = location.Latitude;
            document.getElementById('<%= txtLongitude.ClientID %>')
.value = location.Longitude;
            document.getElementById('<%= txtZoomLevel.ClientID %>')
.value = map.GetZoomLevel();
        }
    }
}
</script>

这段代码将把地图加载到默认位置并监听地图上的鼠标事件。只要单击鼠标,该代码就会检查是否按下了 Ctrl 键,如果按下了,就会删除地图中的所有现有图钉,并在检测到 Ctrl+Click 的位置添加一个新图钉。坐标文本框将被更新,以反映这个新位置。

接下来,需要更改 C# 代码文件。右键单击 AddNewStore.aspx 页面上的任意位置并选择 View Code。找到 Page_Load 方法并将 清单 26 中的代码添加到这个方法中。

清单 26. 加载 Map Control 并将坐标设置为只读
HtmlGenericControl body = (HtmlGenericControl)Master.FindControl("pageBody");
body.Attributes.Add("onload", "GetMap()");
txtLatitude.Attributes.Add("readonly", "readonly");
txtLongitude.Attributes.Add("readonly", "readonly");
txtZoomLevel.Attributes.Add("readonly", "readonly");

这确保在页面加载结束时加载地图,并将坐标文本框设置为只读,但坐标文本框中设置的数据值仍将被发送到服务器。

最后的更改是,更改 btnAdd_Click 方法中将 XML 数据实际添加到数据库的代码。找到下面的行:Sunday.InnerXml = txtSunday.Text;

在这一行下面,添加以下代码,确保地理数据随同其他商店详细信息一起添加到数据库(参见 清单 27)。

清单 27. 将坐标添加到数据库的代码
XmlElement location = x.CreateElement("location");
store.AppendChild(location);

XmlElement latitude = x.CreateElement("latitude");
XmlElement longitude = x.CreateElement("longitude");
XmlElement zoom = x.CreateElement("zoom");
location.AppendChild(latitude);
location.AppendChild(longitude);
location.AppendChild(zoom);
latitude.InnerXml = txtLatitude.Text;
longitude.InnerXml = txtLongitude.Text;
zoom.InnerXml = txtZoomLevel.Text;

应用程序现在已经就绪,可以使用了。按 Ctrl+F5 再次启动它,这次导航到 Add New Store 页面。输入一些商店详细信息,在页面底端,放大并移动地图以找到商店位置,当准备好选择位置时,按住 Ctrl 键并在地图上单击商店所处的位置。如果选择了错误的位置该怎么办?没有关系,只需再次按 Ctrl+Click 键,图钉就会移动到另一个位置。图 26 展示了这个特性。

图 26. 在地图上选择一个位置
在地图上选择一个位置的屏幕截图

本教程到此结束。请简单浏览一下下一小节,了解也许能够改进这个应用程序的一些建议。

改进建议

利用在本教程中学习到的技巧,应该能够对这个应用程序进行一些细微但有用的改进。以下是一些可能的改进:

  • 通过其他字段搜索:邮政编码、城市等
  • 在地图上显示多个位置
  • 在地图上的弹出窗口中显示商店详细信息
  • 获取到某个商店的驾车指南
  • 将商店地址导出到一个 KML 文件,以便在 Google Earth 这样的应用程序中查看它们
  • 实现商店编辑特性
  • 在管理员界面周围添加一个安全层,以便只有经过授权和验证的用户能够管理商店
  • 使用一个地理编码服务,通过输入邮政编码找到距离您最近的商店

结束语

在本教程中,学习了如何创建一个将 XML 数据原生地存储到关系表中的 DB2 pureXML 数据库,如何创建 XML 数据的关系视图,如何批量导入 XML 文档。接下来,学习了如何创建一个使用 DB2 Data Adapter 的 ASP.NET 应用程序,DB2 Data Adapter 允许您的 .NET 项目与 DB2 服务器进行通信。还创建了一系列客户端和管理员页面,它们允许用户查找、查看、添加和管理数据库中的商店。最后,向这个应用程序添加了一些特性,用于将商店坐标标示在一个 Bing Map 上,允许您通过在地图上放置图钉来标明新商店的位置。


下载

描述名字大小
Store Locator 源代码locator.source.zip19KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML, Information Management
ArticleID=461606
ArticleTitle=使用 IBM DB2 pureXML 和 ASP.NET 开发一个商店定位器应用程序
publish-date=01142010