用C#開(kāi)發(fā)Pocket PC數(shù)據(jù)庫(kù)應(yīng)用程序

2010-08-28 10:49:44來(lái)源:西部e網(wǎng)作者:

   摘要:本文講解怎樣使用Pocket PC Phone Edition工具集迅速建立無(wú)線數(shù)據(jù)庫(kù)應(yīng)用程序。本文介紹的是使用Visual C# 和SQL Server CE 2.0建立一個(gè)Pocket PC Phone Edition應(yīng)用程序。

新的移動(dòng)計(jì)算時(shí)代開(kāi)始了

   移動(dòng)解決方案系統(tǒng)體系結(jié)構(gòu)的一部分正在迅速成為現(xiàn)實(shí)。最先是很多公司不斷意識(shí)到移動(dòng)解決方案可以帶來(lái)效率的提高和新的商業(yè)機(jī)會(huì)。顧客正在每天的生活中開(kāi)始看到移動(dòng)設(shè)備的使用。移動(dòng)設(shè)備硬件,特別是新的Pocket PC,正在引領(lǐng)企業(yè)和顧客需求的方向。把每種需求膠合在一起的關(guān)鍵就是新的軟件和開(kāi)發(fā)工具。

   我使用新的Visual Studio .NET、Smart Device Extensions和新的SQL Server? CE 2.0工作了幾個(gè)月時(shí)間。這些軟件組件與連接的(connected)Pocket PC,例如Pocket PC Phone Edition,提高了開(kāi)發(fā)效率并且已經(jīng)驅(qū)動(dòng)了很多新的移動(dòng)應(yīng)用程序。

SQL Server CE的新特性

   總的來(lái)說(shuō),SQL Server CE反映了一種矛盾。作為移動(dòng)設(shè)備的本地?cái)?shù)據(jù)庫(kù),SQL Server CE支持不連接的環(huán)境。在大多數(shù)情況中,沒(méi)有網(wǎng)絡(luò)連接的Pocket PC上運(yùn)行的Pocket PC應(yīng)用程序需要本地?cái)?shù)據(jù)存儲(chǔ)。SQL Server CE也支持連接的環(huán)境,并且使數(shù)據(jù)向遠(yuǎn)程服務(wù)器或從遠(yuǎn)程服務(wù)器傳遞效率很高,無(wú)論是從開(kāi)發(fā)還是從帶寬來(lái)看。

   下面是SQL Server CE的部分特性:

· 為本地的SQL Server CE管理和遠(yuǎn)程的SQL Server連接與.NET緊湊框架組件集成。

· 連接安裝向?qū)筍QL Server CE組件的安裝簡(jiǎn)化。

· 大量的內(nèi)部函數(shù),包括很有價(jià)值的NEWID、CHAR、CHARINDEX、UNICODE、LEN、LTRIM、RTRIM、SPACE、 SUBSTRING、IDENT99vY、DATALENGTH等等。

· 聯(lián)合(UNION,例如SELECT * FROM Orders UNION SELECT * FROM OldOrders)

· 使用遠(yuǎn)程數(shù)據(jù)訪問(wèn)牽引(Remote Data Access Pull)從遠(yuǎn)程服務(wù)器表索引得到數(shù)據(jù)的能力。

· 改良的SQL Server查詢(xún)分析器(Query Analyzer)。

   新的SQL Server CE 2.0數(shù)據(jù)訪問(wèn)結(jié)構(gòu)依賴(lài)下面名字空間中的類(lèi):

· System.Data.SqlServerCE(使用合并復(fù)制和遠(yuǎn)程數(shù)據(jù)訪問(wèn)管理本地?cái)?shù)據(jù)庫(kù)和遠(yuǎn)程服務(wù)器連接)。

· System.Data.SqlClient(管理遠(yuǎn)程數(shù)據(jù)庫(kù)并且包含對(duì)TSQL和存儲(chǔ)過(guò)程的支持)。

隨著數(shù)據(jù)訪問(wèn)結(jié)構(gòu)轉(zhuǎn)移到.NET緊湊框架組件,它里面的組件得到了改良,并且更容易使用了。例如,先前的合并復(fù)制初始化(Merge Replication Initialize)、運(yùn)行和終止大綱(schema)被一個(gè)單獨(dú)的方法System.Data.SQLServerCE.Replication.Synchronize所代替,它在第一次同步的時(shí)候建立大綱并下載數(shù)據(jù),接著提交修改過(guò)的數(shù)據(jù),并在接下來(lái)的同步中下載修改過(guò)的數(shù)據(jù)。

