2025年11月10日 星期一

利用Web Service存取SQL Server資料庫


 
作者:楊先民  

精誠資訊/恆逸教育訓練中心資深講師

※網路引用請註明完整出處


我們都知道,如果你寫應用程式需要連接到 SQL Server,你必須準備SQL Native Client或是OLE DB For SQL Server,而且大多數都會希望你的前端應用程式是使用 .NET 開發,然而本文的主題是「如果你的前端並非 .NET的程式」,那你應該如何連接到SQL Server處理資料呢?

Web Service當成資料處理的中繼層

本文的情境前端是使用 iphone的程式開發Objective-C,並且使用原生的方式來呼叫已經寫好的Web Service程式,也就是我並不打算使用 Jason的方式來存取Web Service,自己控制比較有實作的感覺啊,而且也順便記錄一下這種複雜的存取控制方式,頓時之間你會發現使用 .NET來呼叫Web Service是多麼的簡單(只需設定一個 proxy物件就好,但 iphone開發沒那麼容易)!


情境是這樣的,我在 iphone上撰寫了一支車輛油耗管理的程式,詳細請參考下列網站:

https://itunes.apple.com/tw/app/you-hao-wei-xiupro/id491654074?mt=8


這支程式不但管理使用者的車輛油耗以及保養維修記錄,還可以上傳使用者的油耗資訊和其他同車廠同型號的使用者比較,所以我設計了一個如下的介面:


當使用者先登錄車籍資料,並且按下「上傳油耗資訊」,使用者就可以將自身的油耗資訊上傳,並且在統計頁面上可以知道自己的油耗,以及全部車種的油耗排名,如下圖:


其實,單純就以資料庫的角度來看油耗資訊上傳非常容易,只不過就是寫個 update指令,將油耗資訊寫入資料庫罷了,不過別忘記我們用的是 iphone的程式,並不是 .NET的程式,也許有 3-party的廠商提供函式庫給 iphone呼叫 SQL Server,但最通用的方式還是自己透過 Web Service最實在了,主因是 Web Service是一個跨平台的程式,透過 tcp/ip xml做為媒界,任何前端(並非只有 Windows前端)可以呼叫 Web Service,而你只需處理回傳回來的 xml資料即可(xml現今用來廣泛的做為資料交換之用)。


所以我們必須做幾件事情:

1.   SQL Server中撰寫更新油耗的 stored procedure,用來做資料處理。

2.  利用 asp .net Web Service,呼叫1所撰寫的 stored procedure

3.   iphone中利用程式呼叫 Web Service,更新油耗資訊。

 

我們就一步步來:

首先,1的部分就是寫一個更新油耗資訊的 stored procedure,如下:

Create proc [dbo].[COil_UpdateOil]

@deviceid int,@totalwalkkm decimal(10,2),@totallitre decimal(10,2)

as

IF @totallitre=0

set @totallitre=1

update Oil set walkkm = @totalwalkkm , Litre = @totallitre where DeviceID = @deviceid

select @@ROWCOUNT

 

其中就是輸入三個參數,一個是裝置id,一個是總行駛公里,一個是總加油公升,如此而已。

再來,利用 asp .net  Web Service,呼叫寫好的 stored procedure,程式如下:

    <WebMethod()> _

Public Function updateOil(ByVal deviceid As String, ByVal totalwork As String, ByVal totallitre As String) As Integer

        '

Dim returnValue As Integer

returnValue = 0

 

Dim myConnection As SqlConnection = New SqlConnection(System.Configuration.ConfigurationManager.AppSettings("uploadoil"))

Dim myCommand As SqlCommand

myCommand = New SqlCommand("COil_UpdateOil", myConnection)

myCommand.CommandType = CommandType.StoredProcedure

 

Dim parameterCustomerid As SqlParameter = New SqlParameter("@deviceid", SqlDbType.Int, 4)

parameterCustomerid.Value = deviceid

Dim parameterdefaultcar As SqlParameter = New SqlParameter("@totalwalkkm", SqlDbType.Decimal, 9)

parameterdefaultcar.Value = Convert.ToDecimal(totalwork)

 

Dim parameterTotallitre As SqlParameter = New SqlParameter("@totallitre", SqlDbType.Decimal, 9)

parameterTotallitre.Value = Convert.ToDecimal(totallitre)

myCommand.Parameters.Add(parameterCustomerid)

myCommand.Parameters.Add(parameterdefaultcar)

myCommand.Parameters.Add(parameterTotallitre)

' Execute the command

Try

           myConnection.Open()

            returnValue = myCommand.ExecuteScalar

            myConnection.Close()

                  Catch ex As Exception

                 myConnection.Close()

            returnValue = 0
End Try

        Return returnValue
End Function

 

寫好的 Web Service,可以在網站測試一下,初始畫面應該是如下:


而我們還要注意下方的 SOAP的呼叫方式,這個將是等會我們要用 iphone 呼叫的程式:


目前我依然使用 SOAP 1.1的呼叫方式,你有興趣可以使用再更下方的 SOAP 1.2

如果你原先寫的是 .NET程式,你可能根本不會想看這張圖,因為 .NET只要把 Web Service的網址參考進來並且指定一個 proxy物件就可以直接呼叫了,換言之這部分的動作被 .NET給包起來了,如果你在 iphone中是使用 Jason的話,也大概是這麼包(不過絕對沒有 .NET這麼容易就是了)。

