在很多的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