新的遠(yuǎn)程數(shù)據(jù)訪問(wèn)(Remote Data Access)類(lèi)也有一定的改良,包含了從遠(yuǎn)程表索引取得數(shù)據(jù)的能力和為Push方法定義批處理模式(batch-mode)的能力。我將介紹一個(gè)"高爾夫得分卡"示例應(yīng)用程序,它是使用SQL Server CE 2.0、 遠(yuǎn)程數(shù)據(jù)訪問(wèn)和Visual C#建立的。

示例應(yīng)用程序:界面

示例應(yīng)用程序Golf Anyplace可以運(yùn)行在標(biāo)準(zhǔn)的Pocket PC上,但是在實(shí)際的高爾夫球場(chǎng)上Pocket PC Phone Edition的內(nèi)建連接會(huì)有很大的好處。Golf Anyplace本質(zhì)上是一個(gè)數(shù)字得分卡,它能跟蹤你的得分和其它玩家的結(jié)果。其想法是每個(gè)打高爾夫的人使用Pocket PC Phone Edition跟蹤得分。因?yàn)槊總(gè)人都能把自己的分?jǐn)?shù)發(fā)送到遠(yuǎn)程服務(wù)器并下載他人的得分,所以能夠經(jīng)?吹奖荣惖倪^(guò)程。

下面是一些界面:

主窗體用于輸入自己的得分。它也可以用于查看其它玩家的得分。



     圖1.輸入自己的得分

Synchronize命令把本地?cái)?shù)據(jù)推入遠(yuǎn)程服務(wù)器,接著下載所有的遠(yuǎn)程得分?jǐn)?shù)據(jù)。



   圖2.同步分?jǐn)?shù)

你可以使用View窗體查看細(xì)節(jié)和整個(gè)比賽的情況。



   圖3.保持跟蹤其它的玩家

示例應(yīng)用程序:代碼

下面我們看一看代碼。在代碼的某些位置,你可能注意到我使用不同的方法解決同一個(gè)問(wèn)題。這些例子包括我怎樣著手類(lèi)的初始化,使用DataReader還是DataSet,填充ListView,是否使用SQL Server CE包裝等等。我希望這能對(duì)你有些幫助,某個(gè)方案在某種情況下工作得很好,在其它的環(huán)境中可能另一個(gè)方案更好。

啟動(dòng)

Golf Anyplace的啟動(dòng)對(duì)象是GolfAnyplace.RDAGolf。下面是當(dāng)啟動(dòng)應(yīng)用程序時(shí)構(gòu)造邏輯執(zhí)行的操作:

public RDAGolf() { InitializeComponent(); //確定存在數(shù)據(jù)庫(kù) SQLServerCEWrapper SSCEWrapper = new SQLServerCEWrapper(); bool NewDatabase = SSCEWrapper.CreateDatabase(); //如果新數(shù)據(jù)庫(kù)被建立了,執(zhí)行第一次下載數(shù)據(jù) if(NewDatabase==true) { //調(diào)用Pull并且不保存本地?cái)?shù)據(jù) SSCEWrapper.Pull(false); } //填充組合框 for(int iCounter=1; iCounter < 19; iCounter++) this.cmbHole.Items.Add(iCounter.ToString()); //把第一洞設(shè)置為默認(rèn)的 this.cmbHole.SelectedIndex = 0; }

你可以看到我實(shí)現(xiàn)了SQL Server CE包裝。我是通過(guò)把與數(shù)據(jù)庫(kù)相關(guān)的代碼寫(xiě)到同一個(gè)位置實(shí)現(xiàn)的。該包裝幫助我管理并與本地?cái)?shù)據(jù)庫(kù)一起工作,以及遠(yuǎn)程數(shù)據(jù)庫(kù)同步。下面是該包裝的前面幾行代碼:

