本篇繼續(xù)上篇《Winform開發(fā)框架之存儲過程的支持--存儲過程的實(shí)現(xiàn)和演化提煉(1)》來對Winform開發(fā)框架之存儲過程的支持進(jìn)行介紹,上篇主要介紹了SQLServer和Oracle兩種數(shù)據(jù)庫對常規(guī)存儲過程的編寫和對比,本篇主要介紹如何在C#里面,如何對這些存儲過程進(jìn)行調(diào)用,并獲取到對應(yīng)的數(shù)據(jù)類型,如輸出參數(shù),單個數(shù)據(jù)記錄,多個數(shù)據(jù)記錄等情況。最后在完成實(shí)現(xiàn)功能的基礎(chǔ)上,對這些實(shí)現(xiàn)進(jìn)行演化提煉,并擴(kuò)展到我的WInform開發(fā)框架里面,實(shí)現(xiàn)功能重用、代碼簡化的目的。
1、數(shù)據(jù)訪問接口的定義
我們整個實(shí)例是以一個客戶表T_Customer為例進(jìn)行講解的,整個表的框架支持代碼,可以通過代碼生成工具進(jìn)行快速生成,生成后包括了IDAL、Entity、DALSQL、BLL層代碼,然后可以利用代碼進(jìn)行測試存儲過程是否執(zhí)行成功等功能。
數(shù)據(jù)訪問層的定義,依照框架的分層模式來處理,后面我們在增加DALOracle對Oracle數(shù)據(jù)庫進(jìn)行支持即可。
生成后數(shù)據(jù)訪問層接口,他們通過基類接口繼承的方式,已經(jīng)具有了常規(guī)的增刪改查、分頁等系列接口,但是其他業(yè)務(wù)接口還是需要自己定義的,如數(shù)據(jù)訪問接口成的定義如下所示。
namespace WHC.TestProject.IDAL
{
/// <summary>
/// 客戶信息
/// </summary>
public interface ICustomer : IBaseDAL<CustomerInfo>
{
}
}
這里面的代碼很簡單,沒有多余的代碼行,那么里面究竟發(fā)生了什么呢,其中的IBaseDAL又是什么定義呢?

