于磊
摘 要:在基于C#的WinForm+SQL數(shù)據(jù)庫(kù)應(yīng)用開(kāi)發(fā)中,存在著大量數(shù)據(jù)庫(kù)操作,直接使用命令字符串進(jìn)行操作的常規(guī)方式存在執(zhí)行速度低、增加網(wǎng)絡(luò)通信量、修改困難等問(wèn)題。改善上述問(wèn)題的有效方法是利用SQL Server提供的存儲(chǔ)過(guò)程進(jìn)行數(shù)據(jù)庫(kù)操作。在實(shí)際開(kāi)發(fā)中應(yīng)用存儲(chǔ)過(guò)程,采用不同方法進(jìn)行對(duì)比測(cè)試,結(jié)果表明,在開(kāi)發(fā)中合理應(yīng)用存儲(chǔ)過(guò)程能有效地將應(yīng)用與數(shù)據(jù)操作分離,提高運(yùn)行效率,提高數(shù)據(jù)庫(kù)訪問(wèn)的安全性并增加靈活性。
關(guān)鍵詞:C#;WinForm;SQL;存儲(chǔ)過(guò)程
DOI:10.11907/rjdk.172524
中圖分類(lèi)號(hào):TP319
文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1672-7800(2018)004-0178-02
Abstract:In C# based WinForm+SQL application development, there is a large number of database operations. In general, we use command strings to perform database operations, but this method will reduce execution speed, increase network traffic and cause difficulty of modification. In order to improve the above problems, one efficient method is proposed to use the stored procedure of SQL Server to complete database operations. Through the use of stored procedure and the different methods in the development, the results show that applying stored procedure rationally in developmentrational employment of the stored procedure in develoment can effectively separate application implementation from data operation, improve operation efficiency and database access security and enhance flexibility.
Key Words:C#; WinForm; SQL; Stored Procedure
0 引言
在基于C#的WinForm+SQL應(yīng)用開(kāi)發(fā)中,應(yīng)用程序與數(shù)據(jù)庫(kù)之間存在大量數(shù)據(jù)操作,這些操作通常通過(guò)ADO.NET實(shí)現(xiàn)。ADO.NET是.NET框架中的一個(gè)組件庫(kù),在技術(shù)上高于現(xiàn)有的API。ADO.NET分為數(shù)據(jù)提供程序和數(shù)據(jù)集兩個(gè)部分,實(shí)現(xiàn)了數(shù)據(jù)訪問(wèn)和數(shù)據(jù)操作的分離。其數(shù)據(jù)操作一般過(guò)程為:創(chuàng)建連接對(duì)象,打開(kāi)與數(shù)據(jù)庫(kù)的連接,通過(guò)Command類(lèi)對(duì)象選擇數(shù)據(jù)后把它們放到記錄集中,接著處理并在服務(wù)器上更新這些數(shù)據(jù),最后關(guān)閉它們。在該過(guò)程中,ADO.NET所提供的基礎(chǔ)方式是將實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作的T-SQL語(yǔ)句以字符串的形式直接編寫(xiě)在C#代碼中,這種方式難以滿足性能優(yōu)化、安全性和易維護(hù)的開(kāi)發(fā)要求。除了進(jìn)行SQL語(yǔ)句的性能調(diào)優(yōu),借助SQL Server的存儲(chǔ)過(guò)程完成數(shù)據(jù)庫(kù)操作是解決上述問(wèn)題的重要方法。
1 SqlCommand對(duì)象
在ADO.NET中,對(duì)應(yīng)于SQL Server提供程序的SqlCommand類(lèi)對(duì)象負(fù)責(zé)執(zhí)行對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作的各種命令,包括SELECT、INSERT、UPDATE、DELETE等,一般過(guò)程為:①創(chuàng)建SqlCommand類(lèi)對(duì)象;②將數(shù)據(jù)庫(kù)操作對(duì)應(yīng)的SQL語(yǔ)句指定給該對(duì)象的CommandText屬性;③將該對(duì)象綁定到一個(gè)打開(kāi)的數(shù)據(jù)庫(kù)連接對(duì)象;④調(diào)用該對(duì)象的相應(yīng)執(zhí)行方法或?qū)⒃搶?duì)象綁定到DataAdapter完成數(shù)據(jù)操作。
例如:在基于C#開(kāi)發(fā)的成人高等教育畢業(yè)生審核系統(tǒng)中,為了將成教畢業(yè)生申報(bào)數(shù)據(jù)與學(xué)信網(wǎng)的學(xué)生學(xué)籍?dāng)?shù)據(jù)進(jìn)行一致性比對(duì),對(duì)畢業(yè)生申報(bào)數(shù)據(jù)表與學(xué)信網(wǎng)的在校生學(xué)籍?dāng)?shù)據(jù)表進(jìn)行了一個(gè)左外連接操作,代碼如下:
DataTable checkTable=new DataTable();
SqlCommand checkConsistencyCmd=new SqlCommand(@"SELECT * FROM (SELECT ksh,xh,cm,xb,csrq,sfzh,zydm,zymc,cc,xxxs,xz,rxrq,yjbyrq,bysxjcf,xjjyt,jc FROM bmdr WHERE xjzt='在校') AS t1 LEFT OUTER JOIN(SELECT ksh,xh,xm,xb,csrq,sfzh,zydm,zymc,cc,xxxs,xz FROM zxs)as t2 ON t1.ksh=t2.ksh",adultEduConn);
SqlDataAdapter checkAda=new SqlDataAdapter(checkConsistencyCmd);
try
{
checkAda.Fill(checkTable);
}
catch(Exception ex){……}
該方式是ADO.NET提供的操作數(shù)據(jù)庫(kù)基本方式,在存在大量T-SQL語(yǔ)句需要逐條執(zhí)行的情況下至少存在以下問(wèn)題:語(yǔ)句的逐條編譯執(zhí)行會(huì)降低執(zhí)行速度,大量T-SQL語(yǔ)句的傳輸會(huì)占用一定的網(wǎng)絡(luò)通信量,由于T-SQL語(yǔ)句是以文本的方式嵌入C#代碼中的,其修改完全依賴于對(duì)應(yīng)用程序的修改。
2 存儲(chǔ)過(guò)程及應(yīng)用
(1)調(diào)用存儲(chǔ)過(guò)程進(jìn)行數(shù)據(jù)庫(kù)操作。 ADO.NET的命令對(duì)象可以調(diào)用存儲(chǔ)過(guò)程完成SQL數(shù)據(jù)庫(kù)的操作。存儲(chǔ)過(guò)程建立在SQL服務(wù)器上,是一組預(yù)先編譯好的T-SQL語(yǔ)句,由用戶通過(guò)指定存儲(chǔ)過(guò)程的名字執(zhí)行。
存儲(chǔ)過(guò)程調(diào)用與上述通過(guò)命令字符串的方式進(jìn)行數(shù)據(jù)庫(kù)操作在使用過(guò)程上的差異在于,前者需要事先在SQL服務(wù)器上創(chuàng)建存儲(chǔ)過(guò)程,然后在SqlCommand類(lèi)的對(duì)象上綁定存儲(chǔ)過(guò)程并將對(duì)象的CommandType屬性指派為“StoreProcedure”。
例如:由于成人教育的特殊性,成人在校生可能存在多個(gè)學(xué)籍,為了統(tǒng)計(jì)出每個(gè)畢業(yè)生是否存在重復(fù)學(xué)籍,在SQL服務(wù)器上編寫(xiě)如下存儲(chǔ)過(guò)程:
create procedure chkXJCF with encryption as
begin
UPDATE bmdr SET bysxjcf=t2.d FROM (SELECT ksh AS c,t1.b AS d FROM bmdr,(SELECT sfzh AS a,count(sfzh) AS b FROM bmdr GROUP BY sfzh) AS t1 WHERE bmdr.sfzh=t1.a) AS t2 WHERE bmdr.ksh=t2.c
UPDATE bmdr SET zxsxjcf=t2.d FROM (SELECT ksh AS c,t1.b AS d FROM bmdr,(SELECT sfzh AS a,count(sfzh) AS b FROM zxs GROUP BY sfzh) AS t1 WHERE bmdr.sfzh=t1.a) AS t2 WHERE bmdr.ksh=t2.c
end
為了調(diào)用該存儲(chǔ)過(guò)程,在C#程序中使用如下代碼:
SqlCommand chkCMD=new SqlCommand(@"chkXJCF",adultEduConn);
chkCMD.CommandType=CommandType.StoreProcedure;
try{
aduleEduConn.Open();
chkCMD.ExecuteNonQuery();
}
catch(Exception ex){……}
與通過(guò)命令字符串方式進(jìn)行數(shù)據(jù)庫(kù)操作相比,存儲(chǔ)過(guò)程調(diào)用好處有:存儲(chǔ)過(guò)程在第一次調(diào)用后就駐留在內(nèi)存中,不需要二次編譯和優(yōu)化,提升了執(zhí)行性能,且由于存儲(chǔ)過(guò)程通過(guò)指定的名字執(zhí)行,降低了網(wǎng)絡(luò)通信量;存儲(chǔ)過(guò)程存在于SQL服務(wù)器上,完全獨(dú)立于應(yīng)用程序,即使出現(xiàn)修改需求也不影響應(yīng)用程序;存儲(chǔ)過(guò)程可以通過(guò)授權(quán)提供更高的安全性。
(2)利用存儲(chǔ)過(guò)程實(shí)現(xiàn)參數(shù)化查詢。 存儲(chǔ)過(guò)程可以攜帶參數(shù),并通過(guò)指定參數(shù)的方向完成應(yīng)用程序中的參數(shù)化查詢。由于參數(shù)是類(lèi)型化的替代占位符,而非未經(jīng)檢查的純文本,因此可以有效防范類(lèi)似于SQL注入之類(lèi)的攻擊,提高安全性。除了在SQL服務(wù)器上正確編寫(xiě)帶參數(shù)的存儲(chǔ)過(guò)程外,還需要為SqlCommand對(duì)象增加參數(shù),以完成參數(shù)的傳遞。
例如:為了將往屆未通過(guò)審核的成教畢業(yè)生數(shù)據(jù)追加到本屆待審核數(shù)據(jù)中,需要通過(guò)姓名查找未通過(guò)審核畢業(yè)生數(shù)據(jù)表并排除該姓名在本屆待審核數(shù)據(jù)中的存在,存儲(chǔ)過(guò)程就需要用學(xué)生的姓名作為一個(gè)輸入?yún)?shù)完成查詢。建立存儲(chǔ)過(guò)程代碼如下:
create procedure joinwith_xm (@para char(40)) with encryption as
begin
select RTRIM(ksh) AS ksh,RTRIM(xh) AS xh,RTRIM(xm) AS xm,RTRIM(xb) AS xb, sfzh, RTRIM(zydm) AS zydm, RTRIM(zymc) AS zymc, RTRIM(cc) AS cc, RTRIM(xxxs) AS xxxs,RTRIM(xz) AS xz from WBY where xm=@para and ksh not in (select ksh from bmdr where xm=@para)
……
end
為了完成該存儲(chǔ)過(guò)程的調(diào)用,在使用SqlCommand對(duì)象時(shí),除了對(duì)象的CommandType屬性指派為“StoreProcedure”外,還應(yīng)創(chuàng)建相關(guān)參數(shù),并將它們附加到SqlCommand對(duì)象上:
SqlCommand joinCmd=new SqlCommand("joinwith_xm",adultEduConn);
joinCmd.CommandType=CommandType.StoreProcedure;
joinCmd.Parameters.AddWithValue("@para",searchPanel.GetXM);
SqlDataAdapter joinAda=new SqlDataAdapter(joinCmd);
try{
joinAda.Fill(joinTable);
}
catch(Exception ex){……}
3 結(jié)語(yǔ)
在基于C#的WinForm+SQL數(shù)據(jù)庫(kù)應(yīng)用開(kāi)發(fā)中合理使用存儲(chǔ)過(guò)程,能有效將應(yīng)用實(shí)現(xiàn)與數(shù)據(jù)操作分離,提高運(yùn)行效率,提高數(shù)據(jù)庫(kù)訪問(wèn)的安全性并增強(qiáng)系統(tǒng)的靈活性。
參考文獻(xiàn):
[1] WILDERMUTH S. ADO.NET應(yīng)用程序開(kāi)發(fā)(MCTS教程)[M].北京:清華大學(xué)出版社,2010.
[2] NAGEL C. C#高級(jí)編程 [M].第9版.北京:清華大學(xué)出版社,2014.
[3] BEN-GAN I. SQL Server 2012 T-SQL基礎(chǔ)教程[M].北京:人民郵電出版社,2013.
[4] HAMILTON B. ADO.NET3.5經(jīng)典實(shí)例[M].第2版.北京:機(jī)械出版社,2009.
[5] DEITEL P J, DEITEL H M. C# 2008程序員教程[M].第3版.北京:電子工業(yè)出版社,2009.
[6] CLARKE J. SQL注入攻擊與防御[M].北京:清華大學(xué)出版社,2013.
[7] FRITCHEY G, DAM S. SQL Server 2008查詢性能優(yōu)化[M].北京:人民郵電出版社,2010.
[8] JORGENSEN A. SQL Server 2012管理高級(jí)教程[M].第2版.北京:清華大學(xué)出版社,2011.
[9] MICHAELIS M. C#本質(zhì)論[M].第2版.北京:人民郵電出版社,2009.
[10] BROWN E. Windows Forms編程實(shí)戰(zhàn)[M].北京:機(jī)械工業(yè)出版社,2008.
(責(zé)任編輯:杜能鋼)