using System; using System.Data; using System.Windows.Forms; using System.Collections; using System.Data.Common; using System.Data.SqlServerCe; using System.Data.SqlClient; namespace GolfAnyplace { public class SQLServerCEWrapper { public string InternetServer = "http://servername/directory/sscesa20.dll"; public string InternetUser ="DOMAIN\\user"; public string InternetPassword = "password"; public string RemoteConnection = "Provider=sqloledb;  Data Source=MySQLServer;Initial Catalog=GolfAnyplace;User Id=user;Password=password"; public string LocalDatabase = "\\My Documents\\ga.sdf"; public string LocalConnection = "Provider=Microsoft.SQLSERVER.OLEDB.CE.2.0;  Data Source=\\My Documents\\ga.sdf"; public string LocalTableName = "Result"; public string RemoteTableName = "Result";

我使RDA屬性共享的唯一原因是當(dāng)我在應(yīng)用程序的其它部分演示DataReader時(shí)需要它。實(shí)際上RDA屬性應(yīng)該是私有的,這樣就能與應(yīng)用程序的其它部分保持一致,并且可以自該包裝傳遞一個(gè)DataSet。

下面是該包裝的CreateDatabase和Pull方法:

public bool CreateDatabase() { // 確定數(shù)據(jù)庫(kù)存在,如果新數(shù)據(jù)庫(kù)被建立了就返回true,否則返回false。 if(System.IO.File.Exists(LocalDatabase) == false) { System.Data.SqlServerCe.Engine SQLCEEngine =   new System.Data.SqlServerCe.Engine(LocalConnection); SQLCEEngine.CreateDatabase(); return true; } else { return false; }

如果數(shù)據(jù)庫(kù)不存在,CreateDatabase方法就建立一個(gè)新數(shù)據(jù)庫(kù)。

public void Pull(bool KeepLocalData) { //把表下載到本地?cái)?shù)據(jù)庫(kù) string SQL; SqlCeConnection cn; SqlCeCommand cmd; RemoteDataAccess RDA = null; //建立和初始化新的RDA對(duì)象 RDA = new RemoteDataAccess(InternetServer,   InternetUser, InternetPassword, LocalConnection); //保持本地?cái)?shù)據(jù)碼?如果保持,首先上載它。 if(KeepLocalData) { RDA.Push(LocalTableName, RemoteConnection,    RdaBatchOption.BatchingOff); } //在下載前,必須刪除本地表 //打開(kāi)本地?cái)?shù)據(jù)庫(kù)連接 cn = new SqlCeConnection(LocalConnection); cn.Open(); //刪除本地表 SQL = "DROP TABLE " + LocalTableName; cmd = new SqlCeCommand(SQL, cn); //如果表不存在,會(huì)出現(xiàn)錯(cuò)誤 try { cmd.ExecuteNonQuery(); } catch{ } //關(guān)閉連接 cn.Dispose(); //最后下載遠(yuǎn)程表 SQL = "SELECT PlayerName, Hole, Result FROM " + RemoteTableName; RDA.Pull(LocalTableName, SQL, RemoteConnection, RdaTrackOption.TrackingOnWithIndexes, "RDAErrors"); //清除 RDA.Dispose(); }

注意我給Pull方法傳遞了一個(gè)布爾變量。如果本地已經(jīng)存在某個(gè)表,那么把遠(yuǎn)程服務(wù)器表的內(nèi)容傳遞到本地SQL Server CE表是無(wú)法做到的,因此傳遞前必須執(zhí)行一個(gè)DROP TABLE語(yǔ)句。該布爾變量用于控制第一次向服務(wù)器傳遞數(shù)據(jù)時(shí)是否保持本地?cái)?shù)據(jù)。你也可以看到Push方法的新的批處理參數(shù)。在本例中我使用了BatchingOff,這意味著我不認(rèn)為提交到服務(wù)器的行是批處理的,而那意味著要么全部完成要么什么也不作。另一個(gè)可以的選項(xiàng)是BatchingOn。你可以看到,我使用了SqlCeCommand來(lái)執(zhí)行DROP TABLE。

我想強(qiáng)調(diào)我在Pull語(yǔ)句中指定了字段的名稱(chēng)。記住通常要指定字段名稱(chēng),無(wú)論是在Pull語(yǔ)句中還是在正常的SELECT語(yǔ)句中。通過(guò)這種做法,你可以確保只涉及絕對(duì)需要的數(shù)據(jù)。另外,你還可以輕易地防止與查找不能下載的字段(例如rowguidcol、int identity和timestamp字段)相關(guān)的問(wèn)題。

服務(wù)器上有什么

在我們進(jìn)一步深入該P(yáng)ocket PC應(yīng)用程序源代碼前,我將解釋以下服務(wù)器端有什么內(nèi)容。遠(yuǎn)程服務(wù)器是SQL Server 2000,運(yùn)行著一個(gè)叫GolfAnyplace的數(shù)據(jù)庫(kù),該數(shù)據(jù)庫(kù)只有一個(gè)表Result。下面是該表的定義:

CREATE TABLE [dbo].[Result] ( [PlayerName] [nvarchar] (50) NOT NULL , [Hole] [smallint] NOT NULL , [Result] [smallint] NOT NULL ) ON [PRIMARY] GO ALTER TABLE [dbo].[Result] W99vH NOCHECK ADD CONSTRAINT [PK_Result] PRIMARY KEY CLUSTERED ( [PlayerName], [Hole], [Result] ) ON [PRIMARY]

我使用幫助向?qū)渲昧薙QL Server CE 2.0服務(wù)器代理。

填充ListView

GolfAnyplace項(xiàng)目演示了兩種填充listview的方法:使用DataSet和使用DataReader。因?yàn)槲疫x擇在SQL Server CE周?chē)鷮?shí)現(xiàn)包裝,所以最好使用DataSet。DataSet可以不連接,并且在不連接狀態(tài)下在類(lèi)之間傳遞。因?yàn)镈ataReader要求打開(kāi)到數(shù)據(jù)庫(kù)的連接,因此盡可能在打開(kāi)和關(guān)閉連接附近使用它。第一個(gè)例子代碼顯示了怎樣通過(guò)在DataSet循環(huán)填充一個(gè)listview。

