bp(net core)+easyui+efcore实现仓储管理系统——入库管理之三存储过程(三十九)
abp(net core)+easyui+efcore实现仓储管理系统目录
abp(net core)+easyui+efcore实现仓储管理系统——EasyUI前端页面框架 (十八)
abp(net core)+easyui+efcore实现仓储管理系统——ABP WebAPI与EasyUI结合增删改查之八(三十四)
在上一篇文章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); }
}
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,),
new SqlParameter("BH", System.Data.SqlDbType.NVarChar,) }; parameters[].Value = name;
parameters[].Direction = System.Data.ParameterDirection.Output; int cnt = Context.Database.ExecuteSqlCommand(
"EXEC p_NextBH @Name, @BH output",
parameters); string no = parameters[].Value.ToString(); if (cnt < )
{
no = string.Empty;
}
return no;
} void IInStockOrderRepository.ImportCargo(string ids,string no)
{ SqlParameter[] parameters = {
new SqlParameter("id",System.Data.SqlDbType.VarChar,),
new SqlParameter("No", System.Data.SqlDbType.NVarChar,) }; parameters[].Value = ids + ",";
parameters[].Value = 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,'批次号') GO
6.由于这是一个小应用,所以单号的生成就是字母+日期+流水号。通过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, '');
}
} //获取数据库中最大的序列号
private static void GetMaxSeq()
{
//seq =数据库中的最大值 } // 防止创建类的实例
private BillNoBuilder(){}
}
bp(net core)+easyui+efcore实现仓储管理系统——入库管理之三存储过程(三十九)的更多相关文章
- bp(net core)+easyui+efcore实现仓储管理系统——入库管理之二(三十八)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之四(四十)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之五(四十一)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之六(四十二)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之七(四十三)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之八(四十四)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之九(四十五)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之十(四十六)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
- abp(net core)+easyui+efcore实现仓储管理系统——入库管理之十一(四十七)
abp(net core)+easyui+efcore实现仓储管理系统目录 abp(net core)+easyui+efcore实现仓储管理系统——ABP总体介绍(一) abp(net core)+ ...
随机推荐
- day22- hashlib模块-摘要算法(哈希算法)
# python的hashlib提供了常见的摘要算法,如md5(md5算法),sha1等等.摘要:digest # 摘要算法又称哈希算法.散列算法. # 它通过一个函数,把任意长度的数据(明文)转换为 ...
- Git ubuntu 升级
外文文档 This team will distribute the most current stable package of Git for Ubuntu. Stable releases: h ...
- mysql首次使用过程以及彻底卸载过程
安装过程: 步骤一: 安装mysql服务,使用命令行: yum install mysql-server 步骤二: 启动mysql服务: service mysqld start 确认msyql是否启 ...
- OpenWrt Web 开发 LuCI框架 lua语言
LuCI作为“FFLuCI”诞生于2008年3月份,目的是为OpenWrt固件从 Whiterussian 到 Kamikaze实现快速配置接口.Lua是一个小巧的脚本语言,很容易嵌入其它语言.轻量级 ...
- python3下应用pymysql(第一卷)
编程不会操作数据库,就像男人做做了太监,人生不完整,我不想人生不完整,写下pymysql的使用总结 先做下准备工作,准备下数据表,由于是练习操作,所以先做个简单的数据表: 创建单独的一个库:再创建表 ...
- win7+centos6.5安装双系统
前言:之前在琢磨怎么安装双系统 倒腾了两天终于给装上了 使用软件 镜像:CentOS-6.5-x86_64-bin-DVD1.iso 开机引导软件 easybcd2.2 u盘制作软件 USBWrite ...
- SpringMVC学习笔记三:Controller的返回值
springMVC的返回值有ModelAndView,String,void,Object类型 项目目录树: 该项目是在前面项目的基础上修改的,这里的pom.xml文件需要加入使用到的包,应为@Res ...
- struts2学习笔记之十四:使用注解配置Action(不是和spring集成使用)
Struts2支持使用注解配置Action,减少配置文件的配置 Struts2如果要支持注解配置Action,需要插件的支持,导入插件struts2-convention-plugin-2.1.8.1 ...
- FastJson的学习之JSON互相转Map集合,List集合,JavaBean
https://blog.csdn.net/weixin_36910300/article/details/79182120 创建两个实体类 一个是部门类,一个是部门员工类 部门员工类 public ...
- Java编程风格节选
3.3 import语句 3.3.1 import不要使用通配符 即,不要出现类似这样的import语句:import java.util.*; 3.3.2 不要换行 import语句不换行,列限制( ...