一线程序员和 sa 总是相恨相杀,这话确实不假,吐槽这里就不多讲,项目快开发完的时候,让之前各个模块的增删改操作全部都先放入对应的临时表(增加一状态栏位Status,来表示增、删、改)中,然后在主管放行界面放行之后,数据才算真正入库。sa 轻轻一句话,整个项目几乎从头到尾要改一遍。虽然已不是第一次做此事。但着实还是费了一番气力。期间遇到了不少问题很值得记录。

关于MVC项目中的主管放行-LMLPHP

流程大致如上,第一排是原逻辑,第二排是增加主管放行的新逻辑(随手画的,比较简陋)

原项目中涉及到,多表主外键关系,新增范本功能等较为复杂的逻辑。这里在后面会做一个大致的流程说明

由于各表的栏位,主外键关系不同,所以第一次开发时,均是对维护的每个表(功能)建立对应的Biz(业务处理类)来实现增删改查。

新需求要求用户对数据的删、改、查操作均 添加到对应的temp表中,如   表A   栏位 B,C,D    -对应-     临时表TempA   栏位 B,C,D,Status

1 .操作某表数据

在对表进行 新增、修改、删除 操作之前,都要先到临时表中去捞取,看是否该数据已存在待主管放行的临时表中,这里可直接写一个公共方法,传表名 和 联合主键即可

(PS:新增、修改传入的是一笔联合主键值,故需要包装,两侧加单引号,删除的话,前台ajax提交时,已经添加过单引号)

        public bool IsExistTemp(string tbname, string strtablekey,string operatype)
{
try
{
//in操作 新增、修改传入值需要包装下
if (operatype != "D")
{
strtablekey = "'" + strtablekey + "'";
}
string strsql = "SELECT COUNT(*) FROM " + tbname + " AS a WHERE a.TableKey IN(" +
strtablekey + ") ";
int row = (int)base.ExecuteScalar(strsql);
return row > ;
}
catch (Exception e)
{
return false;
}
}

①执行新增、修改

新增界面点击确定、和修改界面点击确定 均是对一笔数据数据操作,直接采用Ajax的 方式,调用serializeArray()方法提交整个表单的数据 ,到Action中

对新增来说: A  判断新增的这笔数据是否已在表中存在    B  判断该笔数据是否已存在待主管放行的临时表中  C 将该数据添加至临时表中

对修改来说: A  第一次跳转至修改界面之前,先去该笔数据是否已存在待主管放行的临时表中,若在,修改界面给出提示,保存按钮为灰色不可点击

B  将该数据添加至临时表中

相对前期判断没什么好说的,至于某笔数据检核无误后(通过A、B)条件, 添加至临时表中,新增、删除的逻辑代码是一样的,所以在Action中直接调用同一个逻辑类中的方法即可。

②执行删除

查询结果界面,可勾选多笔数据,进行删除操作,这就意味着需要往临时表中添加多笔数据。前台Ajax提交时,先遍历复选框,然后将复选框勾选状态所在行的主键Key,进行包装再传递。

前面的博客中,我做的处理是循环删除,用List<string> 接受传来的主键集合,然后遍历该集合, 比较费事,这里我直接将主键进行拼接,作为一个string字符串来进行传递

KeyList.push(tabkey);

tablekeys = "'" + KeyList.join("','") + "'";

这样后台Action中接收的样式就是 ''k1','k2','k3'...'

后台接收到主键拼接的字符串之后,传入 原表名称,目的表名称,联合主键值,目的表的Status栏位值 四个参数即可,公共方法如下

         /// <summary>
/// 新需求-刪除(為主管放行而寫),實則將A表數據寫到B表中
/// </summary>
/// <param name="tbname">原表</param>
/// <param name="tbnameTo">目的表</param>
/// <param name="tablekeys">聯合主鍵</param>
/// <param name="status">操作狀態(D:刪除)</param>
/// <returns></returns>
public bool Delete_AddTemp(string tbname, string tbnameTo, string tablekeys, char status)
{
try
{
string strsql = @"INSERT INTO " + tbnameTo + " SELECT *,'" + status + "' FROM " + tbname + " WHERE TableKey IN (" + tablekeys + ")";
//执行Sql,返回影响的行数,并判断
return base.ExecuteNonQuery(strsql) > ;
}
catch (Exception ex)
{
return false;
} }

写到这里,关于对原表的操作,就完成了,逻辑细分下来,实际上没有多少代码量

2. 主管放行界面的查询

前面用户操作多个表之后,将数据写入到对应的Temp表中,来等待主管审批(放行\删除)。  这里查询界面写一下拉列表,选中某表,点击查询,后台返回对应的视图来显示该临时表数据,为了更好的拓展以及后期的维护,这里对应的视图目录,每一个表写一个视图,这样控制器在查询Action方法中,根据前台传的表名,返回对应表所在的View即可。查询操作比较基础,没什么值得写的。寥寥几句,叙述清楚流程即可。