下面是填充listview的代碼:

private void UpdateResultListView() { //更新listview DataSet ds = null; SQLServerCEWrapper SSCEWrapper = new SQLServerCEWrapper(); //得到DataSet ds = SSCEWrapper.GetPlayerResult(txtPlayerName.Text); //清除listview lvwResult.Items.Clear(); //在DataSet中循環(huán) foreach (DataRow dr in ds.Tables[0].Rows) { ListViewItem lviItem = new ListViewItem(dr["Hole"].ToString()); lviItem.SubItems.Add(dr["Result"].ToString()); lvwResult.Items.Add(lviItem); } }

下面是在包裝類(lèi)中與之對(duì)應(yīng)的代碼:

public DataSet GetPlayerResult(string PlayerName) { //從本地?cái)?shù)據(jù)庫(kù)得到數(shù)據(jù),作為DataSet返回 string SQL; SqlCeConnection cn; SqlCeCommand cmd; DataSet ds; SqlCeDataAdapter da; //初始化新的連接 cn = new SqlCeConnection(LocalConnection); //打開(kāi)連接 cn.Open(); //建立SQL表達(dá)式 SQL = "SELECT PlayerName, Hole, Result FROM " + LocalTableName +   " WHERE PlayerName = '" + PlayerName + "' ORDER BY Hole"; //初始化新的命令 cmd = new SqlCeCommand(SQL,cn); //初始化新的DataSet ds = new DataSet(); da = new SqlCeDataAdapter(SQL,cn); //使用數(shù)據(jù)適配器用數(shù)據(jù)填充DataSet da.Fill(ds, LocalTableName); //清除 cn.Dispose(); cmd.Dispose(true); // Return the DataSet return ds; }



下面的示例代碼演示了怎樣使用DataReader作相同的事情:

private void UpdateDetailedResultListView() { //使用DataReader更新詳細(xì)結(jié)果 SQLServerCEWrapper SSCEWrapper = new SQLServerCEWrapper(); string SQL; //詳細(xì)listview的SQL表達(dá)式 SQL = "SELECT PlayerName, Hole, Result FROM Result ORDER BY PlayerName, Hole"; SqlCeConnection cn = new SqlCeConnection(); cn.ConnectionString = SSCEWrapper.LocalConnection; //打開(kāi)連接 cn.Open(); //初始化命令并執(zhí)行閱讀程序 SqlCeCommand cmd = new SqlCeCommand(SQL,cn); SqlCeDataReader dtr = cmd.ExecuteReader(); //清除listview this.lvwResultDetailed.Items.Clear(); //開(kāi)始讀取 while (dtr.Read()) { ListViewItem lviItem = new ListViewItem(dtr["PlayerName"].ToString()); lviItem.SubItems.Add(dtr["Hole"].ToString()); lviItem.SubItems.Add(dtr["Result"].ToString()); lvwResultDetailed.Items.Add(lviItem); } //關(guān)閉DataReader dtr.Close(); //清除 cmd.Dispose(true); cn.Dispose(); }

最后,注意我在項(xiàng)目中使用了一個(gè)Options窗體(圖4),需要一些附加的代碼來(lái)支持它。

   圖4. SQL Server CE Options窗體

技巧

   下面是一些我希望與你共享的技巧:

· 很好地設(shè)計(jì)界面。

· 不能讓SQL Server CE代碼沒(méi)有Try、Catch和Finally等適當(dāng)?shù)腻e(cuò)誤處理程序。

· 如果你沒(méi)有Pocket PC用于開(kāi)發(fā)或者Pocket PC沒(méi)有網(wǎng)卡,可以使用Pocket模擬器。

· 信任SQL Server CE連接向?qū),至少開(kāi)始時(shí)信任。

· 當(dāng)上載和下載數(shù)據(jù)時(shí)使用服務(wù)器端SQL Profiler或者合并復(fù)制監(jiān)視發(fā)生的情況。如果沒(méi)有其它情況發(fā)生,你可以看到什么時(shí)候或是否發(fā)生了某事。

· 學(xué)習(xí)在Internet上在何處查找編碼問(wèn)題的答案。

結(jié)論

   Visual Studio .NET、Visual C#或Visual Basic .NET與SQL Server CE 2.0一起工作時(shí)匹配得很好。希望你能從本文我提供的方法中學(xué)習(xí)到一些好的想法。  

關(guān)鍵詞:C#

贊助商鏈接: