---
tags: 110-2視窗程式設計
---
# 實作:連線資料庫
雖然資料庫的概念可能對多數一年級同學來說,是比較進階的概念,但這邊老師試著帶同學有一些基本概念。因為不少的資訊系統資料儲存的地方在資料庫中,從銀行帳戶、病患資料、線上購物 … 甚至在線上遊戲中,都會需要資料庫來幫助資料的存取。幫助同學了解如何寫程式,跟資料庫系統查詢(或取得)、新增、修改(或更新)、刪除資料,是一個很重要的學習內容。
:::info
電腦系統常使用資料庫來紀錄資料
:::
以電腦程式和資料庫溝通,主要包括了三個部份,「**資料庫**」、「**資料庫管理系統**」和「**程式**」:
1. **資料庫**:資料庫實際上是一種檔案系統,本質也是檔案,但是透過資料庫管理系統來讓很多人可以一同使用這些資料檔案
2. **資料庫管理系統**:英文是 Database Management System, DBMS,用來操作資料庫的軟體,實際對資料庫進行查詢(或取得)、新增、修改(或更新)、刪除資料新的工作,都是由它進行。多數的資料庫管理系統都可以用一種叫 SQL 的指令來操作。目前比較常見的所謂「資料庫」,實際上都是資料庫管理系統,例如:MySQL、Oracle 等資料庫管理系統。
3. **程式**:我們可以使用各式各樣的程式語言,將前述的 SQL 指令傳到資料庫系統,藉此進行查詢(或取得)、新增、修改(或更新)、刪除資料等等工作。例如:線上 RPG 遊戲要顯示角色的數值,遊戲設計師要寫程式,將查詢玩家遊戲角色數值的 SQL 指令,傳到遊戲伺服器中的資料庫中查詢,資料庫系統就會將資料再回傳到遊戲程式中,再顯示成為玩家遊戲角色的各項數值。
``` mermaid
graph TD;
電腦程式中設定SQL指令-->傳到資料庫管理系統;
傳到資料庫管理系統-->進行查詢新增修改或刪除等等工作;
進行查詢新增修改或刪除等等工作-->將結果回傳給電腦程式;
將結果回傳給電腦程式-->電腦程式將結果顯示;
```
## 資料庫裡面的資料長什麼樣子?
如果你用過 EXCEL 你一定能知道資料庫資料長什麼樣子,你可以看得到,在 EXCEL 表格中的資料是呈現「**表格**」的形式,第一列資料都是「**欄位**」,底下的每一筆資料,都會依照欄位的說明顯示不同的資料。
其中**每一筆資料都是一個獨立的**,例如下表的第 2 行資料,顯示就是一個叫令狐沖的人,在 2017.9.2 那一天花費 300 元搭火車。我們都能利用良好的欄位設計,將各式各樣的資料整理成像這樣的表格資料。

舉例來說,以下就是一個 MySQL 資料庫管理系統的畫面,你可以看到,資料庫中的資料也長的和 Excel 很像,也是表格呈現的資料。

