.NET對存儲過程的調用抽象封裝


最近一邊參與公司的項目開發,一邊還肩負着基礎庫的創建和維護。真真切切的體會到寫框架的不容易,寫出好的,方便使用的框架更不容易,需要考慮的東西太多,需要掌握的東西太多。不過不要緊我們正在前進的道路上。同志們一起加油!

最近在使用存儲過程的時候總覺得有點麻煩,盡管在前期對ORM和統一數據源接口封裝已經下了很多功夫,對IDataParameter之類的接口已經進行了很好的封裝,但是還是覺得麻煩。 [王清培版權所有,轉載請給出署名]

經過與DBA的溝通,他認為對存儲過程的封裝是有必要的,以他十幾年的經驗看,存儲過程后期的移植是必不可少的。現在的項目是用SQLSERVER2008開發的,后期可能會移植到ORACLE上去,那么對存儲過程的編寫DBA考慮很周全。但是對於程序員來說,經驗稍微豐富點的可能會通過某種工廠將具體對象脫耦,或者使用依賴倒置的原則來解決更換數據源問題。但是考慮到統一的使用方法,這里還是真的有必要進行封裝的。那么如何封裝?

代碼生成器的重要性
這里為什么要牽扯到代碼生成器呢?從我剛開始准備編寫基礎庫的時候我就意識到代碼生成器的重要性,當時的想法就是能為了完全的控制代碼生成器。如果使用第三方的代碼生成器可能在初期是可以滿足要求,但是如果想把它做成成熟的開發平台是行不通的。借助代碼生成器的功能,基礎庫的使用將變的更加流暢(后面將看到效果)。很明顯的就是ORM和一些IDE中內置的代碼生成,結合的很完美,讓人痴迷。 [王清培版權所有,轉載請給出署名]
 
我們假設沒有代碼生成器,那些諸如通用的代碼將需要我們程序員手動的敲,那個工作即枯燥也容易出錯。所以需要代碼生成器去幫我們生成我們想要的,符合某種規則的重復性的代碼。比較典型的就是我們三層架構中必不可少的Model集合(有個概念要糾正一下,常常有程序員將Model對象集讀成Model層,它並非層中的“層”,而是層中傳遞數據的結構)。
有了自己公司的代碼生成器之后,就可以按照公司的開發框架去不斷的增強功能,使其逐漸變成開發平台,成熟了之后可能就是一套符合行業要求的快速開發框架。這也是個慢慢積累的過程,急不來。
 
存儲過程的使用分析
我假設我們已經對IDataParameter對象進行了封裝,我想對它簡單的封裝基本也都能滿足日常要求了。一般都是根據當前項目鏈接數據庫的類型字符串進行判斷,然后生成相對應如:SqlParameter、OracleParameter、 OleDbParameter等等,可能還包括一些開源的數據庫擴展框架中的對象,這里的創建工廠可能是抽象工廠,當然方法很多種,實現效果就行。 [王清培版權所有,轉載請給出署名]
對其簡單的封裝我們在使用的時候需要使用工廠方法創建IDataParameter數組,如:
Dictionary<string, object> parameter = new Dictionary<string, object>();
parameter.Add("PurchaseID", Purchase.TempSerialNo);//單據流水號 
parameter.Add("WarehouseId", Purchase.InWarehouseID);//倉庫ID
parameter.Add("UserID", Purchase.UserID);//操作ID
parameter.Add("UserName", Purchase.UserName);//操作人名稱
parameter.Add("PurchaseDate", DBNull.Value);//采購日期
parameter.Add("BuyUserID", DBNull.Value);//采購人編號
parameter.Add("BuyUserName", DBNull.Value);//采購人名稱
parameter.Add("BuyDate", DBNull.Value);//采購日期
parameter.Add("Memo", Purchase.Memo);//備注說明
IDataParameter[] parameterDic = IDataParameterFactory.CreateDbDataParameter(parameter);
 List<IDataParameter> listparameter = IDataParameterHelper.IDataParameterArrayToList(parameterDic);
listparameter.Add(WL.DAL.DAL_TB_WLPurchase.GetErrIDParametere());
using (Fast.Orm.IDataSourceOperation operation = Fast.Orm.IDataSourceOperationFactory.Create())
{
operation.ExecuteNonQuery(CommandType.StoredProcedure, "prc_WLPurchaseTmpAdd", listparameter.ToArray());
if (listparameter[listparameter.Count - 1].Value.ToString() == "0")
return true;
return false;
}

  

