數(shù)據(jù)訪問組件是一組通用的訪問數(shù)據(jù)庫的代碼,在所有項目中都可以用,一般不需要修改。本節(jié)使用的是Microsoft提供的數(shù)據(jù)訪問助手,其封裝很嚴密,且應(yīng)用簡單。
首先要先添加一個類,并命名為SqlHelper,系統(tǒng)會提示是否將類放在App_Code文件夾中。此時一定要選擇“是”,因為放在此文件夾下,系統(tǒng)會自動進行編譯,程序員就可以直接使用,無需另外編譯了。
SqlHelper的目的是從數(shù)據(jù)庫獲得信息或?qū)⑿畔⒈4娴綌?shù)據(jù)庫。本實例的SqlHelper主要功能如下。
(1)執(zhí)行不返回數(shù)據(jù)的T-Sql命令。例如修改會員卡信息、添加會員資料等。
(2)返回一個字段的T-Sql命令。例如獲取會員卡類型的積分規(guī)則。
?。?)返回一組數(shù)據(jù)。例如獲取會員資料、獲取所有會員卡類型等。
?。?)緩存參數(shù)列表。在執(zhí)行一條語句時,可能有多個參數(shù),為了提高速度,將參數(shù)緩存。
?。?)讀取緩存的參數(shù)。
下面用圖表的方式描述SqlHelper類的功能,如圖7-16所示。