3. 主管放行

主管放行这里逻辑较为复杂,遇到了不少问题,刚开始就写一存储过程,命名为sp_TableName,里面嵌入一事务,将某表的多个语句(增、删、改)寫在一起,執行成功就提交事务,失败就回滚。这样的思路的话,只需要一个表对应一个存储过程,调用时传入存储过程名, 和联合主键值。于是乎,洋洋洒洒的将存储过程写完。极度简化后删除的代码如下

            --刪除
DELETE FROM A
WHERE TableKey IN
(SELECT TableKey FROM tempA WHERE sFlag = 'D' AND TableKey IN(@str)) --清空臨時資源表
DELETE FROM tempZT_SysConfig_Master WHERE TableKey IN (@str);

结果调用存储过程时候总是执行失败,在T-sql中执行存储过程是成功的,但是在MVC中调用存储过程时,老是执行返回影响行数为0,后来查看日志后发现,传入的主键参数在SQL中解析时有问题,这里用的是联合键,格式为 A,B (A、B栏位为主键,TableKey栏位为联合主键,将表主键用逗号隔开后进行拼接),MVC中调用时传入的联合主键为

string keys ="''a1,b1','a2,b2','a3,b3''";

执行到SQL中时,解析的@str 并不是  ''a1,b1','a2,b2','a3,b3'' ,在打开了不少web界面之后,找了一种解决方案 就是用 exec('') 将sql语句包裹起来,如下

            EXEC('
--刪除
DELETE FROM A
WHERE TableKey IN
(SELECT TableKey FROM tempA WHERE sFlag = ''D'' AND TableKey IN('+@str+')) --清空臨時資源表
DELETE FROM tempZT_SysConfig_Master WHERE TableKey IN ('+@str+');
')

总算解决了,后来在测试中又遇到了新问题,主外键约束,由于原表中存在主外键约束,而对应的临时表Temp是没有主外键关系的,如果有的话,主次颠倒不少,逻辑就更加复杂而且无效

从表新增约束

假如某用户登录系统后对主表A增加几笔数据,然后在从表B中添加了几笔对应的数据,当主管放行临时表B的这笔数据时,由于主管还没放行临时表A的这笔数据,所以执行时就会异常。

这里只需要给一提示,请先放行主表中的新增数据即可,查询是否有从表新增约束的核心sql 如下,判断返回值是否大于0 即可。

SELECT COUNT(a.TableKey) FROM(
SELECT distinct b.TableKey FROM tempMainA AS a
INNER JOIN tempMinorA AS b ON a.s1 = b.s1
AND a.s2 = b.s2 ) AS a
WHERE a.TableKey IN(@tablekeys)

主表删除约束

假如某用户登录系统后对主表删除几笔数据,当主管放行临时表此批数据时,调用存储过程执行到对应的删除T-SQl语句时,由于外键约束,从表对应的数据还在,所以就会报异常

这里主需要在存储过程中删除语句的sql上面,写入删除从表的sql即可。不过这里由于表的特殊性,在获取从表联合主键时着实下了不少功夫,删除从表的SQL如下:

--刪除從表
DELETE FROM Minor WHERE TableKey IN(
SELECT a.TableKey FROM Minor AS a WHERE substring(a.TableKey,1,len(a.TableKey) - CHARINDEX('','',reverse(a.TableKey))) IN(
SELECT TableKey FROM tempMain WHERE status = 'D' AND TableKey IN('+@str+'))
)

从表联合主键为 'a1,a2,a3'  , 主表联合主键为 'a1,a2' ,所以先捞取执行删除操作的主表的联合主键 tablekey(第三行),然后通过截取从表的联合主键Tablekey in (第二行)得到从表需要删除的TableKey,最后执行删除从表操作.

4. 主管删除

关于主管删除 ,其实就是清空Temp表的记录。对于放行来说,其逻辑就相当的简单,没有必要去循环删除,直接如上操作,后台接收前台传入主键拼接后的string字符串和表名,然后在逻辑类中写入delete的T-sql即可

DELETE FROM  " + tbname + "  WHERE TableKey IN(" + tablekeys+ ")

执行操作,完成主管删除

总结:写到这里,关于主管放行的简体版本的大致流程已经叙述完了,说白了,放行实际上就是将 临时表的数据剪切至原表,没错,就是剪切,实现剪切操作时,T-SQL的能力要有,自认为我的水平远不如老大,原项目中本来我就写有对数据的删、改、增操作。所以我第一次的思路是主管放行时,一笔一笔的去执行,传入实体,调用原始的删、改、增方法。这样效率低不说,传入实体的逻辑写起来也相当费劲。最后在老大手把手的教授下,通过存储过程可放行临时表中所有勾选的数据,着实提高了效率。

---市人皆大笑,举手揶揄之

05-08 08:41