一般性的封裝基本都這樣或者在IDataParameterFactory.CreateDbDataParameter(Entity)中加入根據實體的屬性動態的創建IDataParameter[]對象,如果你的創建始終是使用反射的話那么將是不可取的。有興趣的朋友可以參見本人的另一篇文章“利用抽象、多態實現無反射的綠色環保ORM框架”對實體的使用如果不能擺脫反射,那么在以后的基礎庫擴展中將面臨着很多性能問題,這里需三思。

由於很少存儲過程的參數名稱都是對應的實體的屬性名稱,這種對應關系很難做到,或者說是做到的話需要 DBA花點時間呢,在命名上也是個約束。
如果存儲過程有 N個參數的話我們需要對照數據庫設計文檔來編寫IDictionary項,在一般的項目中都將復雜的業務邏輯封裝在存儲過程中實現,所以存儲過程的數量也是不少的。這樣一來也算是一個比較浪費時間的工作。
那么如果減少編碼量,讓存儲過程的調用變的簡單,而且對用戶來說是透明的?
 
抽象存儲過程的參數使其變成參數實體抽象
由於在設計綠色ORM的過程中總結了很多好的想法,也確實能感覺到對簡單實體的抽象能使后期的擴展變的更加自如。比如,不需要那么費力的使用反射獲取屬性元數據,直接使用字典集合就能得到屬性的名稱和值。那么我也使用類似的設計思路來設計了參數實體對象。
首先需要抽象的基類,用來保存對存儲過程的一個簡單的對應關系,請看代碼:
/// <summary>
    /// 存儲過程實體(參數信息類)基類
    /// </summary>
    public abstract class BaseStoredprocedureObject : DictionaryBase
    {
        /// <summary>
        /// 受保護的字段-存儲過程名稱
        /// </summary>
        protected string procedurename = string.Empty;
        /// <summary>
        /// 受保護的字段-命令參數集合
        /// </summary>
        protected List<IDataParameter> parameterlist = new List<IDataParameter>();
        /// <summary>
        /// 獲取命令參數集合
        /// </summary>
        public List<IDataParameter> ParameterList
        {
            get { return parameterlist; }
        }
        /// <summary>
        /// 添加IDataParameter對象到基類parameterlist對象
        /// </summary>
        public abstract void AddParameterToBaseParameterObject();
        /// <summary>
        /// 獲取存儲過程名稱
        /// </summary>
        public string ProcedureName
        {
            get { return procedurename; }
        }
        /// <summary>
        /// 獲取對應參數名稱的值
        /// </summary>
        /// <param name="keyname">參數名稱</param>
        /// <returns>object:參數值</returns>
        public object this[string keyname]
        {
            get { return this.Dictionary[keyname]; }
            internal set { this.Dictionary[keyname] = value; }
        }
        /// <summary>
        /// 獲取所有參數信息
        /// </summary>
        public IDictionary GetProcedureEntity
        {
            get { return this.Dictionary; }
        }
        /// <summary>
        /// 存儲過程返回的數據集
        /// </summary>
        public DataTable Source
        {
            get;
            internal set;
        }
}

  

具體的屬性都有注釋,我就不解釋了。可能這個對象在初期也是比較簡單的,隨着使用范圍的變大或者使用復雜,那么這個類還需要其他的東西。
這是抽象的對象,那么在具體的子類當中是如何的呢?還是看代碼比較直觀;
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Fast.Orm;

namespace Fast.WL.Parmeter
{
    [Serializable()]
    public class Init_prc_WLOrderTmpAdd : BaseStoredprocedureObject
    {
        public Init_prc_WLOrderTmpAdd()
        {
            this.procedurename = "prc_WLOrderTmpAdd";
            this.Dictionary.Add("OrderID", null);
            this.Dictionary.Add("StationID", null);
            this.Dictionary.Add("UserID", null);
            this.Dictionary.Add("UserName", null);
            this.Dictionary.Add("OrderDate", null);
            this.Dictionary.Add("DeliveryAddress", null);
            this.Dictionary.Add("OrderType", null);
            this.Dictionary.Add("APNumber", null);
            this.Dictionary.Add("Memo", null);
            this.Dictionary.Add("ErrID", DBNull.Value);
        }
        public override void AddParameterToBaseParameterObject()
        {
            base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
"OrderID", base.Dictionary["OrderID"],
ParameterDirection.Input, DbType.String, 14));
            base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
"StationID", base.Dictionary["StationID"],
ParameterDirection.Input, DbType.String, 36));
            base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
"UserID", base.Dictionary["UserID"],
ParameterDirection.Input, DbType.String, 36));
            base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
"UserName", base.Dictionary["UserName"],
ParameterDirection.Input, DbType.String, 10));
            base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
"OrderDate", base.Dictionary["OrderDate"],
ParameterDirection.Input, DbType.DateTime, 8));
            base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
