abp(net core)+easyui+efcore實作倉儲管理系統目錄
abp(net core)+easyui+efcore實作倉儲管理系統——ABP總體介紹(一) abp(net core)+easyui+efcore實作倉儲管理系統——解決方案介紹(二) abp(net core)+easyui+efcore實作倉儲管理系統——領域層創建物體(三) abp(net core)+easyui+efcore實作倉儲管理系統——定義倉儲并實作 (四) abp(net core)+easyui+efcore實作倉儲管理系統——創建應用服務(五) abp(net core)+easyui+efcore實作倉儲管理系統——展現層實作增刪改查之控制器(六) abp(net core)+easyui+efcore實作倉儲管理系統——展現層實作增刪改查之串列視圖(七) abp(net core)+easyui+efcore實作倉儲管理系統——展現層實作增刪改查之增刪改視圖(八) abp(net core)+easyui+efcore實作倉儲管理系統——展現層實作增刪改查之選單與測驗(九) abp(net core)+easyui+efcore實作倉儲管理系統——多語言(十) abp(net core)+easyui+efcore實作倉儲管理系統——使用 WEBAPI實作CURD (十一) abp(net core)+easyui+efcore實作倉儲管理系統——選單-上 (十六)abp(net core)+easyui+efcore實作倉儲管理系統——EasyUI前端頁面框架 (十八)
abp(net core)+easyui+efcore實作倉儲管理系統——EasyUI之貨物管理一 (十九) abp(net core)+easyui+efcore實作倉儲管理系統——ABP WebAPI與EasyUI結合增刪改查之一(二十七) abp(net core)+easyui+efcore實作倉儲管理系統——ABP WebAPI與EasyUI結合增刪改查之三(二十九)abp(net core)+easyui+efcore實作倉儲管理系統——ABP WebAPI與EasyUI結合增刪改查之八(三十四)
abp(net core)+easyui+efcore實作倉儲管理系統——ABP WebAPI與EasyUI結合增刪改查之十(三十六) abp(net core)+easyui+efcore實作倉儲管理系統——入庫管理之一(三十七) abp(net core)+easyui+efcore實作倉儲管理系統——入庫管理之二(三十八)在上一篇文章Abp(net core)+easyui+efcore實作倉儲管理系統——入庫管理之二(三十八) 中我們創建了入庫單的一些有關DTO類與分頁類,由于入庫單我使用了到了資料庫的存盤程序,那么本篇文章中我們來學習一下如何在ABP中呼叫存盤程序,
我們都知道,倉儲管理系統中的單號最基本的要求就是唯一,這個條件必須滿足,或者說對于任何有單號的系統來說單號必須唯一,這是硬性要求,
先來講講對于單號命名的幾種規則:
1、不重復,
這點我相信大家都懂,單號的唯一性不用解釋,
2、安全性,
你的單號編號盡量不要透露你公司的真實運營資訊,比如你的單號就是流水號的話,那么別人就可以從單號推測出你公司的整體運營概括了,所以單號編碼必須是除了你們公司少部分人外,其他人基本看不懂的,其實最好的防泄漏編碼規則就是在編碼中不要加入任何和公司運營的資料,
3、隨機碼,
很多人在制定單號編碼規則的時候,第一個想法肯定是不重復唯一性,那么第二個想法可能就是安全性,同時滿足前兩者的第三個想法,就是在單號中添加隨機碼了,在單號中添加2~3隨機碼,和流水號結合使用,可以起到隱藏流水號的真實資料的作用,
4、防止并發,
這條規則主要針對編碼中有時間的設定,
5、控制位數,
這點很好理解,單號的作用就是便于查詢,
單號幾種常規的創建方式:
1.利用資料庫主鍵值產生一個自增長的訂單號(訂單號即資料表的主鍵)
2.日期+自增長數字的訂單號(比如:20200101100662、202002100662、2002100662)
3.隨機生成的單號(6512353245921)
4.字母+數字字串式,字母應該有特殊意義,如入庫單,GD202016652
訂單號設計用戶體驗規則:
1.訂單號無重復性;
2.如果方便客服的話,最好是“日期+自增數”樣式的訂單號,
3.訂單號長度盡量保持短(15位以內),方便用戶,長的號碼報錯幾率高,影響客服效率;
4.如果你的系統用戶量在千萬級別,那么訂單號盡量保持數字型(純整數),在資料庫訂單索引查詢中,長整數字型的資料索引與檢索效率,遠遠高于文本型,對于中小應用可以使用“字母+數字”的字串形式!
五、使用存盤程序創建單號
在使用ABP框架構建專案時,如果想在倉儲層呼叫存盤程序,我們應該如何來實作呢?關于這個問題,我搜索了很多資料,最后還看了官方檔案:https://aspnetboilerplate.com/Pages/Documents/Articles/Using-Stored-Procedures,-User-Defined-Functions-and-Views/index.html
在看完官方檔案,對于如何在ABP中使用存盤程序已經有了一個相應的思路,現在我們來實作,
- 在Visual Studio 2017的“解決方案資源管理器”中,右鍵單擊“ABP.TPLMS.Core”專案的“IRepositories”檔案夾,在彈出選單中選擇“添加” > “類”,在彈出對話框中選擇“介面”, 將介面命名為 IInStockOrderRepository,然后選擇“添加”,如下圖,

