对数据访问层的重构(及重构中Perl的应用)
以前上学的时候,听到“一个学生在毕业后刚刚开始编程的头几年中,写出的代码多半是垃圾”这样的说法,均不屑一顾。现在工作一年多了,越发感觉自己代码中疏漏处甚多,故近来常做亡羊补牢的重构之举。拿自己4个月前写的数据访问层来说,这个层位于整个系统的最底端,根据传入的sql语句进行查询和更新操作。就拿查询来说吧,我当时觉得很简单,写出来的方法是这样的:
//代码段12
public DataTable ExecuteQuery(string cmdText)3
{4
if (cmdText == null)5
return null;6

7
SqlDataAdapter adapter = new SqlDataAdapter(cmdText, connection);8
DataTable table = new DataTable();9
try10
{11
adapter.Fill(table);12
}13
catch (SqlException e)14
{15

16
HttpContext.Current.Response.Redirect(string.format("~/ErrorPage?ErrorString={0}", HttpContext.Current.Server.UrlEncode(e.Message)));17
}18
finally19
{20
connection.Close();21
}22

23
return table;24
}而此方法都是这样被我调用的:
//代码段22
string cmd = "select * from MaterialClass where MaterialClassCode = {0}";3
cmd = string.Format(cmd, scope);4
DataTable table = new BitAECSqlExe().ExecuteQuery(cmd);有心的朋友很容易就能看出,如此的代码是非常脆弱的,根本无法应付查询字符串中出现特殊字符的情况(单引号,百分号等)。程序容错性差不说,还容易遭到sql注入的攻击。另外,对于sql操作可能抛出的异常,这里捕获之后就跳转到出错页的方式也是很糟糕的;因为这段底层代码,除了会被页面调用以外,还会被其他一些不是由客户端请求引发的功能所调用(如部署到服务器上的windows服务,处理复杂任务的专用工作线程等),在这些调用环境里HttpContext.Current是没有意义的。实际上,在这种底层的代码里,只能对异常作必要的处理,至于究竟是跳转到一个友好的错误提示界面,还是写入错误日志,应当交给更高层的代码去决定。
对这部分的重构其实很简单,那就是在进行查询的时候,对于所有参数都以SqlParameter进行封装。重构中新增了方法PrepareCommand,用来得到我们需要的SqlCommand对象。对ExecuteQuery的重构如下:
//代码段32
private SqlCommand PrepareCommand(string cmdText, SqlParameter[] cmdParms)3
{4
if (cmdText == null)5
throw new ArgumentNullException();6

7
SqlCommand cmd = new SqlCommand(cmdText, connection);8
if((cmdParms!=null)&&(cmdParms.Length>0))9
foreach (SqlParameter param in cmdParms)10
{11
cmd.Parameters.Add(param);12
}13

14
return cmd;15
}16

17
public DataTable ExecuteQuery(string cmdText, SqlParameter[] sqlParams)18
{19
if (cmdText == null)20
return null;21

22
SqlDataAdapter adapter = new SqlDataAdapter(this.PrepareCommand(cmdText,sqlParams));23
DataTable table = new DataTable();24
try25
{26
adapter.Fill(table);27
}28
catch (SqlException e)29
{30
throw e;31
}32
finally33
{34
if (connection.State != ConnectionState.Open)35
connection.Close();36
}37

38
return table;39
}上面重构的是ExecuteQuery方法的定义部分,这其实只是重构的开始,因为现有代码中充满了类似代码段2那样的方法调用,都需要改成如下的形式:
//代码段42
string cmd = "select * from MaterialClass where MaterialClassCode = @MaterialClassCode and CompanyName = @CompanyName";3
SqlParameter[] SqlParams = 4
{ 5
new SqlParameter("@MaterialClassCode",MaterialClassCode ),6
new SqlParameter("@CompanyName",CompanyName )7
}8
DataTable table = new BitAECSqlExe().ExecuteQuery(cmd);比照一下上面贴出的代码段2和代码段4,它们之间的差异就是我很希望自己能通过正则表达式跨越的鸿沟(一个一个手工去改的话实在太累了,一百多处啊,呵呵)。需要注意的是,查询语句中可能出现的参数个数是不定的,每个文件中可能包含的方法调用的数量也是不定的。本想写一个C#的console小程序来完成这个批量操作,但又总觉得杀鸡用牛刀,后来写了一个简单的perl脚本,利用perl强大的文本处理能力,很容易就实现了这个功能。脚本代码如下:
use FileHandle;2
use File::Find;3
use strict;4

5
#全局变量6
my $directory = "E:/GEA52_SVN/GEA52/Web";7
#my $directory = "E:/Temp/Perl";8

9
find(\&editFile, $directory);10

