在Pocket PC中使用Web Service連接數據庫
2024-07-21 02:23:22
供稿:網友
本文來源于網頁設計愛好者web開發社區http://www.html.org.cn收集整理,歡迎訪問。在pocket pc中使用web service連接數據庫
前言
微軟的移動開發者大會在六月的北京舉行了,國內的移動應用軟件雖然是剛剛起步,但是這個前景寬廣的領域已經受到越來越多軟件廠商的關注了。移動設備上的商業應用盡管剛剛起步,但已經顯示出巨大的發展潛力。
在微軟移動開發挑戰賽中,我的作品《饕餮元年無線餐飲管理系統》獲得了商業應用的三等獎。為了實現pocket pc與后臺數據庫服務器的連接,我的作品中使用了.net compactframework和web service技術,我將核心部分的實現原理拿出來與大家分享。在本文中沒有涉及《饕餮元年》的代碼,我用另外一個程序“sql查詢分析器”作為演示的范例。
在嵌入式數據庫領域,世界各大數據庫廠商都提供了自己的移動解決方案,比如微軟的sql server ce、sybase的ianywhere、ibm的db2 everyplace等。雖然各家廠商都提供了數據同步的解決方案,而且實現原理大同小異,但是,每種數據同步方案都針對自己的數據庫,不能兼容其他的數據庫產品。
那么有沒有什么辦法,可以通過一種機制來訪問多種數據庫呢?微軟的mobile 2003已經完全支持.net compact framework開發,而.net對web service提供了很好的支持,開發者可以很容易的開發web service的服務程序和客戶端應用。
本文討論的是在windows mobile2003平臺上通過web service來訪問多種數據庫的解決方案。這肯定不會是移動設備訪問多種遠程數據庫的最優解,但我希望這是利用現有技術實現的比較優秀的解決方案。
系統需求:visual studio .net 2003
pocket pc 2003 模擬器
iis 5.0
第一個web service
目前web service受到了廣泛的關注,主流的開發工具都為web service開發提供了很好的支持。我們在這里使用visual studio . net 2003來做一個簡單的web service實例,讓不了解web service開發的程序員更好的進入后續話題的討論。
我們首先來創建一個asp.net web service工程:打開file菜單,選擇new,然后是project。在new project對話框中,project types選擇“visual c# projects”,templates中選擇“asp.net web service”。location中選擇你的web service的位置和名稱,由于我的機器上配置了iis服務,所以我將web service直接部署在本機上。請見圖一。
asp.net web service工程和asp.net十分相似,只是頁面文件的擴展名為asmx,而不是aspx。接下來,我們要為web service 創建一個webmethod。打開service1.asmx.cs文件,在class service1中添加兩個webmethod:yourname和welcom,代碼如下(粗體字為添加的代碼):
namespace webservice1
{
public class service1 : system.web.services.webservice
{
……
[webmethod]
public string yourname()
{
return "my name is wolf!我就是老狼!";
}
[webmethod]
public string welcom(string yourname)
{
string str = yourname+",歡迎使用wolf的web service";
return str;
}
}
}
這里需要提醒大家注意的是,每個webmethod都必須是public的,而且需要在函數前
加上[webmethod]的聲明。yourname函數返回一個包含英文和中文的string對象,主要是想測試一下web service的字符串能否在支持unicode的windowsce平臺上正?,F實。welcom函數增加了一個參數,目的也是為了測試web service和windowsce平臺間是否存在字符串不能正常顯示的問題。
然后我們選擇debug菜單中的start命令(或者按f5)運行這個web service,出現如下界面:(見圖二)
可以看到,我們剛才編寫的兩個函數的名稱被顯示了出來。大家可以點擊函數名稱,來查看webmethod的soap和http post等信息,還可以直接點擊invoke按鈕,查看webmethod返回的結果是否正確。
如果您的web service工作一切正常,接下來,我們創建一個smart device應用程序,在pocket pc 2003環境下調用我們的web service。如果您對如何在visual studio .net 2003下如何創建smart device應用程序還不是很了解,希望您參考下面的說明。
打開一個新的vs.net的ide環境,打開file菜單,選擇new,然后是project。在new project對話框中,project types選擇“visual c# projects”,templates中選擇“smart device application”,然后點擊ok。
在smart device application wizard對話框中,有兩個列表框。上邊一個列表框選擇程序的目標平臺,根據你開發使用機器上所安裝的sdk的不同可以顯示出不同的選項。其中pocket pc中包括pocket pc 2002和pocket pc 2003;windows ce中包括其他支持.net compact framework的windowsce平臺。如果您安裝了smartphone的sdk,這里也會顯示出smartphone的選項。
下面的對話框選擇的是所創建的應用程序類型,包括windows應用程序、類庫、non-graphical應用程序和空工程。這里的選項根據您選擇平臺的不同也會略有差異。還有一點需要說明的是,如果您安裝了mobile internet toolkit,您也可以通過這個向導來創建目標平臺為mobile設備的asp.net web程序。
我們在這里選擇“pocket pc”和“windows application”,點擊ok。我們就可以開始移動開發之旅了。
我們先來看一下ide環境的全景:其實和winform程序的開發環境差不多吧。但請大家注意上圖中的下拉菜單,做過windowsce開發的朋友們一定很熟悉吧。對了,這就是選擇應用程序輸出設備的菜單。我們在這里選擇“chs pocket pc 2003 – sdk emulator”,我們可以選擇下拉菜單旁邊的connect to device按鈕來啟動模擬器,也可以等到程序運行時再啟動模擬器。
請注意:visual studio .net 2003會默認安裝pocket pc 2002的sdk,您需要另外安裝pocket pc 2003 sdk和中文映像才能夠看到這一項。不過您在pocket pc 2002的模擬器下調試下面的應用程序也是沒有問題的。
下面,我們來添加web references,選擇project菜單下的“add web references”,或者在solution explorer中右擊web references,在彈出菜單中選擇“add web references”。我們就會看到下面的對話框:
在url里要填寫web service的url,還記得我們運行web service時ie地址欄中的url嗎?http://localhost/webservice1/service1.asmx。對嗎?如果是winform程序,這樣寫是正確的,但在smart device應用程序中,這樣寫就是錯的。請大家注意,下面討論的話題十分重要:windows ce設備的模擬器盡管運行在你的pc上,但它實際上是作為一臺遠程設備連接到你的pc上的。所以,如果寫“localhost”或者“127.0.0.1”,那么程序訪問的將是模擬器,而不是你用來開發的pc機。所以,在這里你必須填寫你的pc在網絡中的實際機器名或者ip地址。所以這里應該填寫http://yourname/webservice1/service1.asmx(yourname表示你實際的機器名)。這里如果您填寫的是localhost,開發環境也將為你找到web references,但在調用web service時將出現異常。好了,填寫好url,按go按鈕,如果url正確,則會顯示上圖的信息,然后點擊add references按鈕。這樣我們就可以在項目中使用這個web service了。
我們打開界面編輯器,添加兩個button和一個textbox,用來調用兩個webmethod。代碼如下:
private void button1_click(object sender, system.eventargs e)
{
yourname.service1 myservice = new yourname.service1();
label1.text = myservice.yourname();
messagebox.show(myservice.yourname());
}
private void button2_click(object sender, system.eventargs e)
{
yourname.service1 myservice = new yourname.service1();
messagebox.show(myservice.welcom(textbox1.text));
}
yourname是web service的名稱,相當于命名空間,所以我們必須創建一個yourname中的service1類的對象,然后調用service1中的方法。大家可以看到,在c#程序中調用web server的方法和創建本地類并調用其方法的過程是十分類似的。另外需要說明的是,由于網絡因素的影響,調用web service不一定成功,所以在商業軟件的開發中需要加入異常處理的代碼,這里為了程序的簡潔就省略了。程序運行結果如下:
sql查詢分析器
在了解了基本的web service的開發與調用之后,我們可以進入核心內容的討論了。web service是基于xml語言的,正是這種特性才使web service成為連接各種異構系統之間的橋梁。那么有沒有一種可以在跨越不同數據庫來傳遞數據的簡單方式呢?asp.net web service是支持返回dataset對象的,而dataset又能夠被各種數據表示控件所支持,比如datagrid等,dataset就是我們要找的橋梁。
在這一部分里,我將創建一個sql的查詢分析器,與其他查詢分析器不同的是,我可以在調用時再指定連接的數據庫類型,這樣就可以實現連接不同數據庫了。我們首先來創建sql查詢分析器的web service部分:
先來看一下sql service的uml圖。我們沿用了上面sql service中的service1類,并為它添加了五個webmethod,其名稱和功能介紹見下表:
名稱
功能
setdatabasetype
設置連接數據庫的類型
setdbconnectionstring
設置數據庫的連接字符串
create
創建數據庫操作對象
executenonquery
執行不返回結果的sql語句
executedataset
執行sql語句,并返回一個dataset
在web service的service1類中,我們首先來聲明三個變量用來分別存儲數據庫操作對象、數據庫連接字符串和數據庫類型。注意,我們將變量聲明為static,是為了讓客戶端執行不同的webmethod時,可以訪問到相同的數據庫操作對象和字符串對象。dboperater是數據庫操作類的基類,而等一下我們創建的是dboperater派生類的實例。
private static dboperater m_dboperater;
private static string m_databasetype;
private static string m_dbconnectionstring;
setdatabasetype和setdbconnectionstring兩個webmethod是為了設置數據庫類型和數據庫連接字符串而提供的方法。在這里需要特別提醒的是,將數據庫的連接字符串作為web service參數,以明文的方式在網絡上傳遞是一種十分危險的做法,這里只是為了示例程序的簡單才這樣實現的。強烈建議讀者在實現自己的web service時,用更好的方法來保護自己的數據庫連接字符串。
[webmethod]
public bool setdatabasetype(string databasetype)
{
if(databasetype == "")
return false;
m_databasetype = databasetype;
return true;
}
[webmethod]
public bool setdbconnectionstring(string dbstring)
{
if(dbstring == "")
return false;
m_dbconnectionstring = dbstring;
return true;
}
下面的create方法,用來根據m_databasetype來創建相應的dboperater派生類的對象。這里需要說明的是,設置數據庫類型(setdatabasetype)用的是string作為參數,而沒有使用enum類型或者其他類型,是為了再以后添加對新的數據庫支持時不必修改已有的客戶端源代碼。
[webmethod]
public bool create()
{
if(m_databasetype == "sql server")
{
m_dboperater = new dbsqlserveroperater(m_dbconnectionstring);
return true;
}
if(m_databasetype == "access")
{
m_dboperater = new dbaccessoperater(m_dbconnectionstring);
return true;
}
return false;
}
而剩下的兩個webmethod只是簡單的調用了dboperater派生類對象的相應方法,而實際的數據庫操作則是在dboperater派生類中具體實現的。這樣可以更好地實現數據庫操作與客戶端操作的分離。
[webmethod]
public bool executenonquery(string sql)
{
return m_dboperater.executenonquery(sql);
}
[webmethod]
public dataset executedataset(string sql)
{
return m_dboperater.executedataset(sql);
}
我們將對數據庫操作的具體方法封裝到了dboperater類中,并由dboperater類派生出了dbsqlserveroperater和dbaccessoperater類,分別用來進行對sql server數據庫和access數據庫的操作。dboperater類暴露了兩個虛方法:executenonquery()和executedataset(),由派生類負責重載實現。而派生類也各自暴露了自己的構造函數,用來獲取連接數據庫字符串。
下面是dboperater類的實現代碼,可以看到dboperater類中的兩個方法使用了virtual的關鍵字,聲明為虛函數。
// 數據庫操作基類
public class dboperater
{
public virtual bool executenonquery(string sql)
{
return true;
}
public virtual dataset executedataset(string sql)
{
return null;
}
}
在web service的體系結構中,真正進行數據庫操作的只有dboperater的派生類,我們下面看到的dbsqlserveroperater類,使用了system.data.sqlclient命名空間中的數據庫操作類,對sql server數據庫進行存儲操作。dbaccessoperater類對oledb數據進行操作,使用了system.data.oledb命名空間中的類。
// sql server 操作類
public class dbsqlserveroperater : dboperater
{
public dbsqlserveroperater(string dbconnectionstring)
{
m_sqlconnection = new sqlconnection(dbconnectionstring);
}
public override bool executenonquery(string sql)
{
if(sql == "")
return false;
m_sqlconnection.open();
sqlcommand command = m_sqlconnection.createcommand();
command.commandtext = sql;
command.executenonquery();
m_sqlconnection.close();
return true;
}
public override dataset executedataset(string sql)
{
if(sql == "")
return null;
m_sqlconnection.open();
sqldataadapter da = new sqldataadapter(sql,m_sqlconnection);
dataset ds = new dataset();
da.fill(ds);
m_sqlconnection.close();
return ds;
}
private sqlconnection m_sqlconnection;
}
// oledb 操作類
……
這里省略了與sql server操作類基本相同的access操作類代碼。關于c#中重載和繼承的實現方式和注意事項,請參閱相關書籍。
由于對數據庫的操作已經在web service端做了封裝,所以客戶端的代碼實現起來就相對簡單了。smartsqlclient包含兩個屬性頁,第一個屬性頁為setting,設置須查詢的數據庫類型和數據庫連接字符串,設置完畢后,點擊setting按鈕,程序會將數據庫類型和連接字符串保存起來,并自動切換到第二個屬性頁中。
第二個屬性頁query,包括一個textbox,用來書寫sql查詢語句,一個datagrid,用來顯示web service傳來的數據庫,兩個button分別執行返回dataset的sql查詢和不返回結果集的操作。
現在我們來看一下execdataset按鈕的響應函數,該函數首先創建一個web service訪問對象的實例,然后根據setting中的設置,設置數據庫類型和數據庫連接字符串,然后創建相應數據庫操作類的對象實例,并調用web service的executedataset方法,將一個有效的sql查詢語句傳遞給web service,web service會返回一個dataset對象,我們的客戶端程序會將dataset中的第一個表顯示到datagrid控件中。
private void execdsbtn_click(object sender, system.eventargs e)
{
wolf.service1 service = new sqlsmartclient.wolf.service1();
service.setdatabasetype(m_databasetype);
service.setdbconnectionstring(m_connectionstring);
service.create();
dataset ds = service.executedataset(textbox1.text);
datagrid1.datasource = ds.tables[0];
}
好了,到這里,我們已經實現了一個可以在pda端操作遠程sql server和access數據庫的sql查詢分析器了。隨著.net數據庫操作組件的日益豐富,您完全可以實現對oracle、db2等流行的關系型數據庫的支持,并在此基礎上,實現更多基于移動設備的應用程序。
與sql server ce交互
我們上面實現的sql查詢分析器的意義不僅在于可以在移動設備上方便地對不同類型的數據庫進行檢索,而且這種web service的方式還有更現實的意義。
眾所周知,在windows ce平臺上主流的數據庫是sql server ce,sql server ce與服務器交換數據的方式主要有兩種:remote data access(rda)和replication。這兩種數據同步的方式,不但需要搭建專門的iis環境,而且只能與sql server數據庫進行數據同步。這就大大增加了使用其他類型數據庫的系統集成商開發移動設備應用程序的難度,同時也不利于windows ce平臺被更廣泛的領域所接受。
下面的時間,我將在上面web service的基礎上,演示如何利用web service實現sql server ce與access之間的數據同步。當然,我的實現還無法達到rda可以將表結構同步復制的功能。
為了完成這部分的代碼,我們在sqlsmartclient中再添加一個屬性頁,命名為sqlserverce。最上面的textbox和createdb按鈕是為了創建sql server ce數據庫和數據庫中的表所準備的,稍后我們將介紹他們的代碼實現。而下面的fill按鈕完成的任務包括:將第二個textbox中的sql語句提交給web service,由web service負責提供包含相應數據的dataset。然后解析dataset,在組合成相應的sql語句,插入到sql server ce數據庫中。
我們先來介紹一下在.net compact framework中對sql server ce的操作。在.net cf中針對sql server ce的操作類全部在system.data.sqlserverce命名空間中,其中類的名稱與操作方式和.net framework中system.data.sqlclient類似。我們先來看一下,如何創建sql server ce數據庫:
// 創建sql server ce數據庫
private void createdbbtn_click(object sender, system.eventargs e)
{
if (file.exists ( dbnametextbox.text) )
file.delete ( dbnametextbox.text);
string connstring = "data source = "+ dbnametextbox.text;
sqlceengine engine = new sqlceengine (connstring);
engine.createdatabase();
// create table
sqlceconnection conn = new sqlceconnection(connstring);
conn.open();
sqlcecommand command = conn.createcommand();
command.commandtext = "create table human (id int primary key, name ntext,age int ,address ntext)";
command.executenonquery();
conn.close();
messagebox.show("數據庫創建成功");
}
在這段代碼中,我們首先檢查sdf文件是否存在,如果存在則刪除已存在的數據庫文件(擴展名sdf)。然后根據給出的sdf文件名構造數據庫連接字符串,創建sqlceengine對象,并調用sqlceengine的createdatabase方法創建數據庫。最后一部分的代碼使用sqlceconnection和sqlcecommand創建了名為human的表。
好了,創建好sql server ce數據庫了,該將web service上的數據取下來了。我們來看fill按鈕的實現代碼。
第一步是創建web service的訪問對象,向web service請求dataset數據庫。不多做解釋了。
private void filldsbtn_click(object sender, system.eventargs e)
{
// get web service dataset
wolf.service1 service = new sqlsmartclient.wolf.service1();
service.setdatabasetype(m_databasetype);
service.setdbconnectionstring(m_connectionstring);
service.create();
dataset ds = service.executedataset(sqltextbox.text);
第二步,我們根據上面創建好的sql server ce數據庫,組成數據庫連接字符串,并創建sqlceconnection和sqlcecommand對象,看起來很眼熟,不是嗎?
// get sql server ce database dataset
string connstring = "data source = "+ dbnametextbox.text;
sqlceconnection conn = new sqlceconnection(connstring);
conn.open();
sqlcecommand command = conn.createcommand();
第三步,我們從dataset的第一個datatable對象中得到datarow數組,如果datarow的數量不小于1,則表示datarow數組中有數據。我們通過c#語言的foreach關鍵字來循環得到每一個字段的值,然后將其存儲到相應的string對象中去,再由這些string對象組成sql語句。最后用sqlcecommand對象來執行這些sql語句。
// build sql command
string sql,id,name,age,address;
datarow[] currrows = ds.tables[0].select(null, null, dataviewrowstate.currentrows);
if( currrows.length < 1)
return;
foreach(datarow myrow in currrows)
{
id = myrow["id"].tostring();
name = myrow["name"].tostring();
age = myrow["age"].tostring();
address = myrow["address"].tostring();
sql = "insert into human (id,name,age,address) values ("+id+",'"+name+"',"+age+",'"+address+"')";
// execute sql command
command.commandtext = sql;
command.executenonquery();
}
conn.close();
messagebox.show("轉換完成");
}
最后關閉sqlceconnection的連接,整個數據同步過程完成。我們通過sql server ce自帶的查詢分析器,查看一下同步過來的數據,和access表中的完全相同。當然,我們也可以將sql server ce中的數據同步到遠程的數據庫中去,這里就不再重復實現了。