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