其實(shí),IBaseDAL就是定義了很多我們開發(fā)用到的基礎(chǔ)接口,如標(biāo)準(zhǔn)的增刪改查,以及衍生出來的一些其他接口,如分頁查詢,條件查詢等接口內(nèi)容。這個ICustomer就是用來定義一些除了標(biāo)準(zhǔn)接口不能實(shí)現(xiàn)外的業(yè)務(wù)接口。
如果我們需要實(shí)現(xiàn)基于存儲過程的接口,我們可能就需要增加一些接口定義,如下所示。
namespace WHC.TestProject.IDAL
{
/// <summary>
/// 客戶信息
/// </summary>
public interface ICustomer : IBaseDAL<CustomerInfo>
{
#region 使用存儲過程
/// <summary>
/// 使用存儲過程插入數(shù)據(jù)
/// </summary>
/// <param name="info">實(shí)體對象</param>
/// <returns></returns>
bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null);
/// <summary>
/// 使用存儲過程更新數(shù)據(jù)
/// </summary>
/// <param name="info">實(shí)體對象</param>
/// <returns></returns>
bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null);
/// <summary>
/// 使用存儲過程獲取所有數(shù)據(jù)
/// </summary>
/// <returns></returns>
List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null);
/// <summary>
/// 使用存儲過程獲取所有數(shù)據(jù)
/// </summary>
/// <returns></returns>
DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null);
/// <summary>
/// 使用存儲過程,根據(jù)ID獲取對應(yīng)記錄
/// </summary>
/// <param name="ID"></param>
/// <returns></returns>
CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null);
/// <summary>
/// 使用存儲過程,判斷記錄ID是否存在
/// </summary>
/// <param name="ID">記錄ID</param>
/// <returns></returns>
bool StorePorc_ExistByID(string ID, DbTransaction trans = null);
/// <summary>
/// 使用存儲過程,根據(jù)ID刪除對應(yīng)記錄
/// </summary>
/// <param name="ID">記錄ID</param>
/// <returns></returns>
bool StorePorc_DeleteByID(string ID, DbTransaction trans = null);
/// <summary>
/// 獲取客戶的最大年齡
/// </summary>
/// <returns></returns>
int StorePorc_GetMaxAge();
#endregion
}
對于插入、更新和刪除這樣的操作,我們只需要返回它是否成功就可以了,那么它的接口實(shí)現(xiàn)應(yīng)該是如何的呢?
2、SqlServer存儲過程的調(diào)用實(shí)現(xiàn)
由于我們的Winform開發(fā)框架底層是利用微軟企業(yè)庫EnterpriseLibrary來訪問數(shù)據(jù)的,那么對應(yīng)這個企業(yè)庫的使用存儲過程的方法,也就是我們的實(shí)現(xiàn)了。
下面的代碼就是它們對應(yīng)的SqlServer實(shí)現(xiàn)了。
public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null)
{
string procName = "T_Customer_Insert";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
db.AddInParameter(command, "@ID", DbType.String, info.ID);
db.AddInParameter(command, "@Name", DbType.String, info.Name);
db.AddInParameter(command, "@Age", DbType.Int32, info.Age);
bool result = false;
if (trans != null)
{
result = db.ExecuteNonQuery(command, trans) > 0;
}
else
{
result = db.ExecuteNonQuery(command) > 0;
}
return result;
}
public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null)
{
string procName = "T_Customer_UpdateByID";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
db.AddInParameter(command, "@ID", DbType.String, info.ID);
db.AddInParameter(command, "@Name", DbType.String, info.Name);
db.AddInParameter(command, "@Age", DbType.Int32, info.Age);
bool result = false;
if (trans != null)
{
result = db.ExecuteNonQuery(command, trans) > 0;
}
else
{
result = db.ExecuteNonQuery(command) > 0;
}
return result;
}
public bool StorePorc_DeleteByID(string ID, DbTransaction trans = null)
{
string procName = "T_Customer_DeleteByID";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
db.AddInParameter(command, "@ID", DbType.String, ID);
bool result = false;
if (trans != null)
{
result = db.ExecuteNonQuery(command, trans) > 0;
}
else
{
result = db.ExecuteNonQuery(command) > 0;
}
return result;
}
對于有返回輸出參數(shù)的值,我們的做法有些不同,不過最主要的還是最終獲取它的輸出參數(shù)值而已,代碼如下所示。
public bool StorePorc_ExistByID(string ID, DbTransaction trans = null)
{
bool result = false;
string procName = "T_Customer_ExistByID";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
db.AddInParameter(command, "@ID", DbType.String, ID);
db.AddOutParameter(command, "@Exist", DbType.Int32, 0);//輸出參數(shù)
if (trans != null)
{
db.ExecuteNonQuery(command, trans);
}
else
{
db.ExecuteNonQuery(command);
}
int iExist = 0;
int.TryParse(db.GetParameterValue(command, "@Exist").ToString(), out iExist);
result = iExist > 0;
return result;
}
public int StorePorc_GetMaxAge()
{
string procName = "T_Customer_MaxAge";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
db.AddOutParameter(command, "@MaxAge", DbType.Int32, 0);//輸出參數(shù)
db.ExecuteNonQuery(command);
int maxAge = 0;
int.TryParse(db.GetParameterValue(command, "@MaxAge").ToString(), out maxAge);
return maxAge;
}
上面的代碼,主要就是利用了AddOutParameter對輸出參數(shù)的信息進(jìn)行設(shè)置,輸出參數(shù)的數(shù)據(jù)類型要和腳本里面的類型定義對應(yīng),它的AddOutParameter的size參數(shù)值,可以為0。
最后我們通過db.GetParameterValue(command, "@MaxAge")的方式獲取它的輸出參數(shù)的值,并返回即可。
最后一個例子是介紹如何通過代碼調(diào)用,獲得它的實(shí)體對象或者實(shí)體對象列表,以及DataTable集合對象的例子了,這個也相對不是很麻煩,參照框架里面的做法即可。
獲取實(shí)體對象信息的代碼如下所示。
public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null)
{
string procName = "T_Customer_SelectByID";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
db.AddInParameter(command, "@ID", DbType.String, ID);
CustomerInfo entity = null;
if (trans != null)
{
using (IDataReader dr = db.ExecuteReader(command, trans))
{
if (dr.Read())
{
entity = DataReaderToEntity(dr);
}
}
}
else
{
using (IDataReader dr = db.ExecuteReader(command))
{
if (dr.Read())
{
entity = DataReaderToEntity(dr);
}
}
}
return entity;
}
獲取集合的代碼如下所示。
public List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null)
{
string procName = "T_Customer_SelectAll";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
List<CustomerInfo> list = new List<CustomerInfo>();
CustomerInfo entity = null;
if (trans != null)
{
using (IDataReader dr = db.ExecuteReader(command, trans))
{
while (dr.Read())
{
entity = DataReaderToEntity(dr);
list.Add(entity);
}
}
}
else
{
using (IDataReader dr = db.ExecuteReader(command))
{
while (dr.Read())
{
entity = DataReaderToEntity(dr);
list.Add(entity);
}
}
}
return list;
}
public DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null)
{
string procName = "T_Customer_SelectAll";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
DataTable dt = null;
if (trans != null)
{
dt = db.ExecuteDataSet(command, trans).Tables[0];
}
else
{
dt = db.ExecuteDataSet(command).Tables[0];
}
return dt;
}
3、Oracle存儲過程的調(diào)用實(shí)現(xiàn)
上面是基于SqlServer存儲過程的調(diào)用,前面的一篇文章我們介紹了存儲過程的Oracle定義,是增加了一個游標(biāo)來進(jìn)行記錄行數(shù)據(jù)的處理的,不管對于單行記錄,還是多行記錄,都是用了游標(biāo)的輸出參數(shù)的,那么在客戶端里面,使用EnterpriseLibrary,應(yīng)該如何調(diào)用,并且不需要傳入這個輸出參數(shù)的呢,做法其實(shí)很類似,只是有一點(diǎn)差異而已。
我們先從最簡單的Oracle存儲過程調(diào)用案例開始,介紹如何調(diào)用插入、更新和刪除操作的Oracle存儲過程的調(diào)用。這里和SqlServer的類似,不同的是我們使用了p_前綴來定義參數(shù)(基于Oracle的通用腳本參數(shù)定義規(guī)則)。
public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null)
{
string procName = "T_Customer_Insert";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
db.AddInParameter(command, "p_ID", DbType.String, info.ID);
db.AddInParameter(command, "p_Name", DbType.String, info.Name);
db.AddInParameter(command, "p_Age", DbType.Int32, info.Age);
bool result = false;
if (trans != null)
{
result = db.ExecuteNonQuery(command, trans) > 0;
}
else
{
result = db.ExecuteNonQuery(command) > 0;
}
return result;
}
public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null)
{
string procName = "T_Customer_UpdateByID";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
db.AddInParameter(command, "p_ID", DbType.String, info.ID);
db.AddInParameter(command, "p_Name", DbType.String, info.Name);
db.AddInParameter(command, "p_Age", DbType.Int32, info.Age);
bool result = false;
if (trans != null)
{
result = db.ExecuteNonQuery(command, trans) > 0;
}
else
{
result = db.ExecuteNonQuery(command) > 0;
}
return result;
}
Oracle輸出外部參數(shù)的做法也和sqlServer類似,具體調(diào)用代碼如下所示。
public int StorePorc_GetMaxAge()
{
string procName = "T_Customer_MaxAge";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
db.AddOutParameter(command, "p_MaxAge", DbType.Int32, 0);//輸出參數(shù)
db.ExecuteNonQuery(command);
int maxAge = 0;
int.TryParse(db.GetParameterValue(command, "p_MaxAge").ToString(), out maxAge);
return maxAge;
}
其他的也就很類似,就不再一一贅述了,基本上和SqlServer的一致,我們節(jié)省篇幅,用來看看如何調(diào)用返回記錄的查詢接口。下面是對應(yīng)的Oracle存儲過程的調(diào)用代碼
public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null)
{
string procName = "T_Customer_SelectByID";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
db.AddInParameter(command, "p_ID", DbType.String, ID);
CustomerInfo entity = null;
if (trans != null)
{
using (IDataReader dr = db.ExecuteReader(command, trans))
{
if (dr.Read())
{
entity = DataReaderToEntity(dr);
}
}
}
else
{
using (IDataReader dr = db.ExecuteReader(command))
{
if (dr.Read())
{
entity = DataReaderToEntity(dr);
}
}
}
return entity;
}
返回多條記錄的操作代碼如下所示。
public List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null)
{
string procName = "T_Customer_SelectAll";
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(procName);
List<CustomerInfo> list = new List<CustomerInfo>();
CustomerInfo entity = null;
if (trans != null)
{
using (IDataReader dr = db.ExecuteReader(command, trans))
{
while (dr.Read())
{
entity = DataReaderToEntity(dr);
list.Add(entity);
}
}
}
else
{
using (IDataReader dr = db.ExecuteReader(command))
{
while (dr.Read())
{
entity = DataReaderToEntity(dr);
list.Add(entity);
}
}
}
return list;
}
看完上面兩個對記錄處理的接口,我們看到,還是對我們在Oracle存儲過程里面定義的輸出游標(biāo)參數(shù)忽略處理,我們不需要對它進(jìn)行傳值,它好像是透明的,呵呵。
這樣它的做法就和SqlServer個各個接口實(shí)現(xiàn)也都差不多的了。
下面的腳本是我們之前定義的Oracle存儲過程腳本,方便對比參照一下調(diào)用的函數(shù)代碼。
------------------------------------
--作者:伍華聰 http://wuhuacong.cnblogs.com
--創(chuàng)建時間:2014年11月27日
--功能描述:以字段ID為關(guān)鍵字,檢索表中的數(shù)據(jù)
------------------------------------
Create Or Replace Procedure T_Customer_SelectByID
(
cur_OUT OUT MyCURSOR.cur_OUT ,
p_ID IN T_CUSTOMER.ID%TYPE
)
AS
Begin
OPEN cur_OUT FOR Select * from T_CUSTOMER Where ID = p_ID ;
End;
/
4、業(yè)務(wù)邏輯層的實(shí)現(xiàn)
上面我們定義了數(shù)據(jù)訪問接口,以及兩種數(shù)據(jù)實(shí)現(xiàn)層,在框架里面會根據(jù)不同的數(shù)據(jù)庫類型配置,然后從不同的數(shù)據(jù)庫訪問層構(gòu)建對象的,業(yè)務(wù)邏輯層主要就是對他們的接口進(jìn)行調(diào)用了,具體代碼如下所示。
/// <summary>
/// 客戶信息
/// </summary>
public class Customer : BaseBLL<CustomerInfo>
{
public Customer() : base()
{
base.Init(this.GetType().FullName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name);
}
/// <summary>
/// 根據(jù)客戶名稱獲取客戶列表
/// </summary>
/// <param name="name">客戶名稱</param>
/// <returns></returns>
public List<CustomerInfo> FindByName(string name)
{
string condition = string.Format("Name like '%{0}%' ", name);
return baseDal.Find(condition);
}
#region 使用存儲過程
public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null)
{
ICustomer dal = baseDal as ICustomer;
return dal.StorePorc_Insert(info, trans);
}
public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null)
{
ICustomer dal = baseDal as ICustomer;
return dal.StorePorc_Update(info, trans);
}
public List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null)
{
ICustomer dal = baseDal as ICustomer;
return dal.StorePorc_GetAll(trans);
}
public DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null)
{
ICustomer dal = baseDal as ICustomer;
return dal.StorePorc_GetAllToDataTable(trans);
}
public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null)
{
ICustomer dal = baseDal as ICustomer;
return dal.StorePorc_FindByID(ID, trans);
}
public bool StorePorc_ExistByID(string ID, DbTransaction trans = null)
{
ICustomer dal = baseDal as ICustomer;
return dal.StorePorc_ExistByID(ID, trans);
}
public bool StorePorc_DeleteByID(string ID, DbTransaction trans = null)
{
ICustomer dal = baseDal as ICustomer;
return dal.StorePorc_DeleteByID(ID, trans);
}
public int StorePorc_GetMaxAge()
{
ICustomer dal = baseDal as ICustomer;
return dal.StorePorc_GetMaxAge();
}
#endregion
}
為了驗(yàn)證我們的實(shí)現(xiàn)是否能夠正常處理,并順利獲取對應(yīng)的對象或者集合,我們需要編寫一些代碼,用來對它進(jìn)行測試。
測試的代碼如下所示。
/// <summary>
/// 測試存儲過程的插入、修改、返回實(shí)體類、返回實(shí)體類集合、返回DataTable對象、輸出參數(shù)等接口
/// </summary>
private void btnTestStoreProc_Click(object sender, EventArgs e)
{
//定義一個實(shí)體類的數(shù)據(jù)
CustomerInfo info = new CustomerInfo();
info.Name = "測試名稱";
info.Age = 20;
//調(diào)用存儲過程插入數(shù)據(jù),并判斷是否成功
bool inserted = BLLFactory<Customer>.Instance.StorePorc_Insert(info);
Debug.Assert(inserted);
//調(diào)用存儲過程,獲取輸出參數(shù),獲得最大年齡值
int maxAge = BLLFactory<Customer>.Instance.StorePorc_GetMaxAge();
Debug.Assert(maxAge > 0);
//調(diào)用存儲過程,修改客戶名稱
info.Name = "修改名稱";
bool updated = BLLFactory<Customer>.Instance.StorePorc_Update(info);
//調(diào)用存儲過程,獲取最新的實(shí)體類對象,并對比是否修改成功
CustomerInfo newInfo = BLLFactory<Customer>.Instance.StorePorc_FindByID(info.ID);
Debug.Assert(newInfo != null);
Debug.Assert(newInfo.Name == info.Name);
//調(diào)用存儲過程,獲取輸出參數(shù),判斷指定ID記錄是否存在
bool exist = BLLFactory<Customer>.Instance.StorePorc_ExistByID(info.ID);
Debug.Assert(exist);
//調(diào)用存儲過程,獲取全部實(shí)體列表集合,判斷實(shí)體類列表是否正確
List<CustomerInfo> list = BLLFactory<Customer>.Instance.StorePorc_GetAll();
Debug.Assert(list.Count > 0);
//調(diào)用存儲過程,獲取DataTable對象,判斷集合不為空
DataTable dt = BLLFactory<Customer>.Instance.StorePorc_GetAllToDataTable();
Debug.Assert(dt.Rows.Count > 0);
//調(diào)用存儲過程,執(zhí)行刪除操作,并判斷是否成功了
bool deleted = BLLFactory<Customer>.Instance.StorePorc_DeleteByID(info.ID);
Debug.Assert(deleted);
string result = "全部操作完成";
Console.WriteLine(result);
MessageUtil.ShowTips(result);
}
5、具體測試和驗(yàn)證
為了對他們進(jìn)行測試,我們需要分別對SqlServer和Oracle進(jìn)行測試,然后才能確認(rèn)我們的實(shí)現(xiàn)是正確的。
分別在SQLServer和Oracle上運(yùn)行存儲過程腳本,創(chuàng)建對應(yīng)的數(shù)據(jù)庫腳本,如下所示。


