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

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

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

代码生成器的重要性
这里为什么要牵扯到代码生成器呢?从我刚开始准备编写基础库的时候我就意识到代码生成器的重要性,当时的想法就是能为了完全的控制代码生成器。如果使用第三方的代码生成器可能在初期是可以满足要求,但是如果想把它做成成熟的开发平台是行不通的。借助代码生成器的功能,基础库的使用将变的更加流畅(后面将看到效果)。很明显的就是ORM和一些IDE中内置的代码生成,结合的很完美,让人痴迷。[王清培版权所有,转载请给出署名]
我们假设没有代码生成器,那些诸如通用的代码将需要我们程序员手动的敲,那个工作即枯燥也容易出错。所以需要代码生成器去帮我们生成我们想要的,符合某种规则的重复性的代码。比较典型的就是我们三层架构中必不可少的Model集合(有个概念要纠正一下,常常有程序员将Model对象集读成Model层,它并非层中的“层”,而是层中传递数据的结构)。
有了自己公司的代码生成器之后,就可以按照公司的开发框架去不断的增强功能,使其逐渐变成开发平台,成熟了之后可能就是一套符合行业要求的快速开发框架。这也是个慢慢积累的过程,急不来。
存储过程的使用分析
我假设我们已经对IDataParameter对象进行了封装,我想对它简单的封装基本也都能满足日常要求了。一般都是根据当前项目链接数据库的类型字符串进行判断,然后生成相对应如:SqlParameter、OracleParameter、OleDbParameter等等,可能还包括一些开源的数据库扩展框架中的对象,这里的创建工厂可能是抽象工厂,当然方法很多种,实现效果就行。[王清培版权所有,转载请给出署名]
对其简单的封装我们在使用的时候需要使用工厂方法创建IDataParameter数组,如:
  1. Dictionary<string, object> parameter = new Dictionary<string, object>();
  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. namespace Fast.WL.Parmeter
  7. {
  8. [Serializable()]
  9. public class Init_prc_WLOrderTmpAdd : BaseStoredprocedureObject
  10. {
  11. public Init_prc_WLOrderTmpAdd()
  12. {
  13. this.procedurename = "prc_WLOrderTmpAdd";
  14. this.Dictionary.Add("OrderID", null);
  15. this.Dictionary.Add("StationID", null);
  16. this.Dictionary.Add("UserID", null);
  17. this.Dictionary.Add("UserName", null);
  18. this.Dictionary.Add("OrderDate", null);
  19. this.Dictionary.Add("DeliveryAddress", null);
  20. this.Dictionary.Add("OrderType", null);
  21. this.Dictionary.Add("APNumber", null);
  22. this.Dictionary.Add("Memo", null);
  23. this.Dictionary.Add("ErrID", DBNull.Value);
  24. }
  25. public override void AddParameterToBaseParameterObject()
  26. {
  27. base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
  28. "OrderID", base.Dictionary["OrderID"],
  29. ParameterDirection.Input, DbType.String, 14));
  30. base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
  31. "StationID", base.Dictionary["StationID"],
  32. ParameterDirection.Input, DbType.String, 36));
  33. base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
  34. "UserID", base.Dictionary["UserID"],
  35. ParameterDirection.Input, DbType.String, 36));
  36. base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
  37. "UserName", base.Dictionary["UserName"],
  38. ParameterDirection.Input, DbType.String, 10));
  39. base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
  40. "OrderDate", base.Dictionary["OrderDate"],
  41. ParameterDirection.Input, DbType.DateTime, 8));
  42. base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
  43. "DeliveryAddress", base.Dictionary["DeliveryAddress"],
  44. ParameterDirection.Input, DbType.String, 50));
  45. base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
  46. "OrderType", base.Dictionary["OrderType"],
  47. ParameterDirection.Input, DbType.Int16, 2));
  48. base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
  49. "APNumber", base.Dictionary["APNumber"],
  50. ParameterDirection.Input, DbType.String, 20));
  51. base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
  52. "Memo", base.Dictionary["Memo"],
  53. ParameterDirection.Input, DbType.String, 220));
  54. base.parameterlist.Add(IDataParameterFactory.CreateDbDataParameter(
  55. "ErrID", base.Dictionary["ErrID"],
  56. ParameterDirection.Output, DbType.Int32, 4));
  57. }
  58. }
  59. public class prc_WLOrderTmpAdd : Init_prc_WLOrderTmpAdd
  60. {
  61. public object OrderID
  62. {
  63. get { return this.Dictionary["OrderID"] as object; }
  64. set { this.Dictionary["OrderID"] = value; }
  65. }
  66. public object StationID
  67. {
  68. get { return this.Dictionary["StationID"] as object; }
  69. set { this.Dictionary["StationID"] = value; }
  70. }
  71. public object UserID
  72. {
  73. get { return this.Dictionary["UserID"] as object; }
  74. set { this.Dictionary["UserID"] = value; }
  75. }
  76. public object UserName
  77. {
  78. get { return this.Dictionary["UserName"] as object; }
  79. set { this.Dictionary["UserName"] = value; }
  80. }
  81. public object OrderDate
  82. {
  83. get { return this.Dictionary["OrderDate"] as object; }
  84. set { this.Dictionary["OrderDate"] = value; }
  85. }
  86. public object DeliveryAddress
  87. {
  88. get { return this.Dictionary["DeliveryAddress"] as object; }
  89. set { this.Dictionary["DeliveryAddress"] = value; }
  90. }
  91. public object OrderType
  92. {
  93. get { return this.Dictionary["OrderType"] as object; }
  94. set { this.Dictionary["OrderType"] = value; }
  95. }
  96. public object APNumber
  97. {
  98. get { return this.Dictionary["APNumber"] as object; }
  99. set { this.Dictionary["APNumber"] = value; }
  100. }
  101. public object Memo
  102. {
  103. get { return this.Dictionary["Memo"] as object; }
  104. set { this.Dictionary["Memo"] = value; }
  105. }
  106. public object ErrID
  107. {
  108. get { return this.Dictionary["ErrID"] as object; }
  109. set { this.Dictionary["ErrID"] = value; }
  110. }
  111. }
  112. }

