在很多的ORM中对存储过程操作都是一个棘手的地方,因为存储过程是一段预编译的代码,其中可以包含很多处理过程。在Git.Framework中也同样存在这样的问题,目前没有能力解决这个问题。但是对于存储过程的一些外围操作目前还是可以支持的。
上一篇文章简单回顾地址,可能对了解本文有益: Git.Framework 框架随手记--SQL配置文件的使用
一. 结构简单说明
在前面操作基本SQL的时候我们已经知道使用对象模型映射其相关的表,一些基本的操作我们都能够实现完成。在很大的程度上完成了抽象工作,在实现存储过程的时候我们同意遵循抽象的原则。每一个存储过程我们都映射成为一个实体类,而这个存储过程的实体类也必须继承自BaseEntity。先简单看看如下一个存储过程的映射类。
/******************************************************************************* * Copyright (C) Git Corporation. All rights reserved. * * Author: 代码工具自动生成 * Create Date: 2014/03/02 09:57:38 * Blog: http://www.cnblogs.com/qingyuan/ * Description: Git.Framework * * Revision History: * Date Author Description * 2014/03/02 09:57:38 *********************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using Git.Framework.ORM; namespace Git.Storage.Entity.InStorage { [TableAttribute(DbName = "JooShowGit", Name = "Proc_AuditeInStorage", IsInternal = false,MapType=MapType.Proc)] public partial class Proc_AuditeInStorageEntity:BaseEntity { public Proc_AuditeInStorageEntity() { } [DataMapping(ColumnName = "OrderNum", DbType = DbType.String, Length = 20,ColumnType=ColumnType.InPut)] public string OrderNum { get; set; } [DataMapping(ColumnName = "Status", DbType = DbType.Int32, Length = 4000,ColumnType=ColumnType.InPut)] public Int32 Status { get; set; } [DataMapping(ColumnName = "AuditUser", DbType = DbType.String, Length = 20,ColumnType=ColumnType.InPut)] public string AuditUser { get; set; } [DataMapping(ColumnName = "Reason", DbType = DbType.String, Length = 400,ColumnType=ColumnType.InPut)] public string Reason { get; set; } [DataMapping(ColumnName = "OperateType", DbType = DbType.Int32, Length = 4000,ColumnType=ColumnType.InPut)] public Int32 OperateType { get; set; } [DataMapping(ColumnName = "EquipmentNum", DbType = DbType.String, Length = 20,ColumnType=ColumnType.InPut)] public string EquipmentNum { get; set; } [DataMapping(ColumnName = "EquipmentCode", DbType = DbType.String, Length = 20,ColumnType=ColumnType.InPut)] public string EquipmentCode { get; set; } [DataMapping(ColumnName = "Remark", DbType = DbType.String, Length = 400,ColumnType=ColumnType.InPut)] public string Remark { get; set; } [DataMapping(ColumnName = "ReturnValue", DbType = DbType.String, Length = 50,ColumnType=ColumnType.InOutPut)] public string ReturnValue { get; set; } } }
上面这个实体类第一眼看和之前的没有什么区别,如果观察仔细这里还是有小小的区别的,在某些标识特性上存在着差异。
[TableAttribute(DbName = "JooShowGit", Name = "Proc_AuditeInStorage", IsInternal = false,MapType=MapType.Proc)]
在前面TableAttribute是指定的表的映射,这里同样适用于存储过程的的标识。但是这里多了一个标识符,MapType=MapType.Proc. 指定映射为存储过程,默认情况是Table 和View的映射。前面没有提到View,View在结构上和Table没有太大的差异,所以直接将View当做Table使用即可。
[DataMapping(ColumnName = "OrderNum", DbType = DbType.String, Length = 20,ColumnType=ColumnType.InPut)] public string OrderNum { get; set; }
这是一个字段映射的过程,和之前也没有什么区别,但是这里似乎有很多属性没有了,因为没有任何意义,所以不填写也没关系,即使填写了不起任何作用。但是这里需要制定一个ColumnType属性,上一篇讲到了参数类型有几种类型,这里就是指定其类型的。上面一段代码则是指定这个字段为输入参数。
[DataMapping(ColumnName = "ReturnValue", DbType = DbType.String, Length = 50,ColumnType=ColumnType.InOutPut)] public string ReturnValue { get; set; }
这段代码这是指定了一个属性字段为输出参数类型,在调用存储过程执行成功之后,存储过程返回的值会自动填充到这个属性中,不需要我们做任何其他的处理,还是比较方便。
public enum ColumnType { InPut = 1, OutPut = 2, InOutPut = 3, Return = 4, Common = 10, }
这个枚举值就是ColumnType的详细定义,总共有有5中定义,但是其中用到的最多也就是4种,都是我们比较熟悉的。
二. 存储过程接口的定义
在前面已经说过了,这个框架都遵循一定程度的抽象,无论是对表,视图,存储过程,还是函数我们都遵循一定规则的抽象。存储过程是一段已编译的代码,而由于存储过程的本身复杂性,在很多ORM框架中都对存储过程支持的不是那么彻底。该框架也是如此,但是在某种程度上还是可以完成一般性工作的。对于存储过程的支持,我们这里采用另外的一个接口形式。
public interface IDbProcHelper<T> : IDisposable where T : Git.Framework.ORM.BaseEntity { V ExceuteEntity<V>(T entity) where V : class, new(); List<V> ExceuteEntityList<V>(T entity) where V : class, new(); int ExecuteNonQuery(T entity); V ExecuteScalar<V>(T entity); object ExecuteScalar(T entity); }
上面这段代码就是对存储过程操作的公共接口,其中只有5个方法,相比IDbHelper<T>要简单多了,而且其中这五个方法的方法名和ADO.NET中定义的一样,这样是为了更好简单的从ADO.NET过渡到这个上面来。
public partial interface IProc_AuditeInStorage : IDbProcHelper<Proc_AuditeInStorageEntity> { }
这里是一个存储过程对应的接口,该存储过程必须继承自抽象接口IDbProcHelper<T>,在上面提到过了,继承这个抽象接口意味着具有其所有的方法,同时也指明了这个接口具体操作哪个存储过程。其接口实现过程如下:
public partial class Proc_AuditeInStorageDataAccess : DbProcHelper<Proc_AuditeInStorageEntity>, IProc_AuditeInStorage { public Proc_AuditeInStorageDataAccess() { } }
上面这段代码是存储过程接口的实现,从结构上和之前说的结构没有任何区别,执行继承的类稍微做了一些修改。而且必须遵循这种结构,这是保证运行的基础。有人会问是不是可以使用配置做的更加灵活一点,答案是的,但是我个人遵循的观点是"约束大于配置"。
三. 如何使用
存储过程的使用也非常简单,和之前的操作没有太大的区别。在使用之前我们先建好存储过程。
(1) 新建一个存储过程
---------------------------------------------------------------------------------------------------------------- --*******************************************入库审核存储过程*************************************************** --************************************************************************************************************** -- 1000 审核成功 -- 1001 入库单不存在 -- 1002 入库单已经审核 ---------------------------------------------------------------------------------------------------------------- IF EXISTS(SELECT * FROM SYSOBJECTS WHERE NAME = 'Proc_AuditeInStorage') DROP PROCEDURE Proc_AuditeInStorage GO CREATE PROCEDURE Proc_AuditeInStorage ( @OrderNum VARCHAR(20),--入库单号 @Status int,--审核状态 @AuditUser nvarchar(20),--审核人编号 @Reason nvarchar(400), --审核成功或者失败的原因 @OperateType int, --操作方式 1 电脑 2 PDA @EquipmentNum varchar(20), --设备编号[所使用的设备编号] @EquipmentCode varchar(20), --设备条码[如果没有就直接设备编号] @Remark nvarchar(400),--入库单描述 @ReturnValue varchar(50) OUTPUT ) AS BEGIN --判断入库单是否存在 IF NOT EXISTS ( SELECT COUNT(*) FROM InStorage WHERE OrderNum=@OrderNum AND IsDelete=0) BEGIN SET @ReturnValue='1001' RETURN END --判断入库单是否已经被审核 查询入库单等待审核是否存在 IF NOT EXISTS ( SELECT COUNT(*) FROM InStorage WHERE OrderNum=@OrderNum AND Status=1 AND IsDelete=0) BEGIN SET @ReturnValue='1002' RETURN END DECLARE @ProductName nvarchar(100) DECLARE @BarCode varchar(50) DECLARE @ProductNum varchar(50) DECLARE @BatchNum nvarchar(20) DECLARE @Num int DECLARE @LocalNum varchar(20) DECLARE @StorageNum varchar(20) DECLARE MyCursor CURSOR FOR SELECT [ProductName],[BarCode],[ProductNum],[BatchNum],[Num],[LocalNum],[StorageNum] FROM InStorDetail WHERE OrderNum= @OrderNum OPEN MyCursor FETCH NEXT FROM MyCursor INTO @ProductName, @BarCode,@ProductNum,@BatchNum,@Num,@LocalNum,@StorageNum WHILE @@FETCH_STATUS = 0 BEGIN --盘点库存是否存在 IF (SELECT COUNT(*) FROM LocalProduct WHERE ProductNum=@ProductNum AND StorageNum=@StorageNum AND LocalNum=@LocalNum)>0 BEGIN --如果存在则修改相应的库存数量 UPDATE LocalProduct SET Num=Num+@Num WHERE ProductNum=@ProductNum AND StorageNum=@StorageNum AND LocalNum=@LocalNum END ELSE BEGIN --如果不存在则添加 --获得SN流水号 DECLARE @SN varchar(50) EXEC Proc_SwiftNum '','LocalProduct',@SN OUTPUT DECLARE @StorageName nvarchar(50) DECLARE @LocalName nvarchar(50) DECLARE @LocalType int DECLARE @UserName nvarchar(50) SELECT TOP 1 @StorageName=StorageName FROM Storage WHERE StorageNum=@StorageNum SELECT TOP 1 @LocalName=LocalName,@LocalType=LocalType FROM Location WHERE LocalNum=@LocalNum AND StorageNum=@StorageNum SELECT TOP 1 @UserName=UserName FROM Admin WHERE UserCode=@AuditUser INSERT INTO [dbo].[LocalProduct]([Sn],[StorageNum],[StorageName],[LocalNum],[LocalName],[LocalType],[ProductNum],[BarCode],[ProductName],[Num],[CreateTime],[CreateUser],[CreateName],[Remark]) VALUES(@SN,@StorageNum,@StorageName,@LocalNum,@LocalName,@LocalType,@ProductNum,@BarCode,@ProductName,@Num,GETDATE(),@AuditUser,@UserName,'') END --入库台账记录生成 INSERT INTO [dbo].[InventoryBook]([ProductNum],[BarCode],[ProductName],[Num],[Type],[ContactOrder],[FromLocalNum],[ToLocalNum],[StoreNum],[CreateTime],[CreateUser]) VALUES(@ProductNum,@BarCode,@ProductName,@Num,1,@OrderNum,'',@LocalNum,@StorageNum,GETDATE(),@AuditUser) --进行下一个游标循环 FETCH NEXT FROM MyCursor INTO @ProductName, @BarCode,@ProductNum,@BatchNum,@Num,@LocalNum,@StorageNum END CLOSE MyCursor DEALLOCATE MyCursor--释放游标 --修改订单状态 UPDATE InStorage SET Status=@Status,AuditUser=@AuditUser,AuditeTime=GETDATE(),Reason=@Reason,OperateType=@OperateType, EquipmentNum=@EquipmentNum,EquipmentCode=@EquipmentCode,Remark=@Remark WHERE OrderNum=@OrderNum SET @ReturnValue='1000' END GO
上面的代码新建了一个存储过程,这段代码是我们开发的一个条码仓库系统中的一个存储过程,相对比较简单其中有多个输入参数,同时也有一个输出参数。
(2) 使用工具映射生成实体类
/******************************************************************************* * Copyright (C) Git Corporation. All rights reserved. * * Author: 代码工具自动生成 * Create Date: 2014/03/02 09:57:38 * Blog: http://www.cnblogs.com/qingyuan/ * Description: Git.Framework * * Revision History: * Date Author Description * 2014/03/02 09:57:38 *********************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using Git.Framework.ORM; namespace Git.Storage.Entity.InStorage { [TableAttribute(DbName = "JooShowGit", Name = "Proc_AuditeInStorage", IsInternal = false,MapType=MapType.Proc)] public partial class Proc_AuditeInStorageEntity:BaseEntity { public Proc_AuditeInStorageEntity() { } [DataMapping(ColumnName = "OrderNum", DbType = DbType.String, Length = 20,ColumnType=ColumnType.InPut)] public string OrderNum { get; set; } [DataMapping(ColumnName = "Status", DbType = DbType.Int32, Length = 4000,ColumnType=ColumnType.InPut)] public Int32 Status { get; set; } [DataMapping(ColumnName = "AuditUser", DbType = DbType.String, Length = 20,ColumnType=ColumnType.InPut)] public string AuditUser { get; set; } [DataMapping(ColumnName = "Reason", DbType = DbType.String, Length = 400,ColumnType=ColumnType.InPut)] public string Reason { get; set; } [DataMapping(ColumnName = "OperateType", DbType = DbType.Int32, Length = 4000,ColumnType=ColumnType.InPut)] public Int32 OperateType { get; set; } [DataMapping(ColumnName = "EquipmentNum", DbType = DbType.String, Length = 20,ColumnType=ColumnType.InPut)] public string EquipmentNum { get; set; } [DataMapping(ColumnName = "EquipmentCode", DbType = DbType.String, Length = 20,ColumnType=ColumnType.InPut)] public string EquipmentCode { get; set; } [DataMapping(ColumnName = "Remark", DbType = DbType.String, Length = 400,ColumnType=ColumnType.InPut)] public string Remark { get; set; } [DataMapping(ColumnName = "ReturnValue", DbType = DbType.String, Length = 50,ColumnType=ColumnType.InOutPut)] public string ReturnValue { get; set; } } }
上面已经讲到了这段代码的含义,这里不在累述。
(3)做好接口的继承以及实现
上面已经说到了接口继承和和实现,这里不在贴代码,因为结构是一样的。
(4)调用存储过程返回值
Proc_AuditeInStorageEntity auditeEntity = new Proc_AuditeInStorageEntity(); auditeEntity.OrderNum = entity.OrderNum; auditeEntity.Status = entity.Status; auditeEntity.AuditUser = entity.AuditUser; auditeEntity.Reason = entity.Reason; auditeEntity.OperateType = entity.OperateType; auditeEntity.EquipmentNum = entity.EquipmentNum; auditeEntity.EquipmentCode = entity.EquipmentCode; auditeEntity.Remark = entity.Remark; int line = this.Proc_AuditeInStorage.ExecuteNonQuery(auditeEntity); return auditeEntity.ReturnValue;
上面这段就是调用存储过程并且返回输出参数的案例,我们将存储过程的参数映射为一个对象了,在调用存储过程的时候直接将对象传入自动处理输入输出参数。可以从上面的代码看到,输出参数会自动封装到传入的类中指定的属性中去。
使用案例下载链接:http://pan.baidu.com/s/1sj6S4yT
因为这个系列的文章所涉及的东西全是公司项目的的部分截取,非主观臆断的编码和设计,是诸多项目的实战所积累,所以不便完全透漏,望请见谅。如果您想有跟深层次的了解可以加群号: 88718955
作者:情缘
出处:http://www.cnblogs.com/qingyuan/
关于作者:从事仓库,生产软件方面的开发,在项目管理以及企业经营方面寻求发展之路
版权声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。
联系方式: 个人QQ 821865130 ; 仓储技术QQ群 88718955,142050808 ;
吉特仓储管理系统 开源地址: https://github.com/hechenqingyuan/gitwms