.NET对存储过程的调用抽象封装

最近一边参与公司的项目开发,一边还肩负着基础库的创建和维护。真真切切的体会到写框架的不容易,写出好的,方便使用的框架更不容易,需要考虑的东西太多,需要掌握的东西太多。不过不要紧我们正在前进的道路上。同志们一起加油!

最近在使用存储过程的时候总觉得有点麻烦,尽管在前期对ORM和统一数据源接口封装已经下了很多功夫,对IDataParameter之类的接口已经进行了很好的封装,但是还是觉得麻烦。 [王清培版权所有,转载请给出署名]

经过与DBA的沟通,他认为对存储过程的封装是有必要的,以他十几年的经验看,存储过程后期的移植是必不可少的。现在的项目是用SQLSERVER2008开发的,后期可能会移植到ORACLE上去,那么对存储过程的编写DBA考虑很周全。但是对于程序员来说,经验稍微丰富点的可能会通过某种工厂将具体对象脱耦,或者使用依赖倒置的原则来解决更换数据源问题。但是考虑到统一的使用方法,这里还是真的有必要进行封装的。那么如何封装?

代码生成器的重要性
这里为什么要牵扯到代码生成器呢?从我刚开始准备编写基础库的时候我就意识到代码生成器的重要性,当时的想法就是能为了完全的控制代码生成器。如果使用第三方的代码生成器可能在初期是可以满足要求,但是如果想把它做成成熟的开发平台是行不通的。借助代码生成器的功能,基础库的使用将变的更加流畅(后面将看到效果)。很明显的就是ORM和一些IDE中内置的代码生成,结合的很完美,让人痴迷。 [王清培版权所有,转载请给出署名]
 
我们假设没有代码生成器,那些诸如通用的代码将需要我们程序员手动的敲,那个工作即枯燥也容易出错。所以需要代码生成器去帮我们生成我们想要的,符合某种规则的重复性的代码。比较典型的就是我们三层架构中必不可少的Model集合(有个概念要纠正一下,常常有程序员将Model对象集读成Model层,它并非层中的“层”,而是层中传递数据的结构)。
有了自己公司的代码生成器之后,就可以按照公司的开发框架去不断的增强功能,使其逐渐变成开发平台,成熟了之后可能就是一套符合行业要求的快速开发框架。这也是个慢慢积累的过程,急不来。
 
存储过程的使用分析
我假设我们已经对IDataParameter对象进行了封装,我想对它简单的封装基本也都能满足日常要求了。一般都是根据当前项目链接数据库的类型字符串进行判断,然后生成相对应如:SqlParameter、OracleParameter、 OleDbParameter等等,可能还包括一些开源的数据库扩展框架中的对象,这里的创建工厂可能是抽象工厂,当然方法很多种,实现效果就行。 [王清培版权所有,转载请给出署名]
对其简单的封装我们在使用的时候需要使用工厂方法创建IDataParameter数组,如:
 
 
 
  1. Dictionary<stringobject> parameter = new Dictionary<stringobject>();  
  2. parameter.Add("PurchaseID", Purchase.TempSerialNo);//单据流水号   
  3. parameter.Add("WarehouseId", Purchase.InWarehouseID);//仓库ID  
  4. parameter.Add("UserID", Purchase.UserID);//操作ID  
  5. parameter.Add("UserName", Purchase.UserName);//操作人名称  
  6. parameter.Add("PurchaseDate", DBNull.Value);//采购日期  
  7. parameter.Add("BuyUserID", DBNull.Value);//采购人编号  
  8. parameter.Add("BuyUserName", DBNull.Value);//采购人名称  
  9. parameter.Add("BuyDate", DBNull.Value);//采购日期  
  10. parameter.Add("Memo", Purchase.Memo);//备注说明  
  11. IDataParameter[] parameterDic = IDataParameterFactory.CreateDbDataParameter(parameter);  
  12.  List<IDataParameter> listparameter = IDataParameterHelper.IDataParameterArrayToList(parameterDic);  
  13. listparameter.Add(WL.DAL.DAL_TB_WLPurchase.GetErrIDParametere());  
  14. using (Fast.Orm.IDataSourceOperation operation = Fast.Orm.IDataSourceOperationFactory.Create())  
  15. {  
  16. operation.ExecuteNonQuery(CommandType.StoredProcedure, "prc_WLPurchaseTmpAdd", listparameter.ToArray());  
  17. if (listparameter[listparameter.Count - 1].Value.ToString() == "0")  
  18. return true;  
  19. return false;  
  20. }  

 