不過在 iphone中你要是撰寫的是原生的 Web Service,就要把上圖的那個呼叫與回傳好好研究一下,因為我們必須自己呼叫 SOAP Action

我們對照瀏覽器上的 SOAP,將我們自已的 SOAP Action組裝起來,下方的第一張圖是瀏覽器上的Envelope(信封,將 SOAP Action用信封包起來丟出去的意思),第二張則是用 objective-c stringformat SOAP action組合成訊息字串 soapMessage




是的,在 iphone中,你就是要利用 objective-c將這段 SOAP Action傳送到Web Service端,然後輸入所需要的參數(以本例而言,是 deviceidtotalwork以及 totallitre這三個參數)。

接下來,設定NSMutableURLRequest,將我們的 Web Service位址,也就是 http://xxx.xxx.xxx.xxx/upload.asmx指定,並且包上 HeaderField傳送出去,也就是在瀏覽器中,原本 SOAP Action的上面,還有一段資訊,如下:



是的,就是這段資訊,要一併隨著我們的 SOAP Action的信封傳送到 Web Service中。所以尚需要加上這一段程式碼:


其中有一行程式是 msgLength這個變數,主要是 HeaderField中需要 SOAP Action Content-Length,所以必須把剛才前面所定義的 soapMessage的長度輸入進來,然後就可以開始處理Web Service 所回傳的資訊了。


(當然,這裡不要忘記,由於我們設定 connection delegate屬性為 self,表示 xml Web Service這段部分的處理是由我們自行來完成,故不要忘記在h檔定義的地方加上NSXMLParserDelegate,如下圖:



由於要自己處理Web Service的回傳(如果此Web Service沒有回傳的話,只需注意網路有沒有連線成功即可,後續不用處理,但由於本例有回傳結果,所以我們必須知道回傳的格式),所以我們必須知道Web Service會回傳什麼資訊回來,因此我們先在Web Service上測試我們的值以及取得回傳結果的 xml資料,如下兩張圖:



回傳結果如下:



還記得我們的 stored procedure嗎?當資料修改完,回傳修改數量,以上圖為例,回傳值為1表示有資料被修改,那麼就代表成功了。我們在 iphone中要寫的程式主要是取回1這個數值,只要它是大於零,表示資料有成功修改到,本上傳油耗的程式即大功告成!


至此,我們需要設定剛才的 theConnection,要在它接收到資料時,將回傳的結果存入事先定義好的陣列中,所以我們必須在didReceiveResponse中清空陣列以及在didReceiveData中將取出的值填入陣列中,程式碼如下:


在連線結束載入時,利用 NSXMLParser開始剖析回傳回來的 XML資料,如下圖:



而可以注意到程式中的 xmlParser setDelegate設定為 self,所以代表你要自行撰寫取出 XML時的剖析內容,因此我們要自行控制didStartElementfoundCharacters以及didEndElement這三個事件。

 

在此稍微說明一下,XML是個階層式的資料,所以資料很有可能長的如下:

<Employee>

        <id>E01</id>

</Employee>

 

所謂的 didStartElement,代表發現 <Employee>這個 Element,但我們如果要的是<id>這個 element中的 E01資料,則就得請 parser繼續往下找。

 

foundCharacters則是代表 E01這個字元被找到,我們這裡必需寫程式把它存起來,由於 XML unicode以及是文字資料型別,所以要用文字字串或是陣列儲存起來。

 

最後的 didEndElement則是 </Employee>,也就是 XML中的尾巴這部分,代表整個 XML的結束。

 

至於我們如何知道頭尾的 element是什麼呢?很簡單,在瀏覽器上看的 demo其實就給了我們資訊,如下圖:


是的,也就是我們在didStartElement需要判斷是否值為updateOilResult,如果是的話,就可以開始讀裡面的值,如下圖: 



在此程式中,我們設定 UpdateOil YES,主要是如果你在同一個程式中呼叫多支 Web Service,負責處理的 method是同一支,所以你要設定像 UpdateOil這樣的旗標,用來說明我已經找到某個 element了,並且把其他的旗標設定為 NOiphone中是用 YES NO而不是 true false)。


然後在 foundCharacters中撰寫程式判斷回傳的結果是否為1,如下圖:



由於 xml的文字都是 unicode字串,所以即便是它看起來像數字的01,還是要以字串的方式處理。

 

最後,在 didEndElement中結束這次 XML資料的剖析,如下圖:



至此,由 iphone呼叫Web Service的程式宣告完成,是不是感覺有點複雜呢?其實以前習慣了.NET開發,很多動作都包起來,但其實Web Service就是一個很簡單的概念,把SOAP Action用一個信封包起來,利用HTTP傳送到網路上的Web Service,而SOAP Action裡面已經把呼叫Web Service的動作定義好了,Web Service處理完之後再將xml回傳結果用一個信封回傳,而應用程式端看自己有沒有需要接收回傳值,再利用xmlparser負責處理即可,有部分程式是可以直接套用就可以使用的,各位可以參考看看,不用依賴Jason,畢竟寫原生的Web Service好處就是只要概念通,其他應用程式也大概是類似的道理。


0 意見:

張貼留言