11
#使用这个方法来修改每一个代码文件12
#xingyk 2007070213
sub editFile() 14
{15
if ( -f and /.cs?/ ) 16
{17
my $file = $_;18
open FILE, $file;19
my @lines = <FILE>;20
close FILE;21
22
my @maArr;23
for my $line ( @lines ) 24
{25
@maArr = $line =~ /\s+(\w+)\s*={\d}/g;26
27
#先替换原有查询字符串28
$line =~ s/\s+(\w+)\s*={\d}/ $1=\@$1 /g;29
#接下来添加SqlParameter数组的默认构造器30
my $sqlParam = "\n\tSqlParameter[] sqlParams = { ";31
if(@maArr>0)32
{33
for(@maArr)34
{35
$sqlParam = $sqlParam."\t\tnew SqlParameter(\"@".$_."\", ".$_."),\n";36
}37
$line = $line.$sqlParam."\t\t\t};\n";38
}39
40
}41

42
open FILE, ">$file";43
print FILE @lines;44
close FILE;45
}46
}对数据访问层的重构(及重构中Perl的应用)的更多相关文章
- 使用MediatR重构单体应用中的事件发布/订阅
标题:使用MediatR重构单体应用中的事件发布/订阅 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/10640280.html 源代码:https ...
- 【重构】AndroidStudio中代码重构菜单Refactor功能详解
代码重构几乎是每个程序员在软件开发中必须要不断去做的事情,以此来不断提高代码的质量.Android Stido(以下简称AS)以其强大的功能,成为当下Android开发工程师最受欢迎的开发工具,也是A ...
- .NET重构—单元测试重构
.NET重构—单元测试重构 阅读目录: 1.开篇介绍 2.单元测试.测试用例代码重复问题(大量使用重复的Mock对象及测试数据) 2.1.单元测试的继承体系(利用超类来减少Mock对象的使用) 2.1 ...
- 框架重构:测试中的DateTime.Now
存在的问题 DateTime.Now是C#语言中获取计算机的当前时间的代码: 但是,在对使用了DateTime.Now的方法进行测试时,由于计算机时间的实时性,期望值一直在变化.如:计算年龄. pub ...
- 重构网页过程中的小tips
1.display为inline-block的元素可以使用virtical-align:middle来使得元素垂直居中对齐 2.在一些按钮标签或者mark标签中,如果文本内容确定不会改变长度的话,可以 ...
- 关于ruby重构的过程中去除不必要的format
(文章是从我的个人主页上粘贴过来的,大家也可以访问我的主页 www.iwangzheng.com) #这段话可以由下面的话替代56 respond_to do |format|57 ...
- 通过反射获取SSM的controller层的注解以及注解中的value值
package com.reflection.test; import java.lang.annotation.Annotation; import java.lang.reflect.Invoca ...
- C++对象模型6--对象模型对数据访问的影响
如何访问成员? 前面介绍了C++对象模型,下面介绍C++对象模型的对访问成员的影响.其实清楚了C++对象模型,就清楚了成员访问机制.下面分别针对数据成员和函数成员是如何访问到的,给出一个大致介绍. 对 ...
- 阶段3 1.Mybatis_06.使用Mybatis完成DAO层的开发_6 Mybatis中使用Dao实现类的执行过程分析-增删改方法
从测试类入手,断点调试 找到实现类,进入到insert方法里面 这里是SqlSession的接口里面的方法. 我们需要找SqlSession的实现类. DefaultSqlSession 里面有两个i ...
随机推荐
- javascript中的大括号和中括号
文章:javascript中{},[]中括号,大括号的含义和使用
- 查看ClassLoader载入了哪些类?
在执行jar时加上-verbose:class java -verbose:class -Xms1G -Xmx2G -jar xx.jar 必要时还可以使用 >log.txt 将输出输入到文本 ...
- Ext.Net中如何获取组件
我们在编写函数function的时候,常常需要用到页面上的组件.这时候就需要调用组件. 在Ext.net中,调用组件可以用.App.ID.(ID指的是想要调用的组件的ID). 例如: 我写一个函数需要 ...
- DataGridView使用
DataGridView控件概述 DataGridView 控件代码目录(Windows 窗体) 未绑定数据列 定义:可能想要显示并非来自数据源的一列数据,这种列称为未绑定列. 数据格式示例 如何:设 ...
- [剑指Offer] 31.整数中1出现的次数
题目描述 求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1.10.11.12.13因此共出现6次,但是对于后面问题他就没辙了. ...
- Struts1防止表单重复提交
package org.zln.struts.action; import org.apache.struts.action.Action; import org.apache.struts.acti ...
- 在VS2012中设置默认启动
Visual Studio 2012一个解决方案中多个项目,如果想选择哪个项目就设置哪个项目为启动项就好了. 第一种方法,工具===〉〉选项===〉〉〉项目解决方案===〉〉〉对于新的解决方案,使用单 ...
- BZOJ4424/CF19E Fairy(dfs树+树上差分)
即删除一条边使图中不存在奇环.如果本身就是个二分图当然任意一条边都可以,先check一下.否则肯定要删除在所有奇环的交上的边. 考虑怎么找这些边.跑一遍dfs造出dfs树,找出返祖边构成的奇环.可以通 ...
- [hdu 3068] Manacher算法O(n)最长回文子串
一个不错的讲解:https://github.com/julycoding/The-Art-Of-Programming-By-July/blob/master/ebook/zh/01.05.md # ...
- POJ2559 Largest Rectangle in a Histogram (单调栈
Largest Rectangle in a Histogram Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 26012 ...