測試Winform小程序,會得到成功的標(biāo)志,標(biāo)識所有的斷言全部通過。

6、框架基類的演化提煉
本來寫到上面小節(jié),應(yīng)該就可以告一段落了,因?yàn)楣δ芤惨呀?jīng)完成了,而且還是支持了兩種不同的數(shù)據(jù)庫,說明我們的實(shí)現(xiàn)和原先的想法都是正確的。
但是,我從來不喜歡臃腫的代碼,我們留心回頭看看前面的代碼,兩種不同數(shù)據(jù)庫的實(shí)現(xiàn)很多是相似的,即使對于同一個數(shù)據(jù)庫(如SQLServer)的存儲過程接口實(shí)現(xiàn),他們還是有很多優(yōu)化的地方,代碼依舊不夠精簡和優(yōu)化,本小節(jié)就是專門針對這些進(jìn)行提煉和優(yōu)化的。
前面的框架介紹文章,我們可以了解到,數(shù)據(jù)訪問接口實(shí)現(xiàn)層和接口定義層一樣,都有一個基類,如基于SqlServer實(shí)現(xiàn)的基類為BaseDALSQL,這個基于SqlServer的數(shù)據(jù)訪問基類,它也是繼承自一個超級基類(大多數(shù)的實(shí)現(xiàn)在這里)AbstractBaseDAL。他們之間的繼承關(guān)系如下所示,最終我們把提煉好的內(nèi)容,放到這個AbstractBaseDAL就可以了,這樣各個子類都可以進(jìn)行調(diào)用,實(shí)現(xiàn)存儲過程的處理。

