[Delphi]dbGO ( ADO ) 連線失敗 ?
在 SQL Server 2005 加入了 SQL Server Native Client,原本並沒有花太多的時間去研究,因為所開發的產品還是使用 ADO + OLE DB Provider 的運作,所以也沒有特別去注意。但在 SQL Server 2012 正式出來之後,我們在產品上也做些調整來配合 SQL Server 2012 新的欄位型態,為了避免遇到不相容或者是以後維護上的問題,因此開始測試使用 SQL Server Native Client。
首先我們先利用 udl (Universal Data Link) 檔來進行測試,先在桌面上建立一個空的檔案,將其檔名更改為 xxx.udl 之後,再 Double Click 該檔案後,就可以出現連線設定作業畫面了,我們可以選擇 SQL Server NativeClient 11.0 ( 如果您的環境是安裝 SQL Server 2008 的話,則可能會出現的是 SQL Server Native Client 10.0 )
當我們設定好主機,帳號和密碼,也選擇了預設資料庫之後,我們可以選擇 [測試連接] 試看看是否可以順利執行,如果一切都沒有問題的話,則會出現測試連接成功的訊息 ( Native Client 10.0 則會出現 Test connection succeeded. ),按下確定之後,我們就可以用文書處理器開啟這個 xxx.udl 的設定檔,將裡面的連接字串拿到程式裡面去使用了。
1: [oledb]
2: ; Everything after this line is an OLE DB initstring
3: Provider=SQLNCLI11.1;Integrated Security="";Persist Security Info=False;User ID=sa;Initial Catalog=TESTDB;Data Source=(local);Initial File Name="";Server SPN=""
眼尖的人應該可以看出來,在這個連接字串裡面並沒有把 Password 的屬性給存起來。因此當我們如果再次 Double Click 該檔案的時候,則會出現 [使用者 'sa' 的登入失敗。] 的錯誤訊息,所以當要把這個連接字串直接設定到程式內來使用的時候,要記得多加入 Password = "xxxxxx" 的設定在這個連接字串裡面。
原本以為改為 SQL Server Native Client 蠻簡單的,看起來只是把 Provider 給換成 SQLNCLI11 ( 或者是 SQLNCLI10 ),其他也就多了一些參數,但似乎也沒有運用到,想說這真是一塊餅乾。可是每次當這樣想的時候,小惡魔就跑出來了…. ,因為我是使用 Delphi 進行開發,雖然也是一樣可以使用 ADO,但他是類似 Adapter 的架構去將 ADO 給封裝起來,因此就會有一點小落差,在 Delphi 的 TADOConnection 元件內,如果我們使用上述的 ConnectionString,並且把 Password 的屬性給加入的話,一般情況下是可以使用的,但有些時候我們不希望程式和資料庫之間一直保持連線,因此將 TADOConnection 內的 KeepConnection 的屬性將預設的 True 改為 False 的狀況下,則很有可能當你在使用 ADODataset 或者是相關元件在下指令的時候,此時則可能會出現[使用者 'sa' 的登入失敗。] 的錯誤訊息。
1: procedure TADOConnection.DoConnect;
2: begin
3: ConnectionObject.Open(ConnectionObject.ConnectionString, FUserID, FPassword,
4: ConnectOptionValues[FConnectOptions]);
5: if FDefaultDatabase <> '' then
6: ConnectionObject.DefaultDatabase := FDefaultDatabase;
7: end;
如果有興趣的人可以看一下 ADODB.pas 內的 TADOConnection 的 Source,這裡有一個很特殊的地方,由於 Delphi 封裝 ADO 物件採用 Adapter 的方式,因此雖然在 Delphi 內稱為 TADOConnection 的物件,但實際上還是 ADO 的 Connection 物件 ( 也就是程式內的 ConnectionObject )。眼尖的人應該可以看出來,當每次在連線的時候,Delphi 並不是自己本身 TADOConnection 內所記錄下來的 ConnectionString 去連線,而是使用設定在 ConnectionObject 上的 ConnectionString 的屬性值去連線。
但這個跟連線失敗有甚麼關係呢 ? 再以往使用 OLE DB 的時候都沒有問題啊 , 如果各位有興趣,可以參考一開始所介紹 UDL 的設定方式,設定兩個 udl 來做比較,一個是使用 OLE DB Provider,另外一個就是剛剛的 SQL Server Native Driver,來看看 ConnectionString 是否有不一樣嗎 ? 你會發現在使用 OLE DB 的時候,Persist Security Info 的屬性預設是 True,但使用 SQL Server Native Driver 的時候,預設值是 False。也就是因為這個屬性,如果我們設定 ADO 的 ConnectionString 內有包含 Password 的屬性時,當 Connection 連通之後,你再去取得 Connection 上的 ConnectionString 的屬性質的時候,就會發現這個時候設定 Password 的部分消失了。
以往我們使用 Delphi 的時候,因為預設是 True,也沒有造成甚麼使用上的問題,多半也不會特別注意這個 Persist Security Info 的屬性。但是採用 SQL Server Native Driver 的時候,因為預設值是 False,所以當我們習慣把密碼直接設定在 ConnectionString 的時候,就會發生當你把 Connection 開啟 –> 關閉 –> 再開啟的時候,會發生無法連線的狀況。
當我們知道發生原因之後,那接下來我們該如何修改呢 , 個人認為有幾種方式可以考慮來使用:
- 利用 TADOConnection.Open 這個 method 開啟連線的時候,傳入帳號和密碼。
- 利用 TADOConnection.WillConnect 的事件來傳入帳號和密碼。
- 每次要連線的時候都重新賦予新的 ConnectionString,且必須有包含 Password 的設定值
- 使用 Windows 認證的方式來連線
參考資料 :
- MSDN http://technet.microsoft.com/zh-tw/library/ms130978.aspx
- 保哥的技術交流中心 http://blog.miniasp.com/post/2009/07/17/SQL-Connection-String-Persist-Security-Info-Explained.aspx