最近发布的Microsoft .NET Compact Framework beta1版包括了SQL Server CE2.0,SQL Server CE将SQL Server 2000扩展到windows CE环境中,同时它提供了与桌面应用程序开发相似的的开发环境。在这篇文章中我将初步介绍SQL CE 以及如何利用Smart Device Extension开发Pocket PC上的应用程序。
.NET Compact Framework体系结构 .NET Compact Framework(CF)是.NET Framework的子集,他最主要的优点是与.net类库具有相同的灵活性。但是所有.NET Framework的类和方法均能在.NET Compact Framework(CF)中使用。下图显示了一个移动平台的构成体系。
图1
这个平台允许Windows CE自携带的应用程序与基于.net的应用程序共存。应用程序的宿主(本身也是一个应用程序)用一个公共运行时语言(CRL Common Language Runtime)的实例来管理代码。通过SDE利用.NET Compact Framework类库在CRL的顶端开发应用程序。
构造一个应用程序样本实例 在这个样本实例中将揭示.NET CF Pocket PC程序设计中要注意的几个方面。我将解释在.NET CF中怎样使用Web Service,怎样使用各种空件,以及怎样构件SQL Server CE数据库。这是一个书店的例子,例子中书店的售货员使用Pocket PC进行查询(在Web Sevice中),并在书店中下订单,这个订单将被提交给本地的SQL Server CE数据库。
Web Sevice Web Sevice将使用SQL Server2000中携带的Pubs数据库,这样你就可以很容易的在自己的机器上测试代码。
getTitles():
<%@ WebService Language="VB" Class="Service1" %>
Imports System Imports System.Data Imports System.Data.SqlClient Imports System.Web.Services
Public Class Service1 : Inherits WebService
_ Public Function getTitles( _ ByVal title As String) As DataSet
' Make the database connection. Dim conn As New SqlConnection( _ "server=localhost; uid=sa;" & _ "password=; database=Pubs")
' Create the SQL and set the parameter. Dim sql As String = "SELECT * FROM " & _ "titles WHERE title LIKE @title" Dim comm As New SqlCommand(sql, conn) comm.Parameters.Add("@title", _ "%" & title & "%")
' Create a data adapter and data set. Dim dataAdapter As New SqlDataAdapter(comm) Dim ds As New DataSet()
' Fill the data set with the query results. conn.Open() dataAdapter.Fill(ds, "titles") conn.Close()
' Return the dataset. Return ds
End Function
End Class |
getTitles()方法获得查询字符串作为输入参数,同时返回数据集。
使用 Visual Studio .NET中的 Smart Device Extension (SDE) 在建立Web Sevice后,我们继续用SDE创建Pocket PC的应用程序。我们要开发的这个Pocket PC应用程序由一个tab控件和两个tab页组成,第一个tab页允许售货员通过书店搜索书籍并向书店下订单,第二个tab页则显示书店的订单。图2显示了程序重要使用的各种控件,这些控件有:label,textbox,button,combox,listbox和tab等控件:
图2
在form第一次运行的时候,首先需要核查Pocket PC是否有包含书店信息的数据库,如果没有,那么就要用SQL Server CE引擎对象创建数据库。由于需要与SQL Server CE建立连接,所以必须使用SQL Server CE Managed Provider,因此第一件事情是引用System.Data.SqlServerCe.dll配置和输入相关的名称空间。
Imports System.Data.SqlServerCe
图3
建立数据库后,就需要创建表,操作表就必须熟悉ADO.NET类库,在这个例子中我们将在SQL Server CE Managed Provider使用类: SqlCeConnection 和 SqlCeCommand类。
'-----conn and ds are defined globally----- Dim conn As New SqlCeConnection( _ "Provider=Microsoft.SQLServer.OLEDB.CE.1.0;" & _ "Data Source=\My Documents\BookStores.sdf") Dim ds As DataSet '------------------------------------------ Sub createStoreDB() ' if database does not exist, create one If Not File.Exists( _ "\My Documents\BookStores.sdf") Then Dim sqlEngine As New Engine( _ "Data Source=" & _ "\My Documents\BookStores.sdf") sqlEngine.CreateDatabase()
Dim cmd As New SqlCeCommand( _ "CREATE TABLE Stores(storeID int " & _ "Primary Key NOT NULL, " & _ "storeName nvarchar(20))", conn) conn.Open() cmd.ExecuteNonQuery() cmd.CommandText = _ "CREATE TABLE Orders(storeID int, " & _ "title_id nvarchar(20), qty int)" cmd.ExecuteNonQuery()
cmd.CommandText = _ "INSERT INTO Stores (storeID, " & _ "storeName) VALUES (1, " & _ "'Great BookStore')" cmd.ExecuteNonQuery()
cmd.CommandText = _ "INSERT INTO Stores (storeID, " & _ "storeName) VALUES (2, " & _ "'Computer BookStore')" cmd.ExecuteNonQuery() conn.Close() End If End Sub |
在上面的代码中我们创建了两个表:Orders和Store。Orders存储来自售货员的订单,Stores存储书店的库存,出于方便我已经在Stores中输入了两条纪录。
图4
下一步使用SqlCeDataReader类将书店列表价载入ComboBox控件中。
Sub LoadStores() conn.Open() Dim reader As SqlCeDataReader Dim cmd As New SqlCeCommand( _ "SELECT * FROM Stores", conn) reader = cmd.ExecuteReader While reader.Read cboStoreID.Items.Add( _ reader.Item("storeID")) End While conn.Close() End Sub |
这样当表单被载入时,ComboBox控件就填充了书店列表。
图5
当选中Store ID时,就显示他代表的
Private Sub cboStoreID_SelectedIndexChanged( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cboStoreID.SelectedIndexChanged conn.Open() Dim sql As String = _ "SELECT * FROM Stores WHERE storeID=" & _ cboStoreID.Items(cboStoreID.SelectedIndex) Dim cmd As New SqlCeCommand(sql, conn) Dim reader As SqlCeDataReader = cmd.ExecuteReader reader.Read() lblStoreName.Text = reader.Item("storeName") conn.Close() End Sub |
调用Web Sevice 对于特殊的书籍查询,应用程序将调用Web Sevice,在.NET CF中调用Web Sevice与在.NET Framework中没有什么差别,要注意的是Web Sevice必须与实际机器上使用的名称相同,否则Web Sevice将不会工作。
下面将Web Sevice与ComboBox控件绑定:
Private Sub cmdSearch_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cmdSearch.Click Dim ws As New TitlesWS.Service1() ' get the web service ds = ws.getTitles(txtSearch.Text) cboResult.DataSource = ds.Tables(0) cboResult.DisplayMember = "title" End Sub
|
实际上当书名被选定后,他更多的信息将显示在label控件上。
Private Sub cboResult_SelectedIndexChanged( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cboResult.SelectedIndexChanged ' display the information of the ' selected book. Dim row As DataRow row = ds.Tables("titles").Rows( _ cboResult.SelectedIndex) lblTitleID.Text = row.Item("title_id") lblPrice.Text = "$" & row.Item("price") txtNotes.Text = row.Item("notes") End Sub |
图6
显然ADD按钮用于增加订单中的数量,因此必须给ADD按钮添加一个单击事件:
Private Sub cmdAdd_Click( ByVal sender As System.Object,ByVal e As System.EventArgs) _ Handles cmdAdd.Click '---add the title to the stores ORDER table conn.Open() Dim sql As String = "INSERT INTO Orders " &_ "(storeID, title_id, Qty) VALUES (" & _ cboStoreID.Items(cboStoreID.SelectedIndex) _ & ",'" & _ lblTitleID.Text & "'," & txtQty.Text & ")" Dim cmd As New SqlCeCommand(sql, conn) cmd.ExecuteNonQuery() MsgBox("Title added for " & lblStoreName.Text, _ MsgBoxStyle.Information, "Orders") conn.Close() End Sub |
返回消息框证实增加数量。
图7
审核订单 点击第二个tab页显示订单,点击Refresh按钮第一个ListBox控件将显示订单。
Private Sub cmdRefresh_Click( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cmdRefresh.Click '---displays the list of stores available conn.Open() Dim sql As String = "SELECT * FROM Stores" Dim cmd As New SqlCeCommand(sql, conn) Dim reader As SqlCeDataReader = _ cmd.ExecuteReader '---clears the listbox cboStoreIDs.Items.Clear() While reader.Read() cboStoreIDs.Items.Add( _ reader.Item("storeID")) End While conn.Close() End Sub |
当一个书店被选中之后,该书店相应的订单就会显示在第二个Listbox控件中。
Private Sub cboStoreIDs_SelectedIndexChanged( _ ByVal sender As System.Object, _ ByVal e As System.EventArgs) _ Handles cboStoreIDs.SelectedIndexChanged '---displays the orders of store selected conn.Open() Dim sql As String = _ "SELECT * FROM Orders WHERE storeID=" & _ cboStoreIDs.Items(cboStoreIDs.SelectedIndex) Dim cmd As New SqlCeCommand(sql, conn) Dim reader As SqlCeDataReader = _ cmd.ExecuteReader() '---clears the listbox cboOrders.Items.Clear() While reader.Read() cboOrders.Items.Add( _ reader.Item("title_id") & " - " & _ reader.Item("qty") & "-copy(ies)") End While conn.Close() End Sub |
图8
最后你也许注意到了,表单的底部没有虚拟键盘,如果使用仿真系统,那么这个缺陷就不那么明显,但在使用真实设备并准备输入查询条件的时候,你会感到很为难,不过不用担心我们可以在表单中添加一个菜单条,不过在这之后不要忘记将控件的位置作一些调整。
使用SQL Server查询分析器 SDE毕竟是Beta1版,他仍然存在一些Bug,在测试应用程序的时候遇到的最大的问题是-Web Sevice没有工作,即当点击查询按钮后,Web Sevice没有响应,这时有一个方法可以检测Web Sevice是否工作,就是Output窗口将显示一些信息,以指明那些线程存在。
图9
如果没有这些信息显示,那么Web Sevice就没有被调用,这种情况下,简单的终止应用程序,然后再重新启动他就可以了。
另一个问题是,如果你想知道数据表是否被正确的更新了。SQL Server 2.0引入了类一个被称为SQL Server CE查询分析器(SQL Server CE Query Analyzer)的工具,在应用程序第一次引用SQL Server CE Managed Provider时,SDE将把相关文件拷贝到目标设备,SQL Server CE查询分析器可以通过点击Start->SQLCE Query(如下图)调用。
图10
要连接SQL Server CE数据库,点击显示在底部的绿色箭头和圆柱体。
图11
这时候你可以连接一个已存在的数据库或创建一个新的数据库。下图显示了我们创建的数据库表和域。
图12
要取回表中的数据,我们可以在SQL标签下使用SQL语句,然后在Grid标签下察看输出结果。
图13
查询分析器的一个好的特征是"按钮预置",按钮预置功能允许你将常用的SQL语句保存起来,那么下一次调用的时候你只需要简单的点击一下预设的按钮即可。
图14
重新设置仿真器 有些时候查询分析器会拒绝工作,这也许只是Beta类产品的自然特性。在很多例子中在仿真器中将软件重新设置即可解决这个问题。一般情况下重新设置软件不会影响数据库,但过于频繁的那么就有可能导致找不到保存的数据库,这时找回他的唯一方式是在Visual Studio .NET中重新编译软件(就像数据库重新创建一样)。
打开和关闭连接 也许你注意到了应用程序在每一个操作前后均要打开或关闭数据库连接。但我发现在现在的Beta版下,当应用还存在的时候连接关闭,仿真器将会发生冲突。
结论 本文讲述了怎样创建使用SDE创建一个调用Web Sevice的Pocket PC应用程序,以及怎样使用SQL Server CE 2.0将信息存储在本地设备上,同时我们也涉及到了SDE的一些缺陷,希望在正是发布的版本中午按能消灭这些Bug。