對于存儲過程的實(shí)現(xiàn),我們分析一下各個接口,可以看到,輸入?yún)?shù)是可選的,因?yàn)橛行┙涌诓恍枰敵鰠?shù);輸出參數(shù)也是可選的,有些接口也不需要輸出參數(shù),返回的記錄類型主要有bool類型,實(shí)體類型,實(shí)體集合類型,DataTable類型這幾種,當(dāng)然雖然有年齡接口的整形,但是這個是通過輸出參數(shù)來獲得的。
我們于是可以定義一個類似這樣的通用接口參數(shù)集合,用來處理需要返回是否成功獲取帶有輸出參數(shù)的,事務(wù)對象的接口,如下所示。
/// <summary>
/// 執(zhí)行存儲過程,如果影響記錄數(shù),返回True,否則為False,修改并輸出外部參數(shù)outParameters(如果有)。
/// </summary>
/// <param name="storeProcName">存儲過程名稱</param>
/// <param name="inParameters">輸入?yún)?shù),可為空</param>
/// <param name="outParameters">輸出參數(shù),可為空</param>
/// <param name="trans">事務(wù)對象,可為空</param>
/// <returns>如果影響記錄數(shù),返回True,否則為False</returns>
public bool StorePorcExecute(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
它的實(shí)現(xiàn)基本上就是分為了幾部分,第一部分是傳入?yún)?shù)值(包括輸入?yún)?shù)、輸出參數(shù)的值),第二部是執(zhí)行存儲過程,三部分是獲得輸出參數(shù)并修改值即可。
具體的實(shí)現(xiàn)代碼如下所示。
/// <summary>
/// 執(zhí)行存儲過程,如果影響記錄數(shù),返回True,否則為False,修改并輸出外部參數(shù)outParameters(如果有)。
/// </summary>
/// <param name="storeProcName">存儲過程名稱</param>
/// <param name="inParameters">輸入?yún)?shù),可為空</param>
/// <param name="outParameters">輸出參數(shù),可為空</param>
/// <param name="trans">事務(wù)對象,可為空</param>
/// <returns>如果影響記錄數(shù),返回True,否則為False</returns>
public bool StorePorcExecute(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
{
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(storeProcName);
//參數(shù)傳入
SetStoreParameters(db, command, inParameters, outParameters);
//獲取執(zhí)行結(jié)果
bool result = false;
if (trans != null)
{
result = db.ExecuteNonQuery(command, trans) > 0;
}
else
{
result = db.ExecuteNonQuery(command) > 0;
}
//獲取輸出參數(shù)的值
EditOutParameters(db, command, outParameters);
return result;
}
上面兩部分紅色哪里,因?yàn)樗麄冊诤芏嗥渌瘮?shù)里面也通用,所以我就抽離作為一個私有函數(shù)了,就是傳入?yún)?shù),和傳出結(jié)果的兩部分。
由于輸入輸出參數(shù)都是可選的,因?yàn)槲覀儾淮_定它是否存在值,所以我們分別對它進(jìn)行了一定的處理,具體兩個函數(shù)的代碼如下所示。
/// <summary>
/// 傳入輸入?yún)?shù)和輸出參數(shù)到Database和DbCommand對象。
/// </summary>
/// <param name="db">Database對象</param>
/// <param name="command">DbCommand對象</param>
/// <param name="inParameters">輸入?yún)?shù)的哈希表</param>
/// <param name="outParameters">輸出參數(shù)的哈希表</param>
private void SetStoreParameters(Database db, DbCommand command, Hashtable inParameters = null, Hashtable outParameters = null)
{
#region 參數(shù)傳入
//傳入輸入?yún)?shù)
if (inParameters != null)
{
foreach (string param in inParameters.Keys)
{
object value = inParameters[param];
db.AddInParameter(command, param, TypeToDbType(value.GetType()), value);
}
}
//傳入輸出參數(shù)
if (outParameters != null)
{
foreach (string param in outParameters.Keys)
{
object value = outParameters[param];
db.AddOutParameter(command, param, TypeToDbType(value.GetType()), 0);//size統(tǒng)一設(shè)置為0
}
}
#endregion
}
/// <summary>
/// 執(zhí)行存儲過程后,獲取需要輸出的參數(shù)值,修改存儲在哈希表里
/// </summary>
/// <param name="db">Database對象</param>
/// <param name="command">DbCommand對象</param>
/// <param name="outParameters">輸出參數(shù)的哈希表</param>
private void EditOutParameters(Database db, DbCommand command, Hashtable outParameters = null)
{
#region 獲取輸出參數(shù)的值
if (outParameters != null)
{
ArrayList keys = new ArrayList(outParameters.Keys);//使用臨時集合對象,避免迭代錯誤
foreach (string param in keys)
{
object retValue = db.GetParameterValue(command, param);
object value = outParameters[param];
outParameters[param] = Convert.ChangeType(retValue, value.GetType());
}
}
#endregion
}
這樣我們就完成了一個普通存儲過程該接口的通用處理了,但是我們知道,還有返回列表對象,列表集合,DataTable對象的幾種不同方式,我們也應(yīng)該要對他們進(jìn)行一定的封裝處理,已達(dá)到在子類能夠很好使用的目的。
下面我把整個對這幾部分封裝的代碼進(jìn)行公布,它們的封裝的代碼如下所示(記得是放在超級抽象類上AbstractBaseDAL即可。
#region 存儲過程執(zhí)行通用方法
/// <summary>
/// 執(zhí)行存儲過程,如果影響記錄數(shù),返回True,否則為False,修改并輸出外部參數(shù)outParameters(如果有)。
/// </summary>
/// <param name="storeProcName">存儲過程名稱</param>
/// <param name="inParameters">輸入?yún)?shù),可為空</param>
/// <param name="outParameters">輸出參數(shù),可為空</param>
/// <param name="trans">事務(wù)對象,可為空</param>
/// <returns>如果影響記錄數(shù),返回True,否則為False</returns>
public bool StorePorcExecute(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
{
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(storeProcName);
//參數(shù)傳入
SetStoreParameters(db, command, inParameters, outParameters);
//獲取執(zhí)行結(jié)果
bool result = false;
if (trans != null)
{
result = db.ExecuteNonQuery(command, trans) > 0;
}
else
{
result = db.ExecuteNonQuery(command) > 0;
}
//獲取輸出參數(shù)的值
EditOutParameters(db, command, outParameters);
return result;
}
/// <summary>
/// 執(zhí)行存儲過程,返回實(shí)體列表集合,修改并輸出外部參數(shù)outParameters(如果有)。
/// </summary>
/// <param name="storeProcName">存儲過程名稱</param>
/// <param name="inParameters">輸入?yún)?shù),可為空</param>
/// <param name="outParameters">輸出參數(shù),可為空</param>
/// <param name="trans">事務(wù)對象,可為空</param>
/// <returns>返回實(shí)體列表集合</returns>
public List<T> StorePorcToList(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
{
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(storeProcName);
//參數(shù)傳入
SetStoreParameters(db, command, inParameters, outParameters);
#region 獲取執(zhí)行結(jié)果
List<T> result = new List<T>();
T entity = null;
if (trans != null)
{
using (IDataReader dr = db.ExecuteReader(command, trans))
{
while (dr.Read())
{
entity = DataReaderToEntity(dr);
result.Add(entity);
}
}
}
else
{
using (IDataReader dr = db.ExecuteReader(command))
{
while (dr.Read())
{
entity = DataReaderToEntity(dr);
result.Add(entity);
}
}
}
#endregion
//獲取輸出參數(shù)的值
EditOutParameters(db, command, outParameters);
return result;
}
/// <summary>
/// 執(zhí)行存儲過程,返回DataTable集合,修改并輸出外部參數(shù)outParameters(如果有)。
/// </summary>
/// <param name="storeProcName">存儲過程名稱</param>
/// <param name="inParameters">輸入?yún)?shù),可為空</param>
/// <param name="outParameters">輸出參數(shù),可為空</param>
/// <param name="trans">事務(wù)對象,可為空</param>
/// <returns>返回DataTable集合</returns>
public DataTable StorePorcToDataTable(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
{
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(storeProcName);
//參數(shù)傳入
SetStoreParameters(db, command, inParameters, outParameters);
#region 獲取執(zhí)行結(jié)果
DataTable result = null;
if (trans != null)
{
result = db.ExecuteDataSet(command, trans).Tables[0];
}
else
{
result = db.ExecuteDataSet(command).Tables[0];
}
if (result != null)
{
result.TableName = "tableName";//增加一個表名稱,防止WCF方式因?yàn)門ableName為空出錯
}
#endregion
//獲取輸出參數(shù)的值
EditOutParameters(db, command, outParameters);
return result;
}
/// <summary>
/// 執(zhí)行存儲過程,返回實(shí)體對象,修改并輸出外部參數(shù)outParameters(如果有)。
/// </summary>
/// <param name="storeProcName">存儲過程名稱</param>
/// <param name="inParameters">輸入?yún)?shù),可為空</param>
/// <param name="outParameters">輸出參數(shù),可為空</param>
/// <param name="trans">事務(wù)對象,可為空</param>
/// <returns>返回實(shí)體對象</returns>
public T StorePorcToEntity(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
{
Database db = CreateDatabase();
DbCommand command = db.GetStoredProcCommand(storeProcName);
//參數(shù)傳入
SetStoreParameters(db, command, inParameters, outParameters);
#region 獲取執(zhí)行結(jié)果
T result = null;
if (trans != null)
{
using (IDataReader dr = db.ExecuteReader(command, trans))
{
if (dr.Read())
{
result = DataReaderToEntity(dr);
}
}
}
else
{
using (IDataReader dr = db.ExecuteReader(command))
{
if (dr.Read())
{
result = DataReaderToEntity(dr);
}
}
}
#endregion
//獲取輸出參數(shù)的值
EditOutParameters(db, command, outParameters);
return result;
}
/// <summary>
/// 傳入輸入?yún)?shù)和輸出參數(shù)到Database和DbCommand對象。
/// </summary>
/// <param name="db">Database對象</param>
/// <param name="command">DbCommand對象</param>
/// <param name="inParameters">輸入?yún)?shù)的哈希表</param>
/// <param name="outParameters">輸出參數(shù)的哈希表</param>
private void SetStoreParameters(Database db, DbCommand command, Hashtable inParameters = null, Hashtable outParameters = null)
{
#region 參數(shù)傳入
//傳入輸入?yún)?shù)
if (inParameters != null)
{
foreach (string param in inParameters.Keys)
{
object value = inParameters[param];
db.AddInParameter(command, param, TypeToDbType(value.GetType()), value);
}
}
//傳入輸出參數(shù)
if (outParameters != null)
{
foreach (string param in outParameters.Keys)
{
object value = outParameters[param];
db.AddOutParameter(command, param, TypeToDbType(value.GetType()), 0);//size統(tǒng)一設(shè)置為0
}
}
#endregion
}
/// <summary>
/// 執(zhí)行存儲過程后,獲取需要輸出的參數(shù)值,修改存儲在哈希表里
/// </summary>
/// <param name="db">Database對象</param>
/// <param name="command">DbCommand對象</param>
/// <param name="outParameters">輸出參數(shù)的哈希表</param>
private void EditOutParameters(Database db, DbCommand command, Hashtable outParameters = null)
{
#region 獲取輸出參數(shù)的值
if (outParameters != null)
{
ArrayList keys = new ArrayList(outParameters.Keys);//使用臨時集合對象,避免迭代錯誤
foreach (string param in keys)
{
object retValue = db.GetParameterValue(command, param);
object value = outParameters[param];
outParameters[param] = Convert.ChangeType(retValue, value.GetType());
}
}
#endregion
}
#endregion
封裝好這些超級基類后,我們在數(shù)據(jù)訪問層里面,就可以很好地簡化對存儲過程的調(diào)用了,而且他們的做法都很類似,我們可以對比一下,它們調(diào)用存儲過程的實(shí)現(xiàn)真正簡化了很多。
例如對于SqlServer數(shù)據(jù)訪問層,使用超級基類的接口,我們簡化代碼如下所示。
public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null)
{
Hashtable inParameters = new Hashtable();
inParameters.Add("ID", info.ID);
inParameters.Add("Name", info.Name);
inParameters.Add("Age", info.Age);
return StorePorcExecute("T_Customer_Insert", inParameters, null, trans);
}
public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null)
{
Hashtable inParameters = new Hashtable();
inParameters.Add("ID", info.ID);
inParameters.Add("Name", info.Name);
inParameters.Add("Age", info.Age);
return StorePorcExecute("T_Customer_UpdateByID", inParameters, null, trans);
}
public List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null)
{
return StorePorcToList("T_Customer_SelectAll", null, null, trans);
}
public DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null)
{
return StorePorcToDataTable("T_Customer_SelectAll", null, null, trans);
}
public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null)
{
Hashtable inParameters = new Hashtable();
inParameters.Add("ID", ID);
return StorePorcToEntity("T_Customer_SelectByID", inParameters, null, trans);
}
public bool StorePorc_ExistByID(string ID, DbTransaction trans = null)
{
Hashtable inParameters = new Hashtable();
inParameters.Add("ID", ID);
Hashtable outParameters = new Hashtable();
outParameters.Add("Exist", 0);
StorePorcExecute("T_Customer_ExistByID", inParameters, outParameters, trans);
int exist = (int)outParameters["Exist"];
return exist > 0;
}
public bool StorePorc_DeleteByID(string ID, DbTransaction trans = null)
{
Hashtable inParameters = new Hashtable();
inParameters.Add("ID", ID);
return StorePorcExecute("T_Customer_DeleteByID", inParameters, null, trans);
}
public int StorePorc_GetMaxAge()
{
Hashtable outParameters = new Hashtable();
outParameters.Add("MaxAge", 0);
StorePorcExecute("T_Customer_MaxAge", null, outParameters, null);
int MaxAge = (int)outParameters["MaxAge"];
return MaxAge;
}
對于Oracle數(shù)據(jù)訪問層的實(shí)現(xiàn)來說,它的接口實(shí)現(xiàn)一樣簡單,只是參數(shù)命名有所不同而已。
public bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null)
{
Hashtable inParameters = new Hashtable();
inParameters.Add("p_ID", info.ID);
inParameters.Add("p_Name", info.Name);
inParameters.Add("p_Age", info.Age);
return StorePorcExecute("T_Customer_Insert", inParameters, null, trans);
}
public bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null)
{
Hashtable inParameters = new Hashtable();
inParameters.Add("p_ID", info.ID);
inParameters.Add("p_Name", info.Name);
inParameters.Add("p_Age", info.Age);
return StorePorcExecute("T_Customer_UpdateByID", inParameters, null, trans);
}
public List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null)
{
return StorePorcToList("T_Customer_SelectAll", null, null, trans);
}
public DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null)
{
return StorePorcToDataTable("T_Customer_SelectAll", null, null, trans);
}
public CustomerInfo StorePorc_FindByID(string ID, DbTransaction trans = null)
{
Hashtable inParameters = new Hashtable();
inParameters.Add("p_ID", ID);
return StorePorcToEntity("T_Customer_SelectByID", inParameters, null, trans);
}
public bool StorePorc_ExistByID(string ID, DbTransaction trans = null)
{
Hashtable inParameters = new Hashtable();
inParameters.Add("p_ID", ID);
Hashtable outParameters = new Hashtable();
outParameters.Add("p_Exist", 0);
StorePorcExecute("T_Customer_ExistByID", inParameters, outParameters, trans);
int exist = (int)outParameters["p_Exist"];
return exist > 0;
}
public bool StorePorc_DeleteByID(string ID, DbTransaction trans = null)
{
Hashtable inParameters = new Hashtable();
inParameters.Add("p_ID", ID);
return StorePorcExecute("T_Customer_DeleteByID", inParameters, null, trans);
}
public int StorePorc_GetMaxAge()
{
Hashtable outParameters = new Hashtable();
outParameters.Add("p_MaxAge", 0);
StorePorcExecute("T_Customer_MaxAge", null, outParameters, null);
int MaxAge = (int)outParameters["p_MaxAge"];
return MaxAge;
}
以上就是我針對《Winform開發(fā)框架之存儲過程的支持--存儲過程的實(shí)現(xiàn)和演化提煉》這個主題進(jìn)行的介紹和分析,希望對大家有所幫助,也希望結(jié)合我的框架,迅速開發(fā)各種不同的項(xiàng)目。
文章內(nèi)容有點(diǎn)長,感謝您的耐心閱讀和支持。