在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);
这里我就不贴出代码了。
使用上是变的简单了,那么数据实体怎么来呢?这里就是我文章开头小结讲到的,代码生成器对基础框架的重要性。[王清培版权所有,转载请给出署名]

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

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

转载于:https://blog.51cto.com/wangqingpei557/898830

.NET对存储过程的调用抽象封装相关推荐

  1. 写存储过程与调用存储过程

    --创建存储过程的语法 if exists(select * from sysobjects where name='sp_selectStudent')  drop procedure sp_sel ...

  2. oracle java调用存储过程_Java调用Oracle存储过程

    一:无返回值的存储过程 存储过程为: CREATE OR REPLACE PROCEDURE TESTA(PARA1 IN VARCHAR2,PARA2 IN VARCHAR2) AS BEGIN I ...

  3. mysql数据库存储过程及调用方法

    mysql数据库存储过程及调用方法 mysql5.0以后就支持存储过程了,目前mysql的6.0Alpha版也已经推出.6.0不仅支持大型数据库如oracle等的绝大部分功 能,如存储过程.视图.触发 ...

  4. 争议?MySQL存储过程与函数,封装,体,完整详细可收藏

    文章目录 1. 存储过程概述 2. 创建存储过程 3. 调用存储过程 4. 存储函数的使用 5. 存储过程和函数的查看.修改.删除 6. 关于存储过程使用的争议 MySQL从5.0版本开始支持存储过程 ...

  5. C mysql带参数存储过程_C# 调用Mysql 带参数存储过程

    使用C#调用Mysql 带参数的存储过程: 1.创建带参数的存储过程:USP_Temp_Test 2.两个参数:IN 参数为 P_XML , OUT 参数为 P_ErrorOut 3.C#代码调用该存 ...

  6. 第15章_存储过程与函数(创建存储过程、调用存储过程、存储函数的使用、存储过程和函数的查看、修改、删除)

    第15章_存储过程与函数 第15章_存储过程与函数 1. 存储过程概述 1.1 理解 1.2 分类 2. 创建存储过程 2.1 语法分析 2.2 代码举例 3. 调用存储过程 3.1 调用格式 3.2 ...

  7. UML2面向对象分析与设计 -- 面向对象思维(概念、面向对象技术的发展历史、对象和类、面向对象技术的相关原则:抽象 封装 分解 泛化 多态 分层 复用)

    文章目录 1. UML2面向对象分析与设计 学习目标 2. 面向对象思维 2.1 学习目标 2.2 什么是面向对象 2.3 面向对象技术的发展历史 2.4 面向对象技术的优势 2.4.1 便于沟通:在 ...

  8. 5.计算机发展个人理解-电路终究是电路 软件如何控制硬件 代码如何操作硬件 硬件是怎么执行代码 代码如何执行 软件与硬件如何交互 计算机思维 抽象 封装 规范 屏蔽 协议分层...

    计算机只是逻辑电路 除了电路还是电路 计算机就是一堆逻辑电路 他并不知道你到底想要干什么,他也不会理解什么是文件,什么是进程 通电的瞬间,就好像你打开开关,灯泡发光一样 所有的一切都是通过通电来启动的 ...

  9. SQLServer 中存储过程返回的三种方式( 包括存储过程的创建, 在存储过程中调用, 在VS中调用的方法)...

    存储过程有三种返回: 1.   用return返回数字型数据 2.   用返回参数返回结果,可以返回各种数据类型(通过游标来循环查询结果每一行) 3.   直接在存储过程中用select返回结果集,可 ...

  10. oracle创建函数和调用存储过程和调用函数的例子(区别)

    创建函数: 格式:create or replace function func(参数 参数类型) Return number Is Begin --------业务逻辑--------- End; ...