在SqlHelper類中添加數(shù)據(jù)庫處理,代碼如下:
一、原程序:
//編號1
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Collections;
using System.Data.SqlClient;
//編號2
/**//// <summary>
/// 數(shù)據(jù)庫的通用訪問代碼
/// 此類為抽象類,不允許實例化,在應(yīng)用時直接調(diào)用即可
/// </summary>
public abstract class SqlHelper
...{
//獲取數(shù)據(jù)庫連接字符串,其屬于靜態(tài)變量且只讀,項目中所有文檔可以直接使用,但不能修改
//編號13
public static readonly string ConnectionStringLocalTransaction =
ConfigurationManager.ConnectionStrings["connstring"].ConnectionString;
// 哈希表用來存儲緩存的參數(shù)信息,哈希表可以存儲任意類型的參數(shù)
//編號8
private static Hashtable parmCache = Hashtable.Synchronized(new Hashtable());
//編號9
/**//// <summary>
///執(zhí)行一個不需要返回值的SqlCommand命令,通過指定專用的連接字符串。
/// 使用參數(shù)數(shù)組形式提供參數(shù)列表
/// </summary>
/// <remarks>
/// 使用示例:
/// int result = ExecuteNonQuery(connString, CommandType.StoredProcedure,
/// "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="connectionString">一個有效的數(shù)據(jù)庫連接字符串</param>
/// <param name="commandType">SqlCommand命令類型 (存儲過程, T-SQL語句, 等等。)
/// </param>
/// <param name="commandText">存儲過程的名字或者 T-SQL 語句</param>
/// <param name="commandParameters">以數(shù)組形式提供SqlCommand命令中用到的參數(shù)列表
/// </param>
/// <returns>返回一個數(shù)值表示此SqlCommand命令執(zhí)行后影響的行數(shù)</returns>
//編號3
public static int ExecuteNonQuery(string connectionString, CommandType cmdType,
string cmdText, params SqlParameter[] commandParameters)
...{
SqlCommand cmd = new SqlCommand();
//編號4
using (SqlConnection conn = new SqlConnection(connectionString))
...{
//通過PrePareCommand方法將參數(shù)逐個加入到SqlCommand的參數(shù)集合中
PrepareCommand(cmd, conn, null, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
//清空SqlCommand中的參數(shù)列表
cmd.Parameters.Clear();
//編號14
return val;
}
}
/**//// <summary>
///執(zhí)行一條不返回結(jié)果的SqlCommand,通過一個已經(jīng)存在的數(shù)據(jù)庫連接
/// 使用參數(shù)數(shù)組提供參數(shù)
/// </summary>
/// <remarks>
/// 使用示例:
/// int result = ExecuteNonQuery(conn, CommandType.StoredProcedure,
/// "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="conn">一個現(xiàn)有的數(shù)據(jù)庫連接</param>
/// <param name="commandType">SqlCommand命令類型 (存儲過程, T-SQL語句, 等等。)
/// </param>
/// <param name="commandText">存儲過程的名字或者 T-SQL 語句</param>
/// <param name="commandParameters">以數(shù)組形式提供SqlCommand命令中用到的參數(shù)列表
/// </param>
/// <returns>返回一個數(shù)值表示此SqlCommand命令執(zhí)行后影響的行數(shù)</returns>
//編號5
public static int ExecuteNonQuery(SqlConnection connection, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
...{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
}
/**//// <summary>
/// 執(zhí)行一條不返回結(jié)果的SqlCommand,通過一個已經(jīng)存在的數(shù)據(jù)庫事物處理
/// 使用參數(shù)數(shù)組提供參數(shù)
/// </summary>
/// <remarks>
/// 使用示例:
/// int result = ExecuteNonQuery(trans, CommandType.StoredProcedure,
/// "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="trans">一個存在的 sql 事物處理</param>
/// <param name="commandType">SqlCommand命令類型 (存儲過程, T-SQL語句, 等等。)
/// </param>
/// <param name="commandText">存儲過程的名字或者 T-SQL 語句</param>
/// <param name="commandParameters">以數(shù)組形式提供SqlCommand命令中用到的參數(shù)列表
/// </param>
/// <returns>返回一個數(shù)值表示此SqlCommand命令執(zhí)行后影響的行數(shù)</returns>
//編號12
public static int ExecuteNonQuery(SqlTransaction trans, CommandType cmdType,string cmdText, params SqlParameter[] commandParameters)
...{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, trans.Connection, trans, cmdType, cmdText, commandParameters);
int val = cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
return val;
}
/**//// <summary>
/// 執(zhí)行一條返回結(jié)果集的SqlCommand命令,通過專用的連接字符串。
/// 使用參數(shù)數(shù)組提供參數(shù)
/// </summary>
/// <remarks>
/// 使用示例:
/// SqlDataReader r = ExecuteReader(connString, CommandType.StoredProcedure,
/// "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="connectionString">一個有效的數(shù)據(jù)庫連接字符串</param>
/// <param name="commandType">SqlCommand命令類型 (存儲過程, T-SQL語句, 等等。)
/// </param>
/// <param name="commandText">存儲過程的名字或者 T-SQL 語句</param>
/// <param name="commandParameters">以數(shù)組形式提供SqlCommand命令中用到的參數(shù)列表
/// </param>
/// <returns>返回一個包含結(jié)果的SqlDataReader</returns>
public static SqlDataReader ExecuteReader(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
...{
//編號7
SqlCommand cmd = new SqlCommand();
SqlConnection conn = new SqlConnection(connectionString);
// 在這里使用try/catch處理是因為如果方法出現(xiàn)異常,則SqlDataReader就不存在,
//CommandBehavior.CloseConnection的語句就不會執(zhí)行,觸發(fā)的異常由catch捕獲。
//關(guān)閉數(shù)據(jù)庫連接,并通過throw再次引發(fā)捕捉到的異常?!?nbsp;
try
...{
PrepareCommand(cmd, conn, null, cmdType, cmdText, commandParameters);
SqlDataReader rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
cmd.Parameters.Clear();
return rdr;
}
catch
...{
conn.Close();
throw;
}
}
/**//// <summary>
/// 執(zhí)行一條返回第一條記錄第一列的SqlCommand命令,通過專用的連接字符串。
/// 使用參數(shù)數(shù)組提供參數(shù)
/// </summary>
/// <remarks>
/// 使用示例:
/// Object obj = ExecuteScalar(connString, CommandType.StoredProcedure,
/// "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="connectionString">一個有效的數(shù)據(jù)庫連接字符串</param>
/// <param name="commandType">SqlCommand命令類型 (存儲過程, T-SQL語句, 等等。)
/// </param>
/// <param name="commandText">存儲過程的名字或者 T-SQL 語句</param>
/// <param name="commandParameters">以數(shù)組形式提供SqlCommand命令中用到的參數(shù)列表
/// </param>
/// <returns>返回一個object類型的數(shù)據(jù),可以通過 Convert.To{Type}方法轉(zhuǎn)換類型</returns>
public static object ExecuteScalar(string connectionString, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
...{
SqlCommand cmd = new SqlCommand();
using (SqlConnection connection = new SqlConnection(connectionString))
...{
PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
}
}
/**//// <summary>
/// 執(zhí)行一條返回第一條記錄第一列的SqlCommand命令,通過已經(jīng)存在的數(shù)據(jù)庫連接。
/// 使用參數(shù)數(shù)組提供參數(shù)
/// </summary>
/// <remarks>
/// 使用示例:
/// Object obj = ExecuteScalar(connString, CommandType.StoredProcedure,
/// "PublishOrders", new SqlParameter("@prodid", 24));
/// </remarks>
/// <param name="conn">一個已經(jīng)存在的數(shù)據(jù)庫連接</param>
/// <param name="commandType">SqlCommand命令類型 (存儲過程, T-SQL語句, 等等。)
/// </param>
/// <param name="commandText">存儲過程的名字或者 T-SQL 語句</param>
/// <param name="commandParameters">以數(shù)組形式提供SqlCommand命令中用到的參數(shù)列表
/// </param>
/// <returns>返回一個object類型的數(shù)據(jù),可以通過 Convert.To{Type}方法轉(zhuǎn)換類型
/// </returns>
public static object ExecuteScalar(SqlConnection connection, CommandType cmdType, string cmdText, params SqlParameter[] commandParameters)
...{
SqlCommand cmd = new SqlCommand();
PrepareCommand(cmd, connection, null, cmdType, cmdText, commandParameters);
object val = cmd.ExecuteScalar();
cmd.Parameters.Clear();
return val;
}
/**//// <summary>
/// 緩存參數(shù)數(shù)組
/// </summary>
/// <param name="cacheKey">參數(shù)緩存的鍵值</param>
/// <param name="cmdParms">被緩存的參數(shù)列表</param>
public static void CacheParameters(string cacheKey, params SqlParameter[] commandParameters)
...{
//編號10
parmCache[cacheKey] = commandParameters;
}
/**//// <summary>
/// 獲取被緩存的參數(shù)
/// </summary>
/// <param name="cacheKey">用于查找參數(shù)的KEY值</param>
/// <returns>返回緩存的參數(shù)數(shù)組</returns>
public static SqlParameter[] GetCachedParameters(string cacheKey)
...{
SqlParameter[] cachedParms = (SqlParameter[])parmCache[cacheKey];
if (cachedParms == null)
return null;
//新建一個參數(shù)的克隆列表
SqlParameter[] clonedParms = new SqlParameter[cachedParms.Length];
//通過循環(huán)為克隆參數(shù)列表賦值
for (int i = 0, j = cachedParms.Length; i < j; i++)
//使用clone方法復(fù)制參數(shù)列表中的參數(shù)
//編號11
clonedParms[i] = (SqlParameter)((ICloneable)cachedParms[i]).Clone();
return clonedParms;
}
/**//// <summary>
/// 為執(zhí)行命令準備參數(shù)
/// </summary>
/// <param name="cmd">SqlCommand 命令</param>
/// <param name="conn">已經(jīng)存在的數(shù)據(jù)庫連接</param>
/// <param name="trans">數(shù)據(jù)庫事物處理</param>
/// <param name="cmdType">SqlCommand命令類型 (存儲過程,T-SQL語句,等等。) </param>
/// <param name="cmdText">Command text,T-SQL語句 例如 Select * from
/// Products</param>
/// <param name="cmdParms">返回帶參數(shù)的命令</param>
//編號6
private static void PrepareCommand(SqlCommand cmd, SqlConnection conn,SqlTransaction trans, CommandType cmdType, string cmdText, SqlParameter[]
cmdParms)
...{
//判斷數(shù)據(jù)庫連接狀態(tài)
if (conn.State != ConnectionState.Open)
conn.Open();
cmd.Connection = conn;
cmd.CommandText = cmdText;
//判斷是否需要事物處理
if (trans != null)
cmd.Transaction = trans;
cmd.CommandType = cmdType;
if (cmdParms != null)
...{
foreach (SqlParameter parm in cmdParms)
cmd.Parameters.Add(parm);
}
}
}
二、代碼分析:
代碼是一個比較完整的數(shù)據(jù)訪問組件,下面分析這些代碼的具體實現(xiàn)。
?。?)Using關(guān)鍵字:見代碼編號1。Using處理對命名空間的引用。通常,如果系統(tǒng)提示找不到某個類,一定要仔細檢查是否引用了這個類的命名空間。在C# 2.0中Using還可以實現(xiàn)命名空間的別名,例如:Using sc =System.Collections。別名就是用來簡化命名空間的,別名的使用語句是:sc::ArrayList list=new sc::Arraylist( )。
?。?)注釋:見代碼編號2。在方法上面一行如果輸入“///”,系統(tǒng)會自動將此方法的注釋架構(gòu)搭建好,主要包括Summary和param name。如果方法有返回值,還包括returns;如果需要特殊說明可以使用remarks標志。根據(jù)這個結(jié)構(gòu),可以很容易地描述清楚整個方法的組成及使用方法。在注釋中使用“///”,將來在代碼調(diào)用時,會出現(xiàn)提示,提示的內(nèi)容就是所添加的注釋。
?。?)CommandType的使用:見代碼編號3。SQL Server數(shù)據(jù)處理的兩種方法:存儲過程和T-SQL語句。每條執(zhí)行語句都有個參數(shù)CommandType,這是個枚舉類型,有3個選項:StoredProcedure、TableDirect和Text。它們分別表示存儲過程、表和T-SQL語句。如果CommandType參數(shù)選擇StoredProcedure,則cmdText參數(shù)就是存儲過程的名字;如果CommandType是Text,則cmdText是SQL語句;如果CommandType是TableDirect,則cmdText是表名稱。
?。?)Using語句:見代碼編號4。在Using語句中,用“{}”定義一個范圍,在語句完成時釋放語句內(nèi)使用的資源。Using語句通常用在獲取數(shù)據(jù)的方法中,語句的范圍是從打開數(shù)據(jù)庫連接開始,到所有使用連接的資源都運行完畢后終止。Using語句可以與多個對象一起使用。使用Using語句的對象必須繼承IDispose接口。此接口實現(xiàn)了Dispose方法,該方法才是釋放對象資源的執(zhí)行者。
(5)參數(shù)數(shù)組:見代碼編號5。在C#中不允許使用可選參數(shù),所以參數(shù)通常由不指定大小的數(shù)組來實現(xiàn)。數(shù)組中參數(shù)的添加由PrepareCommand方法完成。
(6)SQL事務(wù)處理:見代碼編號6。事務(wù)是指一組相關(guān)聯(lián)的操作。在事務(wù)處理時,通常鎖住相關(guān)的表,等事務(wù)處理完成后才解鎖,這樣保證了數(shù)據(jù)的完整性。事務(wù)一般包括3個方法:開始事務(wù)、執(zhí)行事務(wù)和事務(wù)回滾(RollBack)。如果事務(wù)中的一條語句出現(xiàn)問題,則事務(wù)回滾,其他語句的執(zhí)行也被取消。
(7)throw:見代碼編號7。再次引發(fā)捕獲的異常,目的是向文本中添加異常處理信息。如果要引發(fā)異常,throw和catch一定要搭配使用,如果catch有參數(shù),則throw也要帶參數(shù),相反亦然。
(8)哈希表:見代碼編號8。表示鍵/值(key/value)對的組合。通過鍵值的映射來訪問哈希代碼。.NET中的哈希表是System.Collections命名空間提供的一個容器,英文名稱為HashTable,通過key來實現(xiàn)快速查找,key區(qū)分大小寫。value存儲key對應(yīng)的值,通常表現(xiàn)為object類型,當取值時要進行相應(yīng)的類型轉(zhuǎn)換。
?。?)哈希表的同步包裝:見代碼編號9。private static Hashtable parmCache = Hashtable. Synchronized(new Hashtable( ))這條語句實現(xiàn)了哈希表的同步包裝,包裝是基于線程安全的。此處的哈希表是static類型的靜態(tài)變量,既然是static,就是一次定義,全局使用。所有的參數(shù)都使用這個哈希表,那如何保證其他人在修改的時候不影響自己的讀取呢?以前可以使用lock的方法先鎖定表,不允許他人修改,讀取完畢后再解鎖?,F(xiàn)在.NET提供了HashTable的Synchronized方法,實現(xiàn)同樣的功能,不需要手動加鎖,直接由系統(tǒng)
(10)緩存參數(shù)列表:見代碼編號10。緩存參數(shù)列表就是將參數(shù)信息保存在已經(jīng)定義的靜態(tài)HashTable中。因為HashTable是靜態(tài)的,一旦定義,就分配了內(nèi)存地址,在項目中隨時都可以使用,起到了緩存數(shù)據(jù)的作用。
(11)clone:克隆。見代碼編號11。在.NET中,幾乎所有繼承Collections類的集合都具有clone的方法。在獲取緩存參數(shù)的方法中有下面這句代碼:clonedParms[i] = (SqlParameter) ((ICloneable)cachedParms[i]).Clone( )。因為所有的參數(shù)都保存在一個HashTable中,表中保存的只是參數(shù)的名字,而參數(shù)的內(nèi)容則是由不同的調(diào)用給予不同的值。為了正確反映調(diào)用者的值,必須克隆出一個參數(shù)列表,由調(diào)用者根據(jù)功能賦予對應(yīng)的值。
(12)方法重載:見代碼編號12。定義了兩個或多個具有相同名稱但參數(shù)不同的方法。在SqlHelper中,ExcuteNonQuery被重載了4次,通過代碼中的注釋可以很清楚地看出參數(shù)列表的不同。在實際應(yīng)用中,根據(jù)功能環(huán)境調(diào)用相應(yīng)的方法。
(13)ConfigurationManager:見代碼編號13。是ASP.NET 2.0中新加的一個類,主要對Web.config文件進行管理??梢暂p松獲取在Web.config文件中定義的配置,通常用來獲取數(shù)據(jù)庫連接字符串和個性化配置信息。本例在Web.config中添加了下面這行數(shù)據(jù)庫連接字符串配置。
<connectionStrings >
<add name="connstring" connectionString ="server=.;database=membercard; uid=sa; pwd="/>
</connectionStrings>
(14)ExcuteNonQuery方法的返回值:見代碼編號14。在SqlCommand的方法中,ExcuteNonQuery用來執(zhí)行插入、更新或刪除等操作。程序并不要求有任何返回值,但在SqlHelper中定義此方法時返回了一個數(shù)值型數(shù)據(jù)。該數(shù)據(jù)用來表示執(zhí)行當前語句后數(shù)據(jù)庫中被影響的行數(shù)。雖然在此定義了返回值,但在程序的調(diào)用中,可以不用返回值,直接執(zhí)行方法。例如:SqlHelper. ExcuteNonQuery( )就是正確的,不一定非要寫成 int val= SqlHelper. ExcuteNonQuery( )。如果程序中需要的不是int值,還可以進行轉(zhuǎn)換。