2.在IInStockOrderRepository介面定義我們需要用到的方法,代碼如下,
using Abp.Domain.Repositories; using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; using System.Data.Common; using System.Linq; using System.Text; using ABP.TPLMS.Entitys; using System.Threading.Tasks; using Abp.Dependency; namespace ABP.TPLMS.IRepositories { public interface IInStockOrderRepository : IRepository<InStockOrder,int>, ITransientDependency { /// <summary> /// 執行給定的命令 /// </summary> /// <param name="sql">命令字串</param> /// <param name="parameters">要應用于命令字串的引數</param> /// <returns>執行命令后由資料庫回傳的結果</returns> int Execute(string sql, params object[] parameters); /// <summary> /// 創建一個原始 SQL 查詢,該查詢將回傳給定泛型型別的元素, /// </summary> /// <typeparam name="T">查詢所回傳物件的型別</typeparam> /// <param name="sql">SQL 查詢字串</param> /// <param name="parameters">要應用于 SQL 查詢字串的引數</param> /// <returns></returns> IQueryable<T> SqlQuery<T>(string sql, params object[] parameters); DbCommand CreateCommand(string commandText, CommandType commandType, params object[] parameters); /// <summary> /// 創建單號 /// </summary> /// <param name="name">單證名稱代碼</param> /// <returns></returns> string GetNo(string name); /// <summary> /// 匯入貨物資訊 /// </summary> /// <param name="ids">匯入貨物的ID集合</param> /// <param name="no">單號</param> void ImportCargo(string ids,string no); } }3. 在Visual Studio 2017的“解決方案資源管理器”中,右鍵單擊“ABP.TPLMS.EntityFrameworkCore”專案的“Repositories”檔案夾,在彈出選單中選擇“添加” > “類”, 將類命名為 InStockOrderRepository,并繼承IInStockOrderRepository介面,實作介面中的方法,代碼如下,
using Abp.Data; using Abp.Dependency; using Abp.Domain.Entities; using Abp.EntityFrameworkCore; using ABP.TPLMS.Entitys; using ABP.TPLMS.IRepositories; using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Data; using System.Data.Common; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ABP.TPLMS.EntityFrameworkCore.Repositories { public class InStockOrderRepository : TPLMSRepositoryBase<InStockOrder, int> ,IInStockOrderRepository, ITransientDependency { private readonly IActiveTransactionProvider _transactionProvider; public InStockOrderRepository(IDbContextProvider<TPLMSDbContext> dbContextProvider) : base(dbContextProvider) { } protected InStockOrderRepository(IDbContextProvider<TPLMSDbContext> dbContextProvider, IActiveTransactionProvider transactionProvider) : base(dbContextProvider) { _transactionProvider = transactionProvider; } public DbCommand CreateCommand(string commandText, CommandType commandType, params SqlParameter[] parameters) { EnsureConnectionOpen(); var dbFacade = Context.Database; var connection = Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.GetDbConnection(dbFacade); var command = connection.CreateCommand(); command.CommandText = commandText; command.CommandType = commandType; command.Transaction = GetActiveTransaction(); foreach (var parameter in parameters) { command.Parameters.Add(parameter); } return command; } DbCommand IInStockOrderRepository.CreateCommand(string commandText, CommandType commandType, params object[] parameters) { EnsureConnectionOpen(); var dbFacade = Context.Database; var connection = Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.GetDbConnection(dbFacade); var command = connection.CreateCommand(); command.CommandText = commandText; command.CommandType = commandType; command.Transaction = GetActiveTransaction(); foreach (var parameter in parameters) { command.Parameters.Add(parameter); } return command; } private void EnsureConnectionOpen() { var dbFacade = Context.Database; var connection = Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.GetDbConnection(dbFacade); if (connection.State != ConnectionState.Open) { connection.Open(); } } int IInStockOrderRepository.Execute(string sql, params object[] parameters) { throw new NotImplementedException(); } private DbTransaction GetActiveTransaction() { return (DbTransaction)_transactionProvider.GetActiveTransaction(new ActiveTransactionProviderArgs { {"ContextType", typeof(TPLMSDbContext) }, {"MultiTenancySide", MultiTenancySide } }); } string IInStockOrderRepository.GetNo(string name) { SqlParameter[] parameters = { new SqlParameter("Name",System.Data.SqlDbType.NVarChar,10), new SqlParameter("BH", System.Data.SqlDbType.NVarChar,20) }; parameters[0].Value =https://www.cnblogs.com/dearroy/p/ name; parameters[1].Direction = System.Data.ParameterDirection.Output; int cnt = Context.Database.ExecuteSqlCommand( "EXEC p_NextBH @Name, @BH output", parameters); string no = parameters[1].Value.ToString(); if (cnt < 0) { no = string.Empty; } return no; } void IInStockOrderRepository.ImportCargo(string ids,string no) { SqlParameter[] parameters = { new SqlParameter("id",System.Data.SqlDbType.VarChar,500), new SqlParameter("No", System.Data.SqlDbType.NVarChar,20) }; parameters[0].Value = https://www.cnblogs.com/dearroy/p/ids + ","; parameters[1].Value =https://www.cnblogs.com/dearroy/p/ no; int cnt = Context.Database.ExecuteSqlCommand( "EXEC SP_ImportCargo2GDE @id, @No", parameters); } IQueryable<T> IInStockOrderRepository.SqlQuery<T>(string sql, params object[] parameters) { throw new NotImplementedException(); } } }
4.在這里我一共使用了兩個存盤程序,p_NextBH 與SP_ImportCargo2GDE,
5.定義一張表TPLMS_NO,專門用來存放存所有需要唯一單號的單號的型別,以及類單號當前所使用到最大值,
CREATE TABLE [dbo].[TPLMS_NO]( [Name] [nvarchar](10) NOT NULL, [Head] [nvarchar](10) NOT NULL, [CurrentNo] [int] NOT NULL, [BHLen] [bigint] NOT NULL, [IsYear] [int] NOT NULL, [DESCRIPTION] [nvarchar](50) NULL, PRIMARY KEY CLUSTERED ( [Name] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY] GO ALTER TABLE [dbo].[TPLMS_NO] ADD DEFAULT ('') FOR [Head] GO ALTER TABLE [dbo].[TPLMS_NO] ADD DEFAULT ((0)) FOR [CurrentNo] GO ALTER TABLE [dbo].[TPLMS_NO] ADD DEFAULT ((6)) FOR [BHLen] GO ALTER TABLE [dbo].[TPLMS_NO] ADD DEFAULT ((1)) FOR [IsYear] GO INSERT INTO [TPLMS_NO]([Name],[Head],[CurrentNo] ,[BHLen],[IsYear],[DESCRIPTION]) VALUES ('GDE','GD',0,6,1,'入庫單') GO INSERT INTO [TPLMS_NO]([Name],[Head],[CurrentNo],[BHLen],[IsYear],[DESCRIPTION]) VALUES ('BAT','A' ,0,7,0,'批次號') GO6.由于這是一個小應用,所以單號的生成就是字母+日期+流水號,通過p_NextBH來實作單號的創建,專門用來在上一步的表中取單號,p_NextBH這個存盤程序的實作如下:
--獲取新編號的存盤程序 CREATE PROC [dbo].[p_NextBH] @Name nvarchar(10), --編號種類 @BH nvarchar(20) OUTPUT --新編號 AS BEGIN TRAN UPDATE [TPLMS_NO] WITH(ROWLOCK) SET @BH=Head+case isyear when 1 then convert(varchar(4),year(getdate())) else '' end +RIGHT(POWER(convert(bigint,10),BHLen)+CurrentNo+1,BHLen), CurrentNo=CurrentNo+1 WHERE Name=@Name select @BH COMMIT TRAN GO
7. 關于使用p_NextBH這個存盤程序生成單號有什么優缺點呢?在存盤程序中使用事物,資料庫的性能會急劇下滑,對于小應用來說,這并不是太大的問題,對于中大型應用來說,就可能是問題了,可以直接使用UPDATE獲取到的更新鎖,即SQL SERVER會保證UPDATE的順序執行,適用中型應用,但是無法滿足高并發性能要求,我們來改一下存盤程序,
--獲取新編號的存盤程序 CREATE PROC [dbo].[p_NextBH] @Name nvarchar(10), --編號種類 @BH nvarchar(20) OUTPUT --新編號 AS UPDATE [TPLMS_NO] WITH(ROWLOCK) SET @BH=Head+case isyear when 1 then convert(varchar(4),year(getdate())) else '' end +RIGHT(POWER(convert(bigint,10),BHLen)+CurrentNo+1,BHLen), CurrentNo=CurrentNo+1 WHERE Name=@Name select @BH GO
8. 通過傳遞貨物資訊的ID,把貨物資訊匯入到入庫單中,這個功能通過存盤程序SP_ImportCargo2GDE來實作,這個存盤程序的實作如下:
CREATE Proc [dbo].[SP_ImportCargo2GDE] @id varchar(1000), --id集合 @No nvarchar(20) --單號 as CREATE TABLE #IdTable(Id int NULL) DECLARE @PointerPrev int DECLARE @PointerCurr int DECLARE @TName nvarchar(100) Set @PointerPrev=1 while (@PointerPrev < LEN(@id)) Begin Set @PointerCurr=CharIndex(',',@id,@PointerPrev) if(@PointerCurr>0) Begin set @TName=SUBSTRING(@id,@PointerPrev,@PointerCurr-@PointerPrev) --如果作為查詢條件,我需要創建一個臨時表,將資料插入進去 insert into #IdTable (Id) VALUES (convert(int,@TName)) SET @PointerPrev = @PointerCurr+1 End else Break End DECLARE @BH nvarchar(20),@batch varchar(20),@maxseqno int select @BH=@No select @maxseqno=isnull(MAX(seqno),0) from [InStockOrderDetail] where InStockNo= @BH --創建批次號 EXEC [dbo].[p_NextBH] 'BAT', @batch OUTPUT INSERT INTO [dbo].[InStockOrderDetail] ([InStockNo],[SeqNo],[SupplierId],[CargoCode],[HSCode],[CargoName],[Spcf] ,[Unit],[Country],[Brand] ,[Curr],[Package],[Length],[Width],[Height],[Qty] ,[Vol],[LawfQty],[SecdLawfQty],[Price],[TotalAmt],[GrossWt],[NetWt] ,[LawfUnit] ,[SecdLawfUnit],[Batch],[DeliveryOrderDetailId],[CreationTime]) SELECT @BH,convert(int,seqno)+@maxseqno,a.supplierid,[CargoCode],[HSCode],[CargoName],[Spcf] ,[Unit],[Country],[Brand],[Curr] ,[Package],[Length],[Width],[Height],0 [Qty] ,[Vol] ,0 [LawfQty], 0 [SecdLawfQty] ,[Price],0 [TotalAmt],[GrossWt],[NetWt] ,'' [LawfUnit],'' [SecdLawfUnit],@batch,a.id,getdate() FROM (select row_number() OVER ( order by id) seqno,* from [dbo].Cargos where id in (select id from #IdTable where id not in (select [DeliveryOrderDetailId] from [InStockOrderDetail] where InStockNo= @BH ) ) ) a drop table #IdTable GO
9. 關于單號的創建,除了使用存盤程序,也可以使用應用程式來創建,不過使用應用程式來創建,你要保證應用的高可用性,并且建議把最大值保存到資料庫,我在這里只是給出大概的代碼,
public class BillNoBuilder{ private static object locker = new object(); private static int seq = 0; public static string NextBillNumber(string head){ //在這里執行,或是經過一定的步長之后,再執行,GetMaxSeq(); lock(locker){ if(seq == 99999999) seq = 0; else seq++; return head+DateTime.Now.ToString("yyyyMMdd") + sn.ToString().PadLeft(8, '0'); } } //獲取資料庫中最大的序列號 private static void GetMaxSeq() { //seq =資料庫中的最大值 } // 防止創建類的實體 private BillNoBuilder(){} }
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/71843.html
標籤:其他