一般性的封装基本都这样或者在IDataParameterFactory.CreateDbDataParameter(Entity)中加入根据实体的属性动态的创建IDataParameter[]对象,如果你的创建始终是使用反射的话那么将是不可取的。有兴趣的朋友可以参见本人的另一篇文章“利用抽象、多态实现无反射的绿色环保ORM框架”对实体的使用如果不能摆脱反射,那么在以后的基础库扩展中将面临着很多性能问题,这里需三思。

由于很少存储过程的参数名称都是对应的实体的属性名称,这种对应关系很难做到,或者说是做到的话需要 DBA花点时间呢,在命名上也是个约束。
如果存储过程有 N个参数的话我们需要对照数据库设计文档来编写IDictionary项,在一般的项目中都将复杂的业务逻辑封装在存储过程中实现,所以存储过程的数量也是不少的。这样一来也算是一个比较浪费时间的工作。
那么如果减少编码量,让存储过程的调用变的简单,而且对用户来说是透明的?
 
抽象存储过程的参数使其变成参数实体抽象
由于在设计绿色ORM的过程中总结了很多好的想法,也确实能感觉到对简单实体的抽象能使后期的扩展变的更加自如。比如,不需要那么费力的使用反射获取属性元数据,直接使用字典集合就能得到属性的名称和值。那么我也使用类似的设计思路来设计了参数实体对象。
首先需要抽象的基类,用来保存对存储过程的一个简单的对应关系,请看代码:
 
 
  1. /// <summary>  
  2.     /// 存储过程实体(参数信息类)基类  
  3.     /// </summary>  
  4.     public abstract class BaseStoredprocedureObject : DictionaryBase  
  5.     {  
  6.         /// <summary>  
  7.         /// 受保护的字段-存储过程名称  
  8.         /// </summary>  
  9.         protected string procedurename = string.Empty;  
  10.         /// <summary>  
  11.         /// 受保护的字段-命令参数集合  
  12.         /// </summary>  
  13.         protected List<IDataParameter> parameterlist = new List<IDataParameter>();  
  14.         /// <summary>  
  15.         /// 获取命令参数集合  
  16.         /// </summary>  
  17.         public List<IDataParameter> ParameterList  
  18.         {  
  19.             get { return parameterlist; }  
  20.         }  
  21.         /// <summary>  
  22.         /// 添加IDataParameter对象到基类parameterlist对象  
  23.         /// </summary>  
  24.         public abstract void AddParameterToBaseParameterObject();  
  25.         /// <summary>  
  26.         /// 获取存储过程名称  
  27.         /// </summary>  
  28.         public string ProcedureName  
  29.         {  
  30.             get { return procedurename; }  
  31.         }  
  32.         /// <summary>  
  33.         /// 获取对应参数名称的值  
  34.         /// </summary>  
  35.         /// <param name="keyname">参数名称</param>  
  36.         /// <returns>object:参数值</returns>  
  37.         public object this[string keyname]  
  38.         {  
  39.             get { return this.Dictionary[keyname]; }  
  40.             internal set { this.Dictionary[keyname] = value; }  
  41.         }  
  42.         /// <summary>  
  43.         /// 获取所有参数信息  
  44.         /// </summary>  
  45.         public IDictionary GetProcedureEntity  
  46.         {  
  47.             get { return this.Dictionary; }  
  48.         }  
  49.         /// <summary>  
  50.         /// 存储过程返回的数据集  
  51.         /// </summary>  
  52.         public DataTable Source  
  53.         {  
  54.             get;  
  55.             internal set;  
  56.         }  
  57. }  