最新文章

  1. 张立贤:积跬步至千里,我与地学大数据的探索之旅 | 提升之路系列(五)
  2. RxJava/RxAndroid:timer(long delay, TimeUnit unit)
  3. NFS文件系统详解以及安全设置
  4. 利用脚本将文字插入到图片或进行多个图片拼接
  5. 关于进程和线程以及句柄
  6. 大数运算(2)——大数加法
  7. Redis作者antirez:开源维护者的挣扎
  8. 联名款Redmi K40游戏增强版今日揭晓:神秘女主粉色头发吸睛
  9. android的动态注册,Android JNI 函数注册的两种方式(静态注册/动态注册)
  10. 迭代器之输入和输出迭代器
  11. python将excel文件变成txt文件
  12. Lesson 1 A puma at large
  13. Window open使用教程
  14. MAC安装Securecrt
  15. 两款Java中小医院信息管理系统源码
  16. 深度学习入门:手写体识别
  17. python图像的手绘效果代码_Python项目1:实现将图片转化为手绘效果
  18. html 文件怎么改类型,怎么更改文件类型
  19. Android 11 OTA升级集成
  20. python使用 Captcha 模块来生成验证码图片

热门文章

  1. 【Leetcode】打家劫舍 I and 打家劫舍 II(动态规划)PYTHON
  2. python-numpy.vectorize()
  3. 《高翔视觉slam十四讲》学习笔记 第六讲 非线性优化
  4. python入门必备10个坑_python中的基础坑
  5. Linux查看磁盘空间和文件夹大小
  6. microsoft azure
  7. 什么格式的照片可以是透明的
  8. StoryBoard和代码结合 按比例快速兼容iPhone6/6 Plus教程
  9. jquery 源码分析
  10. DataAdapter对象填充数据集