## 登入系統的邏輯
試想以下是一儲存使用者帳號密碼的表格:
|id|account|password|name
|--|-------|--------|----
|1|abc|123|車太炫
|2|def|456|孔瘤
這時同學你可以想想看,如果我們把使用者的帳號和密碼都放到這個表格來儲存,每一個使用者都有帳號(account)與密碼(password)欄位,使用者帳號與密碼也必須要「成對」。
如果我們利用這兩個資料來「查詢」這一張表格,假設用「帳號 abc 與密碼 123」,應該可以查詢出來有一個「車太炫」會員的資料。也就是說:帳號 abc 與密碼 123 是一組「合法」的帳號密碼。如果有人在登入系統輸入帳號 abc 與密碼 123,就能成功登入。
如果你能理解這個簡單的例子,接下來我們來建置一個簡單的資料庫登入系統。
## 專案範例檔下載
[GitHub](https://github.com/billy1125/110-2_WindowsFormProgrammingTutorial)
## 程式功能
這次我們會設計一個簡單的登入畫面,使用者可以輸入帳號密碼來進入系統,特別需要注意的是,我們是將使用者帳號和密碼放在資料庫中。
為什麼要把使用者帳戶和密碼資料放在資料庫呢?為什麼不乾脆把有權限的帳號密碼直接寫在程式裡面?你可以這樣想,因為一個需要登入的系統,你不會只希望只有幾個人使用,你會希望可以控管可以進來的人。如果每一次有新的使用者,你就要改程式,同樣的,修改帳戶資料或者刪除使用者,你也得改程式,每次帳號密碼一有變化,都要修改程式,這樣會改不完。
利用資料庫,你可以簡單的控管會員,無論你要確認帳號密碼正確性、修改會員資料、刪除會員等等,都只要修改資料庫會員資料即可,不用動到程式本身。
## 開啟新專案
開啟新專案之前有教學過,如果你忘記了或不太熟悉,請點選[這裡](https://hackmd.io/sFFV6T2oT0-ysHotbIybhw?view#WPF-%E7%9A%84%E5%B0%88%E6%A1%88%E8%A8%AD%E5%AE%9A)。
:::success
我們的專案名稱可以叫做「Login」,簡單就好。
:::
## 開始吧!
還記得第二部分的基本視窗程式設計嗎?同樣的,我們依照之前說過的兩個主要程式設計流程來設計:
1. 介面基本設計
2. 程式撰寫
因此,我們也是先初步設計介面,再來進行程式撰寫。
## 第一步:進行介面基本設計
和過去的範例程式都類似,請你先從工具箱拉控制項元件進來,需要 1 個輸入文字框、 1 個密碼輸入文字框(PasswordBox)、1 個按鍵、2 個標籤元件。除了標籤元件之外,將每一個控制項設定一個名稱,例如以下的範例名稱:
:::info
1. 帳號輸入文字框:txtAccount
2. 密碼輸入文字框:pbPassword
3. 登入按鍵:btnLogin
:::

你可以依據自己的喜好,將元件大小、字體大小和字型設定好。如果你打算把文字能夠做上下置中,你可以參考以下的屬性設定,將「VerticalContentAlignment」項目設定為上下置中。

## 第二部分:程式碼撰寫
接下來,由於我們要連線的資料庫叫做 MySQL 資料庫管理系統,如果要用 Visual Studio 2019 來連線到 MySQL,必須要安裝第三方的套件程式庫。
而在 Visual Studio 之中有一個可以讓程式設計師取得第三方程式庫的管理工具,稱為 **NuGet**,NuGet 其實就是一個用來安裝第三方套件的工具,有些比較複雜的套件需要很多設定步驟才能在專案上使用,其實會很麻煩,如果透過 NuGet 就可以簡化並自動依照專案環境來安裝。
使用 NuGet 的方式,需要到方案總管上面按右滑鼠鍵,就會出現以下畫面,點選「管理NuGet套件」選項。

然後就會出現以下套件管理畫面,由於我們要安裝的是可以連線 MySQL 的第三方套件,請點選「瀏覽」,然後在搜尋列查詢關鍵字「MySQL」,應該第一個選項就是 MySQL.Data,請再點選「安裝」來安裝這個套件

如果出現這個畫面,請再按「OK」。

再給予安裝授權,請按「I Accept」,就會進行下載與安裝。

如果你安裝完成之後,到方案總管點開參考資料夾,你可以看到 MySQL.Data 已經被放到裡面去。

通常大多數的第三方套件都是這樣來安裝的,下次如果你寫的程式需要一個特定的第三方套件,安裝的方式就跟以上的流程是類似的。
### 事件綁定
然後我們就要把程式寫進來,讓程式可以運作。首先請先把登入按鍵綁定 click 事件,然後將以下程式碼置入。請特別注意,在程式碼上方要有一段「using MySql.Data.MySqlClient;」才能引入 MySQL.Data 第三方套件。
```csharp=
using MySql.Data.MySqlClient; // 導入第三方 MySQL.Data 套件的連線程式庫
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
string strAccount = txtAccount.Text; // 取得帳號輸入文字框的內容
string strPassword = pbPassword.Password; // 取得密碼輸入文字框的內容
bool isLoginSuccess = false; // 設定一個布林變數,用來判別是否登入成功
string strUserName = ""; // 設定一個字串變數,屆時用來儲存資料庫中的使用者姓名
try
{
// 資料庫連線字串,Server:資料庫管理系統的位址(IP或網址)、Uid:可以使用資料庫的帳號、Pwd:可以使用資料庫的密碼、database:要連線的資料庫名稱
var connstr = "Server=120.101.58.4;Uid=user;Pwd=user;database=login";
// 使用 using 語法來進行資料庫連線
using (var conn = new MySqlConnection(connstr))
{
conn.Open(); // 開啟資料庫連線
// 使用 using 語法來設定資料庫的操作命令
using (var cmd = conn.CreateCommand())
{
// 我們之前講過的 SQL 指令
cmd.CommandText = "SELECT * FROM users WHERE account=?account and password=?password";
cmd.Parameters.Add(new MySqlParameter("account", strAccount)); // 綁定帳號參數
cmd.Parameters.Add(new MySqlParameter("password", strPassword)); // 綁定密碼參數
// 使用 using 語法來對資料庫執行操作命令
using (var reader = cmd.ExecuteReader())
{
//檢查是否有資料列
if (reader.HasRows)
{
isLoginSuccess = true; // 有資料的話,代表的確帳號密碼無誤,然後將是否登入成功的變數(isLoginSuccess)設定為「true」
// 利用一個迴圈來讀取每一筆資料
while (reader.Read())
{
strUserName = reader["name"].ToString(); // 擷取欄位是「name」的資料,然後儲存到使用者姓名的變數(strUserName)
}
}
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString()); // 如果中間的資料庫連線流程有問題,就會跳出錯誤訊息
}
finally
{
// 最後依照是否登入成功,顯示成功或失敗的訊息
if (isLoginSuccess == true)
MessageBox.Show(strUserName + " 你好!你已經登入成功");
else
MessageBox.Show("登入失敗");
}
}
```
### 小小測試程式
這時請按下 F5 來測試程式,將帳號密碼輸入「abc/123」或「def/456」你應該就會看到成功登入的畫面,而且使用不同帳號密碼組合,都會顯示不同姓名。
 
 
如果你輸入錯誤的帳密,也會跳出錯誤訊息。

而且聰明的你,大概會發現,程式中並沒有帳號密碼資訊,所以程式一定是從遠端的資料庫管理系統中取得,這時恭喜你第一次寫出一支連線到網路資料庫的程式哦!
### 程式說明
1. **使用 try...catch...finally 語法來避免程式當機**
為什麼要用 try...catch...finally 呢?主要是因為如果有一段程式可能出錯,一旦發生錯誤不僅可能讓程式當機,也有可能出現讓使用者無法理解的錯誤訊息,對使用者來說是一種困擾。而且使用 try...catch...finally 也能提供補救的機會,讓程式能夠穩定繼續執行。
以下就是 try...catch...finally 的標準語法。
```csharp=
try
{
// 這邊放要執行的程式
}
catch (Exception ex)
{
// 如果發生錯誤,進行錯誤的處理
}
finally
{
// 無論程式是否有錯誤,都必須要執行的程式段落
}
```
Tr觀u可以觀察我們的資料庫連線程式,也就是只要是進行資料庫存取的程式都是放在 try 與 catch 之間。catch 與 finally 之間則是用來顯示錯誤訊息,finally 之後就是顯示是否成功或失敗的訊息,因為無論如何是否連線成功,都必須要出現成功或失敗的結果,因此要放在 finally 後面。
2. **建立資料庫連線字串**
要與資料庫連線,必須要有資料庫的使用權限,因此必須要使用資料庫管理員給你的帳號密碼,然後藉此建立資料庫連線字串。
我們連線的資料庫位址為 120.101.58.4,帳號密碼都是 user,並且指定的資料庫為 login。
:::info
Server=120.101.58.4
Uid=user
Pwd=user
database=login
:::
所以建立的資料庫連線字串為以下:
```csharp
// 資料庫連線字串,Server:資料庫管理系統的位址(IP或網址)、Uid:可以使用資料庫的帳號、Pwd:可以使用資料庫的密碼、database:要連線的資料庫名稱
var connstr = "Server=120.101.58.4;Uid=user;Pwd=user;database=login";
```
3. **使用 using 語法讓連線資料庫能自動被斷線**
使用 using 最主要的目的是為了讓物件建立的同時能確保該物件所佔用的資源一定會被完整釋放,如果沒有釋放這些無法自動釋放的資源,就很有可能讓程式發生系統資源耗盡的狀況。
由於要和資料庫進行存取,其實都要進行連線,對資料庫管理系統來說,佔住一個連線如果不放掉,都是浪費寶貴的網路資源。因此我們使用 using 語法,讓我們連線的過程最後都會釋放連線,避免佔住連線不放造成資料庫無法提供更多的連線。
以下就是 using 的標準語法。
```csharp
using (建立物件)
{
進行的工作
}
```
你可以注意到,我們的程式中使用了三個 using 來進行資料庫連線的工作。
資料庫連線
```csharp
// 使用 using 語法來進行資料庫連線
using (var conn = new MySqlConnection(connstr))
{
...資料庫連線的工作
}
```
設定資料庫操作指令
```csharp
// 使用 using 語法來設定資料庫的操作命令
using (var cmd = conn.CreateCommand())
{
...設定資料庫操作命令
}
```
執行資料庫操作指令
```csharp
// 使用 using 語法來對資料庫執行操作命令
using (var reader = cmd.ExecuteReader())
{
...執行資料庫操作命令
}
```
因此在程式中對資料庫進行各種工作,大致流程如下:
``` mermaid
graph TD;
資料庫連線-->設定資料庫操作指令;
設定資料庫操作指令-->執行資料庫操作指令;
```
並且由於我們使用 using,所以只要執行完成,就會自動釋放連線,你不需要另外寫程式關閉連線,這樣會比較方便。
4. **SQL 資料庫操作指令**
之前有跟同學提及,多數的資料庫管理系統都可以用一種叫 SQL 的指令來操作,因此不管使用什麼程式語言,只要能夠連線到資料庫,都能使用 SQL 資料庫操作指令對資料庫進行各種工作。
因此我們當然需要在程式裡設定 SQL 資料庫操作指令,才能對資料庫進行操作。
```csharp=
cmd.CommandText = "SELECT * FROM users WHERE account=?account and password=?password";
cmd.Parameters.Add(new MySqlParameter("account", strAccount)); // 綁定帳號參數
cmd.Parameters.Add(new MySqlParameter("password", strPassword));
```
其中第 1 行就是 SQL 資料庫操作指令,並且第 2-3 行是將使用者輸入的帳號密碼「綁定」到 SQL 指令中,藉此組合成查詢指令。
5. **讀取資料**
最後我們執行了資料庫操作指令,並且進行判斷是否有資料回來,如果有的話,就將變數「isLoginSuccess」設定為 true,並且逐一讀取回來的每一筆資料,由於應該一個帳號密碼的組合只會有一筆資料,所以雖然是跑迴圈,但其實應該只會跑到一筆資料。
之後,我們將讀取的資料擷取欄位是「name」的資料,然後儲存到使用者姓名的變數「strUserName」,作為最後顯示使用者姓名的變數之用。
```csharp=
//檢查是否有資料列
if (reader.HasRows)
{
isLoginSuccess = true; // 有資料的話,代表的確帳號密碼無誤,然後將是否登入成功的變數(isLoginSuccess)設定為「true」
// 利用一個迴圈來讀取每一筆資料
while (reader.Read())
{
strUserName = reader["name"].ToString(); // 擷取欄位是「name」的資料,然後儲存到使用者姓名的變數(strUserName)
}
}
```
6. **顯示結果到視窗中**
之前的工作都只是在對資料庫存取,不過最後的結果仍然要顯示給使用者看,所以我們在 finally 這邊寫了一個簡單的判斷式,如果變數「isLoginSuccess」為 true,代表登入成功,我們用一個訊息視窗來顯示使用者姓名與成功訊息;反之,如果「isLoginSuccess」不為 true,則是顯示失敗訊息。
```csharp=
finally
{
// 最後依照是否登入成功,顯示成功或失敗的訊息
if (isLoginSuccess == true)
MessageBox.Show(strUserName + " 你好!你已經登入成功");
else
MessageBox.Show("登入失敗");
}
```
## 總結
這邊我們利用簡單的視窗程式,然後結合 MySQL.Data 第三方套件程式庫來對資料庫連線,透過標準的資料庫連線程序,做一個簡單的登入系統。
其實很多資訊系統都是和資料庫進行操作,提供各種大小型的資訊服務,雖然這部份的議題比較進階,但希望透過簡單案例幫助同學建立基本的資料庫概念,對於各位同學之後學習各種程式來操作資料庫,會是一個很好的開始。