摘要
在上一篇文章《你必須知道的ADO.NET(六) 談談Command對象與數據檢索》中,我詳細講解了Command對象的基礎知識以及基本用法。作為ADO.NET中最具執行力的對象,Command對象實屬變幻莫測。在本文中,我將與大家一起探討Command對象的高級應用與技巧。
目錄
1. 異步執行命令
在ADO.NET 2.0版本之前,執行Command對象命令時,需要等待命令完成才能執行其他操作。比如,執行ExcuteNonQuery()方法,應用程序將會保持阻塞,直到數據操作成功完成或者異常終止以及連接超時。在ADO.NET 2.0版本引入異步執行特性,顯然,ADO.NET更穩健,更完美了。
異步執行的根本思想是,在執行命令操作時,無需等待命令操作完成,可以並發的處理其他操作。ADO.NET提供了豐富的方法來處理異步操作,BeginExecuteNonQuery和EndExcuteNonQuery就是一對典型的為異步操作服務的方法。BeginExecuteNonQuery方法返回System.IAsyncResult接口對象。我們可以根據IAsyncResult的IsCompleted屬性來輪詢(檢測)命令是否執行完成。還是來看一個簡單的實例把!這個實例采用了《你必須知道的ADO.NET(六) 談談Comand對象與數據檢索》中的數據庫和數據表。在這個實例中,我們將在tb_SelCustomer中插入500行數據,並計算執行時間。代碼如下:
View Code
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Data;//必須引入
6 using System.Data.SqlClient;//必須引入
7
8 namespace Command
9 {
10 class Program
11 {
12 static void Main(string[] args)
13 {
14 SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder();
15 connStr.DataSource = @".\SQLEXPRESS";
16 connStr.IntegratedSecurity = true;
17 connStr.InitialCatalog = "db_MyDemo";
18 connStr.AsynchronousProcessing = true;//必須顯示說明異步操作
19
20 StringBuilder strSQL = new StringBuilder();
21
22 //插入100個測試客戶
23 for (int i = 1; i <= 500; ++i)
24 {
25 strSQL.Append("insert into tb_SelCustomer ");
26 strSQL.Append("values('");
27 string name = "測試客戶" + i.ToString();
28 strSQL.Append(name);
29 strSQL.Append("','0','0','13822223333','liuhaorain@163.com','廣東省深圳市寶安區',12.234556,34.222234,'422900','備注信息'); ");
30 }
31
32 using (SqlConnection conn = new SqlConnection(connStr.ConnectionString))
33 {
34 conn.Open();
35 SqlCommand cmd = new SqlCommand(strSQL.ToString(), conn);
36
37 IAsyncResult pending = cmd.BeginExecuteNonQuery();//開始執行異步操作
38 double time = 0;
39
40 //檢查異步處理狀態
41 while (pending.IsCompleted == false)
42 {
43 System.Threading.Thread.Sleep(1);
44 time++;
45 Console.WriteLine("{0}s", time * 0.001);
46 }
47
48 if (pending.IsCompleted == true)
49 {
50 Console.WriteLine("Data is inserted completely...\nTotal coast {0}s", time * 0.001);
51 }
52
53 cmd.EndExecuteNonQuery(pending);//結束異步操作
54 }
55
56 Console.Read();
57 }
58 }
59 }
處理結果如下:

2. 請使用參數化查詢
在ADO.NET中,查詢語句是以字符串的形式傳遞給外部數據庫服務器的。這些字符串不僅包含了基本命令關鍵字,操作符,還包含了限制查詢的數值。與其他編程語言不同,.NET是基於強類型來管理查詢字符串數據的。通過提供類型檢查和驗證,命令對象可使用參數來將值傳遞給 SQL 語句或存儲過程。 與命令文本不同,參數輸入被視為文本值,而不是可執行代碼。 這樣可幫助抵御“SQL 注入”攻擊,這種攻擊的攻擊者會將命令插入 SQL 語句,從而危及服務器的安全。參數化命令還可提高查詢執行性能,因為它們可幫助數據庫服務器將傳入命令與適當的緩存查詢計划進行准確匹配。
對於不同的數據源來說,Parameter對象不同,但都派生自DbParameter對象。下表列舉了不同數據源對應的Parameter對象。
| 數據提供程序 | 對應Paramter對象 | 命名空間 |
| SQLServer 數據源 | 使用SqlParamter對象 | System.Data.SqlClient.SqlParameter |
| Ole DB 數據源 | 使用OleDbParameter對象 | System.Data.OleDb.OleDbParameter |
| ODBC 數據源 | 使用OdbcParamter對象 | System.Data.Odbc.OdbcParameter |
| Oracle數據源 | 使用OracleParameter對象 | System.Data.OracleClient.OracleParameter |
Paramter對象的屬性很多,其中常見而且非常重要的主要有以下幾個:
- DbType: 獲取或設置參數的數據類型。
- Direction: 獲取或設置一個值,該值指示參數是否只可輸入、只可輸出、雙向還是存儲過程返回值參數。
- IsNullable: 獲取或設置一個值,該值指示參數是否可以為空。
- ParamteterName: 獲取或設置DbParamter的名稱。
- Size: 獲取或設置列中數據的最大大小。
- Value: 獲取或設置該參數的值。
以SQL Server為例,SqlCommand對象包含一個Paramters集合,Paramters集合中包含了所有所需的SqlParamter對象。當執行命令時,ADO.NET同時將SQL文本,占位符和參數集合傳遞給數據庫。
提示: 對於不同的數據源來說,占位符不同。SQLServer數據源用@parametername格式來命名參數,OleDb以及Odbc數據源均用問號(?)來標識參數位置,而Oracle則以:parmname格式使用命名參數。
下面我們看一個實例,修改 測試顧客1 的基本信息。修改的基本信息如下:
- 電話號碼(Phone):18665691100
- 電子郵箱(Email):test@163.com
- 地址(ContactAddress):中國深圳市南山區
代碼如下:
View Code
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Data;
6 using System.Data.SqlClient;
7
8
9 namespace Command2
10 {
11 class Program
12 {
13 static void Main(string[] args)
14 {
15 //構造連接字符串
16 SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder();
17 connStr.DataSource = @".\SQLEXPRESS";
18 connStr.IntegratedSecurity = true;
19 connStr.InitialCatalog = "db_MyDemo";
20
21 //拼接SQL語句
22 StringBuilder strSQL = new StringBuilder();
23 strSQL.Append("Update tb_SelCustomer Set ");
24 strSQL.Append("Phone = @Phone,");
25 strSQL.Append("Email = @Email,");
26 strSQL.Append("ContactAddress = @Address ");
27 strSQL.Append("where Name = @Name");
28
29 using (SqlConnection conn = new SqlConnection(connStr.ConnectionString))
30 {
31 SqlCommand cmd = new SqlCommand(strSQL.ToString(), conn);
32
33 //構造Parameter對象
34 SqlParameter para1 = new SqlParameter("@Phone", SqlDbType.VarChar, 12);
35 SqlParameter para2 = new SqlParameter("@Email",SqlDbType.VarChar,50);
36 SqlParameter para3 = new SqlParameter("@Address",SqlDbType.VarChar,200);
37 SqlParameter para4 = new SqlParameter("@Name",SqlDbType.VarChar,20);
38
39 //給Parater對象賦值
40 para1.Value = "18665691100";
41 para2.Value = "test@163.com";
42 para3.Value = "中國深圳市南山區";
43 para4.Value = "測試客戶1";
44
45 //添加到Parameters集合中
46 cmd.Parameters.Add(para1);
47 cmd.Parameters.Add(para2);
48 cmd.Parameters.Add(para3);
49 cmd.Parameters.Add(para4);
50
51 try
52 {
53 conn.Open();
54 cmd.ExecuteNonQuery();
55 Console.WriteLine("Update Success...");
56 }
57 catch(Exception ex)
58 {
59 Console.WriteLine("{0}",ex.Message);
60 }
61 }
62
63 Console.Read();
64 }
65 }
66 }
看了上面的代碼不知大家有何感想,是否覺得太多繁瑣呢?的確,我們可以用更簡潔的方法來實現。具體方法是,我們可以先構造Parameter對象數組,然后遍歷添加到Command對象的Paramters集合中。代碼如下:
View Code
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Data;
6 using System.Data.SqlClient;
7
8
9 namespace Command2
10 {
11 class Program
12 {
13 static void Main(string[] args)
14 {
15 //構造連接字符串
16 SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder();
17 connStr.DataSource = @".\SQLEXPRESS";
18 connStr.IntegratedSecurity = true;
19 connStr.InitialCatalog = "db_MyDemo";
20
21 //拼接SQL語句
22 StringBuilder strSQL = new StringBuilder();
23 strSQL.Append("Update tb_SelCustomer Set ");
24 strSQL.Append("Phone = @Phone,");
25 strSQL.Append("Email = @Email,");
26 strSQL.Append("ContactAddress = @Address ");
27 strSQL.Append("where Name = @Name");
28
29 using (SqlConnection conn = new SqlConnection(connStr.ConnectionString))
30 {
31 SqlCommand cmd = new SqlCommand(strSQL.ToString(), conn);
32
33 //構造Parameter對象
34 SqlParameter[] paras = new SqlParameter[]{
35 new SqlParameter("@Phone", SqlDbType.VarChar, 12),
36 new SqlParameter("@Email", SqlDbType.VarChar, 50),
37 new SqlParameter("@Address", SqlDbType.VarChar, 200),
38 new SqlParameter("@Name", SqlDbType.VarChar, 20)
39 };
40
41 //給Parater對象賦值
42 paras[0].Value = "18665691100";
43 paras[1].Value = "test@163.com";
44 paras[2].Value = "中國深圳市南山區";
45 paras[3].Value = "測試客戶1";
46
47 //遍歷添加到Parameters集合中
48 foreach (var item in paras)
49 {
50 cmd.Parameters.Add(item);
51 }
52
53 try
54 {
55 conn.Open();
56 cmd.ExecuteNonQuery();
57 Console.WriteLine("Update Success...");
58 }
59 catch (Exception ex)
60 {
61 Console.WriteLine("{0}", ex.Message);
62 }
63 }
64
65 Console.Read();
66 }
67 }
68 }
上面兩種寫法,結果完全相同。查詢數據庫,我們可以得到以下結果:

3. 如何獲取插入行的ID?
很多時候,我們需要知道插入行的ID是多少,以方便我們進行利用插入行的ID進行其他操作,比如在頁面上的展示等等。當然實現的方法有很多種,比如利用C#的out修飾符修飾參數,我更傾向於用SQL Server數據庫原生的OUTPUT關鍵字。OUTPUT關鍵字返回INSERT操作的一個字段(一般是主鍵ID)。因此我們只要結合OUTPUT關鍵字以及ExecuteScalar方法,就很容易得到插入行的主鍵。還是看一個簡單的實例把!我們在tb_SelCustomer中插入一個新的顧客,並返回這個顧客的ID。代碼如下:
View Code
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Data;
6 using System.Data.SqlClient;
7
8
9 namespace Command2
10 {
11 class Program
12 {
13 static void Main(string[] args)
14 {
15 //構造連接字符串
16 SqlConnectionStringBuilder connStr = new SqlConnectionStringBuilder();
17 connStr.DataSource = @".\SQLEXPRESS";
18 connStr.IntegratedSecurity = true;
19 connStr.InitialCatalog = "db_MyDemo";
20
21 //拼接SQL語句
22 StringBuilder strSQL = new StringBuilder();
23 strSQL.Append("insert tb_SelCustomer(Name) ");
24 strSQL.Append("OUTPUT inserted.ID values(@Name)");
25
26 using (SqlConnection conn = new SqlConnection(connStr.ConnectionString))
27 {
28 SqlCommand cmd = new SqlCommand(strSQL.ToString(), conn);
29
30 SqlParameter para = new SqlParameter("@Name", SqlDbType.VarChar, 20);
31 para.Value = "Kemi";
32 cmd.Parameters.Add(para);
33
34 try
35 {
36 conn.Open();
37 int insertedID = (int)cmd.ExecuteScalar();//獲取單個值
38
39 Console.WriteLine("Inserted ID:{0}", insertedID);
40 }
41 catch (Exception ex)
42 {
43 Console.WriteLine("{0}", ex.Message);
44 }
45 }
46
47 Console.Read();
48 }
49 }
50 }
運行結果如下:

4. 總結
簡言之,Command對象的核心作用是執行命令。在執行命令過程中,面臨的情況是十分復雜的。盡管如此,Command對象擁有優越的人力資源(屬性和方法),來應對一切可能發生的事。可以說,Command對象的穩定發揮,為ADO.NET打下了扎實的根基。到目前為止,我們基本上了解ADO.NET DataProvider組件所有的內容。因此,后面我將重點講述ADO.NET的心臟----DataSet以及如何將數據源本地化。另外,我非常期待能得到您的推薦和關注。