具体的属性都有注释,我就不解释了。可能这个对象在初期也是比较简单的,随着使用范围的变大或者使用复杂,那么这个类还需要其他的东西。
这是抽象的对象,那么在具体的子类当中是如何的呢?还是看代码比较直观;
 
 
 
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Data;  
  5. using Fast.Orm;  
  6.  
  7. namespace Fast.WL.Parmeter  
  8. {  
  9.     [Serializable()]  
  10.     public class Init_prc_WLOrderTmpAdd : BaseStoredprocedureObject  
  11.     {  
  12.         public Init_prc_WLOrderTmpAdd()  
  13.         {  
  14.             this.procedurename = "prc_WLOrderTmpAdd";  
  15.             this.Dictionary.Add("OrderID"null);  
  16.             this.Dictionary.Add("StationID"null);  
  17.             this.Dictionary.Add("UserID"null);  
  18.             this.Dictionary.Add("UserName"null);  
  19.             this.Dictionary.Add("OrderDate"null);  
  20.             this.Dictionary.Add("DeliveryAddress"null);  
  21.             this.Dictionary.Add("OrderType"null);  
  22.             this.Dictionary.Add("APNumber"null);  
  23.             this.Dictionary.Add("Memo"null);  
  24.             this.Dictionary.Add("ErrID", DBNull.Value);  
  25.         }  
  26.         public override void AddParameterToBaseParameterObject()  
  27.         {  
  28.             base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(  
  29. "OrderID"base.Dictionary["OrderID"],  
  30. ParameterDirection.Input, DbType.String, 14));  
  31.             base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(  
  32. "StationID"base.Dictionary["StationID"],  
  33. ParameterDirection.Input, DbType.String, 36));  
  34.             base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(  
  35. "UserID"base.Dictionary["UserID"],  
  36. ParameterDirection.Input, DbType.String, 36));  
  37.             base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(  
  38. "UserName"base.Dictionary["UserName"],  
  39. ParameterDirection.Input, DbType.String, 10));  
  40.             base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(  
  41. "OrderDate"base.Dictionary["OrderDate"],  
  42. ParameterDirection.Input, DbType.DateTime, 8));  
  43.             base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(  
  44. "DeliveryAddress"base.Dictionary["DeliveryAddress"],  
  45. ParameterDirection.Input, DbType.String, 50));  
  46.             base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(  
  47. "OrderType"base.Dictionary["OrderType"],  
  48. ParameterDirection.Input, DbType.Int16, 2));  
  49.             base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(  
  50. "APNumber"base.Dictionary["APNumber"],  
  51. ParameterDirection.Input, DbType.String, 20));  
  52.             base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(  
  53. "Memo"base.Dictionary["Memo"],  
  54. ParameterDirection.Input, DbType.String, 220));  
  55.             base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(  
  56. "ErrID"base.Dictionary["ErrID"],  
  57. ParameterDirection.Output, DbType.Int32, 4));  
  58.         }  
  59.     }  
  60.     public class prc_WLOrderTmpAdd : Init_prc_WLOrderTmpAdd  
  61.     {  
  62.         public object OrderID  
  63.         {  
  64.             get { return this.Dictionary["OrderID"as object; }  
  65.             set { this.Dictionary["OrderID"] = value; }  
  66.         }  
  67.         public object StationID  
  68.         {  
  69.             get { return this.Dictionary["StationID"as object; }  
  70.             set { this.Dictionary["StationID"] = value; }  
  71.         }  
  72.         public object UserID  
  73.         {  
  74.             get { return this.Dictionary["UserID"as object; }  
  75.             set { this.Dictionary["UserID"] = value; }  
  76.         }  
  77.         public object UserName  
  78.         {  
  79.             get { return this.Dictionary["UserName"as object; }  
  80.             set { this.Dictionary["UserName"] = value; }  
  81.         }  
  82.         public object OrderDate  
  83.         {  
  84.             get { return this.Dictionary["OrderDate"as object; }  
  85.             set { this.Dictionary["OrderDate"] = value; }  
  86.         }  
  87.         public object DeliveryAddress  
  88.         {  
  89.             get { return this.Dictionary["DeliveryAddress"as object; }  
  90.             set { this.Dictionary["DeliveryAddress"] = value; }  
  91.         }  
  92.         public object OrderType  
  93.         {  
  94.             get { return this.Dictionary["OrderType"as object; }  
  95.             set { this.Dictionary["OrderType"] = value; }  
  96.         }  
  97.         public object APNumber  
  98.         {  
  99.             get { return this.Dictionary["APNumber"as object; }  
  100.             set { this.Dictionary["APNumber"] = value; }  
  101.         }  
  102.         public object Memo  
  103.         {  
  104.             get { return this.Dictionary["Memo"as object; }  
  105.             set { this.Dictionary["Memo"] = value; }  
  106.         }  
  107.         public object ErrID  
  108.         {  
  109.             get { return this.Dictionary["ErrID"as object; }  
  110.             set { this.Dictionary["ErrID"] = value; }  
  111.         }  
  112.     }  
  113. }  

在Init_prc_WLOrderTmpAdd构造函数中我们设置所有的参数名称和默认的值,这里可以会是DbNull.Value。[王清培版权所有,转载请给出署名]

在 AddParameterToBaseParameterObject重写方法中我们用来创建所有的IDataParameter对象的具体实例,由于不同的参数名称,不同的数据类型,不同的输入输出。所以我们需要这么一个创建IDataParameter对象的工厂方法,这个方法应该在前期就已经存在了,这里我假设它已经被创建了。
那么在使用的时候我们不需要关心太多的细节,只需要对将该对象当作执行存储过程的参数对象即可。
 
 
 
  1. prc_WLOrderTmpAdd ordertmp = new prc_WLOrderTmpAdd();  
  2.                 ordertmp.OrderID = order.OrderID;//订单流水号  
  3.                 ordertmp.StationID = order.StationID;//站点ID  
  4.                 ordertmp.UserID = order.UserID;  
  5.                 ordertmp.UserName = order.UserName;  
  6.                 ordertmp.OrderDate = DBNull.Value;  
  7.                 ordertmp.DeliveryAddress = order.DeliveryAddress;  
  8.                 ordertmp.OrderType = order.OrderType;  
  9.                 ordertmp.APNumber = string.IsNullOrEmpty(order.APNumber) ? DBNull.Value : (object)order.APNumber;  
  10.                 ordertmp.Memo = DBNull.Value;//备注  
  11.                 Fast.Orm.ProcedureHelper.ProcedureOperation<prc_WLOrderTmpAdd>(ordertmp);  
  12.                 return int.Parse(ordertmp.ErrID.ToString()) == 0 ? true : false;  

这样保证我们写的代码都围绕着数据实体来进行数据库的操作。

只需要封装一个简单的执行存储过程的方法就行了。
Fast.Orm.ProcedureHelper.ProcedureOperation<prc_WLOrderTmpAdd>(ordertmp);
这里我就不贴出代码了。
使用上是变的简单了,那么数据实体怎么来呢?这里就是我文章开头小结讲到的,代码生成器对基础框架的重要性。 [王清培版权所有,转载请给出署名]
 

 

有了专业的代码生成器之后,一切就变的简单多了,我们按照自己的要求设计开发代码生成器来配合基础框架的使用,那么我们的开发效率将大大提高了。

 总结:这里只是本人在封装存储过程的使用时的一些小小的经验,与大家分享一下,也算是一个抛砖引玉吧,可能大面积的使用会存在点未知的问题,不过框架就是这样才变的稳定的,希望对大家有点用,谢谢。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值