"DeliveryAddress", base.Dictionary["DeliveryAddress"],
ParameterDirection.Input, DbType.String, 50));
            base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
"OrderType", base.Dictionary["OrderType"],
ParameterDirection.Input, DbType.Int16, 2));
            base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
"APNumber", base.Dictionary["APNumber"],
ParameterDirection.Input, DbType.String, 20));
            base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
"Memo", base.Dictionary["Memo"],
ParameterDirection.Input, DbType.String, 220));
            base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
"ErrID", base.Dictionary["ErrID"],
ParameterDirection.Output, DbType.Int32, 4));
        }
    }
    public class prc_WLOrderTmpAdd : Init_prc_WLOrderTmpAdd
    {
        public object OrderID
        {
            get { return this.Dictionary["OrderID"] as object; }
            set { this.Dictionary["OrderID"] = value; }
        }
        public object StationID
        {
            get { return this.Dictionary["StationID"] as object; }
            set { this.Dictionary["StationID"] = value; }
        }
        public object UserID
        {
            get { return this.Dictionary["UserID"] as object; }
            set { this.Dictionary["UserID"] = value; }
        }
        public object UserName
        {
            get { return this.Dictionary["UserName"] as object; }
            set { this.Dictionary["UserName"] = value; }
        }
        public object OrderDate
        {
            get { return this.Dictionary["OrderDate"] as object; }
            set { this.Dictionary["OrderDate"] = value; }
        }
        public object DeliveryAddress
        {
            get { return this.Dictionary["DeliveryAddress"] as object; }
            set { this.Dictionary["DeliveryAddress"] = value; }
        }
        public object OrderType
        {
            get { return this.Dictionary["OrderType"] as object; }
            set { this.Dictionary["OrderType"] = value; }
        }
        public object APNumber
        {
            get { return this.Dictionary["APNumber"] as object; }
            set { this.Dictionary["APNumber"] = value; }
        }
        public object Memo
        {
            get { return this.Dictionary["Memo"] as object; }
            set { this.Dictionary["Memo"] = value; }
        }
        public object ErrID
        {
            get { return this.Dictionary["ErrID"] as object; }
            set { this.Dictionary["ErrID"] = value; }
        }
    }
}

  

在Init_prc_WLOrderTmpAdd構造函數中我們設置所有的參數名稱和默認的值,這里可以會是DbNull.Value。[王清培版權所有,轉載請給出署名]

AddParameterToBaseParameterObject重寫方法中我們用來創建所有的IDataParameter對象的具體實例,由於不同的參數名稱,不同的數據類型,不同的輸入輸出。所以我們需要這么一個創建IDataParameter對象的工廠方法,這個方法應該在前期就已經存在了,這里我假設它已經被創建了。
那么在使用的時候我們不需要關心太多的細節,只需要對將該對象當作執行存儲過程的參數對象即可。
prc_WLOrderTmpAdd ordertmp = new prc_WLOrderTmpAdd();
                ordertmp.OrderID = order.OrderID;//訂單流水號
                ordertmp.StationID = order.StationID;//站點ID
                ordertmp.UserID = order.UserID;
                ordertmp.UserName = order.UserName;
                ordertmp.OrderDate = DBNull.Value;
                ordertmp.DeliveryAddress = order.DeliveryAddress;
                ordertmp.OrderType = order.OrderType;
                ordertmp.APNumber = string.IsNullOrEmpty(order.APNumber) ? DBNull.Value : (object)order.APNumber;
                ordertmp.Memo = DBNull.Value;//備注
                Fast.Orm.ProcedureHelper.ProcedureOperation<prc_WLOrderTmpAdd>(ordertmp);
                return int.Parse(ordertmp.ErrID.ToString()) == 0 ? true : false;

  

這樣保證我們寫的代碼都圍繞着數據實體來進行數據庫的操作。

只需要封裝一個簡單的執行存儲過程的方法就行了。
Fast.Orm.ProcedureHelper.ProcedureOperation<prc_WLOrderTmpAdd>(ordertmp);
這里我就不貼出代碼了。
使用上是變的簡單了,那么數據實體怎么來呢?這里就是我文章開頭小結講到的,代碼生成器對基礎框架的重要性。 [王清培版權所有,轉載請給出署名]

有了專業的代碼生成器之后,一切就變的簡單多了,我們按照自己的要求設計開發代碼生成器來配合基礎框架的使用,那么我們的開發效率將大大提高了。

總結:這里只是本人在封裝存儲過程的使用時的一些小小的經驗,與大家分享一下,也算是一個拋磚引玉吧,可能大面積的使用會存在點未知的問題,不過框架就是這樣才變的穩定的,希望對大家有點用,謝謝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM