本篇繼續上篇《Winform開發框架之存儲過程的支持--存儲過程的實現和演化提煉(1)》來對Winform開發框架之存儲過程的支持進行介紹,上篇主要介紹了SQLServer和Oracle兩種數據庫對常規存儲過程的編寫和對比,本篇主要介紹如何在C#里面,如何對這些存儲過程進行調用,並獲取到對應的數據類型,如輸出參數,單個數據記錄,多個數據記錄等情況。最后在完成實現功能的基礎上,對這些實現進行演化提煉,並擴展到我的WInform開發框架里面,實現功能重用、代碼簡化的目的。
1、數據訪問接口的定義
我們整個實例是以一個客戶表T_Customer為例進行講解的,整個表的框架支持代碼,可以通過代碼生成工具進行快速生成,生成后包括了IDAL、Entity、DALSQL、BLL層代碼,然后可以利用代碼進行測試存儲過程是否執行成功等功能。
數據訪問層的定義,依照框架的分層模式來處理,后面我們在增加DALOracle對Oracle數據庫進行支持即可。
生成后數據訪問層接口,他們通過基類接口繼承的方式,已經具有了常規的增刪改查、分頁等系列接口,但是其他業務接口還是需要自己定義的,如數據訪問接口成的定義如下所示。
namespace WHC.TestProject.IDAL { /// <summary> /// 客戶信息 /// </summary> public interface ICustomer : IBaseDAL<CustomerInfo> { } }
這里面的代碼很簡單,沒有多余的代碼行,那么里面究竟發生了什么呢,其中的IBaseDAL又是什么定義呢?
其實,IBaseDAL就是定義了很多我們開發用到的基礎接口,如標准的增刪改查,以及衍生出來的一些其他接口,如分頁查詢,條件查詢等接口內容。這個ICustomer就是用來定義一些除了標准接口不能實現外的業務接口。
如果我們需要實現基於存儲過程的接口,我們可能就需要增加一些接口定義,如下所示。
namespace WHC.TestProject.IDAL { /// <summary> /// 客戶信息 /// </summary> public interface ICustomer : IBaseDAL<CustomerInfo> { #region 使用存儲過程 /// <summary> /// 使用存儲過程插入數據 /// </summary> /// <param name="info">實體對象</param> /// <returns></returns> bool StorePorc_Insert(CustomerInfo info, DbTransaction trans = null); /// <summary> /// 使用存儲過程更新數據 /// </summary> /// <param name="info">實體對象</param> /// <returns></returns> bool StorePorc_Update(CustomerInfo info, DbTransaction trans = null); /// <summary> /// 使用存儲過程獲取所有數據 /// </summary> /// <returns></returns> List<CustomerInfo> StorePorc_GetAll(DbTransaction trans = null); /// <summary> /// 使用存儲過程獲取所有數據 /// </summary> /// <returns></returns> DataTable StorePorc_GetAllToDataTable(DbTransaction trans = null); /// <summary> /// 使用存儲過程,根據ID獲取對應記錄 /// </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> /// 使用存儲過程,根據ID刪除對應記錄 /// </summary> /// <param name="ID">記錄ID</param> /// <returns></returns> bool StorePorc_DeleteByID(string ID, DbTransaction trans = null); /// <summary> /// 獲取客戶的最大年齡 /// </summary> /// <returns></returns> int StorePorc_GetMaxAge(); #endregion }
對於插入、更新和刪除這樣的操作,我們只需要返回它是否成功就可以了,那么它的接口實現應該是如何的呢?
2、SqlServer存儲過程的調用實現
由於我們的Winform開發框架底層是利用微軟企業庫EnterpriseLibrary來訪問數據的,那么對應這個企業庫的使用存儲過程的方法,也就是我們的實現了。
下面的代碼就是它們對應的SqlServer實現了。
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; }
對於有返回輸出參數的值,我們的做法有些不同,不過最主要的還是最終獲取它的輸出參數值而已,代碼如下所示。
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);//輸出參數 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);//輸出參數 db.ExecuteNonQuery(command); int maxAge = 0; int.TryParse(db.GetParameterValue(command, "@MaxAge").ToString(), out maxAge); return maxAge; }
上面的代碼,主要就是利用了AddOutParameter對輸出參數的信息進行設置,輸出參數的數據類型要和腳本里面的類型定義對應,它的AddOutParameter的size參數值,可以為0。
最后我們通過db.GetParameterValue(command, "@MaxAge")的方式獲取它的輸出參數的值,並返回即可。
最后一個例子是介紹如何通過代碼調用,獲得它的實體對象或者實體對象列表,以及DataTable集合對象的例子了,這個也相對不是很麻煩,參照框架里面的做法即可。
獲取實體對象信息的代碼如下所示。
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存儲過程的調用實現
上面是基於SqlServer存儲過程的調用,前面的一篇文章我們介紹了存儲過程的Oracle定義,是增加了一個游標來進行記錄行數據的處理的,不管對於單行記錄,還是多行記錄,都是用了游標的輸出參數的,那么在客戶端里面,使用EnterpriseLibrary,應該如何調用,並且不需要傳入這個輸出參數的呢,做法其實很類似,只是有一點差異而已。
我們先從最簡單的Oracle存儲過程調用案例開始,介紹如何調用插入、更新和刪除操作的Oracle存儲過程的調用。這里和SqlServer的類似,不同的是我們使用了p_前綴來定義參數(基於Oracle的通用腳本參數定義規則)。
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輸出外部參數的做法也和sqlServer類似,具體調用代碼如下所示。
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);//輸出參數 db.ExecuteNonQuery(command); int maxAge = 0; int.TryParse(db.GetParameterValue(command, "p_MaxAge").ToString(), out maxAge); return maxAge; }
其他的也就很類似,就不再一一贅述了,基本上和SqlServer的一致,我們節省篇幅,用來看看如何調用返回記錄的查詢接口。下面是對應的Oracle存儲過程的調用代碼
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存儲過程里面定義的輸出游標參數忽略處理,我們不需要對它進行傳值,它好像是透明的,呵呵。
這樣它的做法就和SqlServer個各個接口實現也都差不多的了。
下面的腳本是我們之前定義的Oracle存儲過程腳本,方便對比參照一下調用的函數代碼。
------------------------------------ --作者:伍華聰 http://wuhuacong.cnblogs.com --創建時間:2014年11月27日 --功能描述:以字段ID為關鍵字,檢索表中的數據 ------------------------------------ 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、業務邏輯層的實現
上面我們定義了數據訪問接口,以及兩種數據實現層,在框架里面會根據不同的數據庫類型配置,然后從不同的數據庫訪問層構建對象的,業務邏輯層主要就是對他們的接口進行調用了,具體代碼如下所示。
/// <summary> /// 客戶信息 /// </summary> public class Customer : BaseBLL<CustomerInfo> { public Customer() : base() { base.Init(this.GetType().FullName, System.Reflection.Assembly.GetExecutingAssembly().GetName().Name); } /// <summary> /// 根據客戶名稱獲取客戶列表 /// </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 }
為了驗證我們的實現是否能夠正常處理,並順利獲取對應的對象或者集合,我們需要編寫一些代碼,用來對它進行測試。
測試的代碼如下所示。
/// <summary> /// 測試存儲過程的插入、修改、返回實體類、返回實體類集合、返回DataTable對象、輸出參數等接口 /// </summary> private void btnTestStoreProc_Click(object sender, EventArgs e) { //定義一個實體類的數據 CustomerInfo info = new CustomerInfo(); info.Name = "測試名稱"; info.Age = 20; //調用存儲過程插入數據,並判斷是否成功 bool inserted = BLLFactory<Customer>.Instance.StorePorc_Insert(info); Debug.Assert(inserted); //調用存儲過程,獲取輸出參數,獲得最大年齡值 int maxAge = BLLFactory<Customer>.Instance.StorePorc_GetMaxAge(); Debug.Assert(maxAge > 0); //調用存儲過程,修改客戶名稱 info.Name = "修改名稱"; bool updated = BLLFactory<Customer>.Instance.StorePorc_Update(info); //調用存儲過程,獲取最新的實體類對象,並對比是否修改成功 CustomerInfo newInfo = BLLFactory<Customer>.Instance.StorePorc_FindByID(info.ID); Debug.Assert(newInfo != null); Debug.Assert(newInfo.Name == info.Name); //調用存儲過程,獲取輸出參數,判斷指定ID記錄是否存在 bool exist = BLLFactory<Customer>.Instance.StorePorc_ExistByID(info.ID); Debug.Assert(exist); //調用存儲過程,獲取全部實體列表集合,判斷實體類列表是否正確 List<CustomerInfo> list = BLLFactory<Customer>.Instance.StorePorc_GetAll(); Debug.Assert(list.Count > 0); //調用存儲過程,獲取DataTable對象,判斷集合不為空 DataTable dt = BLLFactory<Customer>.Instance.StorePorc_GetAllToDataTable(); Debug.Assert(dt.Rows.Count > 0); //調用存儲過程,執行刪除操作,並判斷是否成功了 bool deleted = BLLFactory<Customer>.Instance.StorePorc_DeleteByID(info.ID); Debug.Assert(deleted); string result = "全部操作完成"; Console.WriteLine(result); MessageUtil.ShowTips(result); }
5、具體測試和驗證
為了對他們進行測試,我們需要分別對SqlServer和Oracle進行測試,然后才能確認我們的實現是正確的。
分別在SQLServer和Oracle上運行存儲過程腳本,創建對應的數據庫腳本,如下所示。
測試Winform小程序,會得到成功的標志,標識所有的斷言全部通過。
6、框架基類的演化提煉
本來寫到上面小節,應該就可以告一段落了,因為功能也已經完成了,而且還是支持了兩種不同的數據庫,說明我們的實現和原先的想法都是正確的。
但是,我從來不喜歡臃腫的代碼,我們留心回頭看看前面的代碼,兩種不同數據庫的實現很多是相似的,即使對於同一個數據庫(如SQLServer)的存儲過程接口實現,他們還是有很多優化的地方,代碼依舊不夠精簡和優化,本小節就是專門針對這些進行提煉和優化的。
前面的框架介紹文章,我們可以了解到,數據訪問接口實現層和接口定義層一樣,都有一個基類,如基於SqlServer實現的基類為BaseDALSQL,這個基於SqlServer的數據訪問基類,它也是繼承自一個超級基類(大多數的實現在這里)AbstractBaseDAL。他們之間的繼承關系如下所示,最終我們把提煉好的內容,放到這個AbstractBaseDAL就可以了,這樣各個子類都可以進行調用,實現存儲過程的處理。
對於存儲過程的實現,我們分析一下各個接口,可以看到,輸入參數是可選的,因為有些接口不需要輸出參數;輸出參數也是可選的,有些接口也不需要輸出參數,返回的記錄類型主要有bool類型,實體類型,實體集合類型,DataTable類型這幾種,當然雖然有年齡接口的整形,但是這個是通過輸出參數來獲得的。
我們於是可以定義一個類似這樣的通用接口參數集合,用來處理需要返回是否成功獲取帶有輸出參數的,事務對象的接口,如下所示。
/// <summary> /// 執行存儲過程,如果影響記錄數,返回True,否則為False,修改並輸出外部參數outParameters(如果有)。 /// </summary> /// <param name="storeProcName">存儲過程名稱</param> /// <param name="inParameters">輸入參數,可為空</param> /// <param name="outParameters">輸出參數,可為空</param> /// <param name="trans">事務對象,可為空</param> /// <returns>如果影響記錄數,返回True,否則為False</returns> public bool StorePorcExecute(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null)
它的實現基本上就是分為了幾部分,第一部分是傳入參數值(包括輸入參數、輸出參數的值),第二部是執行存儲過程,三部分是獲得輸出參數並修改值即可。
具體的實現代碼如下所示。
/// <summary> /// 執行存儲過程,如果影響記錄數,返回True,否則為False,修改並輸出外部參數outParameters(如果有)。 /// </summary> /// <param name="storeProcName">存儲過程名稱</param> /// <param name="inParameters">輸入參數,可為空</param> /// <param name="outParameters">輸出參數,可為空</param> /// <param name="trans">事務對象,可為空</param> /// <returns>如果影響記錄數,返回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); //參數傳入 SetStoreParameters(db, command, inParameters, outParameters); //獲取執行結果 bool result = false; if (trans != null) { result = db.ExecuteNonQuery(command, trans) > 0; } else { result = db.ExecuteNonQuery(command) > 0; } //獲取輸出參數的值 EditOutParameters(db, command, outParameters); return result; }
上面兩部分紅色哪里,因為他們在很多其他函數里面也通用,所以我就抽離作為一個私有函數了,就是傳入參數,和傳出結果的兩部分。
由於輸入輸出參數都是可選的,因為我們不確定它是否存在值,所以我們分別對它進行了一定的處理,具體兩個函數的代碼如下所示。
/// <summary> /// 傳入輸入參數和輸出參數到Database和DbCommand對象。 /// </summary> /// <param name="db">Database對象</param> /// <param name="command">DbCommand對象</param> /// <param name="inParameters">輸入參數的哈希表</param> /// <param name="outParameters">輸出參數的哈希表</param> private void SetStoreParameters(Database db, DbCommand command, Hashtable inParameters = null, Hashtable outParameters = null) { #region 參數傳入 //傳入輸入參數 if (inParameters != null) { foreach (string param in inParameters.Keys) { object value = inParameters[param]; db.AddInParameter(command, param, TypeToDbType(value.GetType()), value); } } //傳入輸出參數 if (outParameters != null) { foreach (string param in outParameters.Keys) { object value = outParameters[param]; db.AddOutParameter(command, param, TypeToDbType(value.GetType()), 0);//size統一設置為0 } } #endregion } /// <summary> /// 執行存儲過程后,獲取需要輸出的參數值,修改存儲在哈希表里 /// </summary> /// <param name="db">Database對象</param> /// <param name="command">DbCommand對象</param> /// <param name="outParameters">輸出參數的哈希表</param> private void EditOutParameters(Database db, DbCommand command, Hashtable outParameters = null) { #region 獲取輸出參數的值 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對象的幾種不同方式,我們也應該要對他們進行一定的封裝處理,已達到在子類能夠很好使用的目的。
下面我把整個對這幾部分封裝的代碼進行公布,它們的封裝的代碼如下所示(記得是放在超級抽象類上AbstractBaseDAL即可。
#region 存儲過程執行通用方法 /// <summary> /// 執行存儲過程,如果影響記錄數,返回True,否則為False,修改並輸出外部參數outParameters(如果有)。 /// </summary> /// <param name="storeProcName">存儲過程名稱</param> /// <param name="inParameters">輸入參數,可為空</param> /// <param name="outParameters">輸出參數,可為空</param> /// <param name="trans">事務對象,可為空</param> /// <returns>如果影響記錄數,返回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); //參數傳入 SetStoreParameters(db, command, inParameters, outParameters); //獲取執行結果 bool result = false; if (trans != null) { result = db.ExecuteNonQuery(command, trans) > 0; } else { result = db.ExecuteNonQuery(command) > 0; } //獲取輸出參數的值 EditOutParameters(db, command, outParameters); return result; } /// <summary> /// 執行存儲過程,返回實體列表集合,修改並輸出外部參數outParameters(如果有)。 /// </summary> /// <param name="storeProcName">存儲過程名稱</param> /// <param name="inParameters">輸入參數,可為空</param> /// <param name="outParameters">輸出參數,可為空</param> /// <param name="trans">事務對象,可為空</param> /// <returns>返回實體列表集合</returns> public List<T> StorePorcToList(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null) { Database db = CreateDatabase(); DbCommand command = db.GetStoredProcCommand(storeProcName); //參數傳入 SetStoreParameters(db, command, inParameters, outParameters); #region 獲取執行結果 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 //獲取輸出參數的值 EditOutParameters(db, command, outParameters); return result; } /// <summary> /// 執行存儲過程,返回DataTable集合,修改並輸出外部參數outParameters(如果有)。 /// </summary> /// <param name="storeProcName">存儲過程名稱</param> /// <param name="inParameters">輸入參數,可為空</param> /// <param name="outParameters">輸出參數,可為空</param> /// <param name="trans">事務對象,可為空</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); //參數傳入 SetStoreParameters(db, command, inParameters, outParameters); #region 獲取執行結果 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方式因為TableName為空出錯 } #endregion //獲取輸出參數的值 EditOutParameters(db, command, outParameters); return result; } /// <summary> /// 執行存儲過程,返回實體對象,修改並輸出外部參數outParameters(如果有)。 /// </summary> /// <param name="storeProcName">存儲過程名稱</param> /// <param name="inParameters">輸入參數,可為空</param> /// <param name="outParameters">輸出參數,可為空</param> /// <param name="trans">事務對象,可為空</param> /// <returns>返回實體對象</returns> public T StorePorcToEntity(string storeProcName, Hashtable inParameters = null, Hashtable outParameters = null, DbTransaction trans = null) { Database db = CreateDatabase(); DbCommand command = db.GetStoredProcCommand(storeProcName); //參數傳入 SetStoreParameters(db, command, inParameters, outParameters); #region 獲取執行結果 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 //獲取輸出參數的值 EditOutParameters(db, command, outParameters); return result; } /// <summary> /// 傳入輸入參數和輸出參數到Database和DbCommand對象。 /// </summary> /// <param name="db">Database對象</param> /// <param name="command">DbCommand對象</param> /// <param name="inParameters">輸入參數的哈希表</param> /// <param name="outParameters">輸出參數的哈希表</param> private void SetStoreParameters(Database db, DbCommand command, Hashtable inParameters = null, Hashtable outParameters = null) { #region 參數傳入 //傳入輸入參數 if (inParameters != null) { foreach (string param in inParameters.Keys) { object value = inParameters[param]; db.AddInParameter(command, param, TypeToDbType(value.GetType()), value); } } //傳入輸出參數 if (outParameters != null) { foreach (string param in outParameters.Keys) { object value = outParameters[param]; db.AddOutParameter(command, param, TypeToDbType(value.GetType()), 0);//size統一設置為0 } } #endregion } /// <summary> /// 執行存儲過程后,獲取需要輸出的參數值,修改存儲在哈希表里 /// </summary> /// <param name="db">Database對象</param> /// <param name="command">DbCommand對象</param> /// <param name="outParameters">輸出參數的哈希表</param> private void EditOutParameters(Database db, DbCommand command, Hashtable outParameters = null) { #region 獲取輸出參數的值 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
封裝好這些超級基類后,我們在數據訪問層里面,就可以很好地簡化對存儲過程的調用了,而且他們的做法都很類似,我們可以對比一下,它們調用存儲過程的實現真正簡化了很多。
例如對於SqlServer數據訪問層,使用超級基類的接口,我們簡化代碼如下所示。
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數據訪問層的實現來說,它的接口實現一樣簡單,只是參數命名有所不同而已。
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開發框架之存儲過程的支持--存儲過程的實現和演化提煉》這個主題進行的介紹和分析,希望對大家有所幫助,也希望結合我的框架,迅速開發各種不同的項目。
文章內容有點長,感謝您的耐心閱讀和支持。