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

 1//代码段1
 2public 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    try
10    {
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    finally
19    {
20            connection.Close();
21    }
22
23    return table;
24}

而此方法都是这样被我调用的:

1//代码段2
2string cmd = "select * from MaterialClass where MaterialClassCode = {0}";
3cmd = string.Format(cmd, scope);
4DataTable table = new BitAECSqlExe().ExecuteQuery(cmd);

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

 1//代码段3
 2private 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
17public 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    try
25    {
26        adapter.Fill(table);
27    }
28    catch (SqlException e)
29    {
30        throw e;
31    }
32    finally
33    {
34        if (connection.State != ConnectionState.Open)
35            connection.Close();
36    }
37
38    return table;
39}

上面重构的是ExecuteQuery方法的定义部分,这其实只是重构的开始,因为现有代码中充满了类似代码段2那样的方法调用,都需要改成如下的形式:

1//代码段4
2string cmd = "select * from MaterialClass where MaterialClassCode = @MaterialClassCode and CompanyName = @CompanyName";
3SqlParameter[] SqlParams = 
4
5    new SqlParameter("@MaterialClassCode",MaterialClassCode ),
6    new SqlParameter("@CompanyName",CompanyName )
7}
8DataTable table = new BitAECSqlExe().ExecuteQuery(cmd);

比照一下上面贴出的代码段2和代码段4,它们之间的差异就是我很希望自己能通过正则表达式跨越的鸿沟(一个一个手工去改的话实在太累了,一百多处啊,呵呵)。需要注意的是,查询语句中可能出现的参数个数是不定的,每个文件中可能包含的方法调用的数量也是不定的。本想写一个C#的console小程序来完成这个批量操作,但又总觉得杀鸡用牛刀,后来写了一个简单的perl脚本,利用perl强大的文本处理能力,很容易就实现了这个功能。脚本代码如下:

 1use FileHandle;
 2use File::Find;
 3use strict;
 4
 5#全局变量
 6my $directory = "E:/GEA52_SVN/GEA52/Web";
 7#my $directory = "E:/Temp/Perl";
 8
 9find(\&editFile, $directory);
10
11#使用这个方法来修改每一个代码文件
12#xingyk 20070702
13sub 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的应用)的更多相关文章

  1. 使用MediatR重构单体应用中的事件发布/订阅

    标题:使用MediatR重构单体应用中的事件发布/订阅 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/10640280.html 源代码:https ...

  2. 【重构】AndroidStudio中代码重构菜单Refactor功能详解

    代码重构几乎是每个程序员在软件开发中必须要不断去做的事情,以此来不断提高代码的质量.Android Stido(以下简称AS)以其强大的功能,成为当下Android开发工程师最受欢迎的开发工具,也是A ...

  3. .NET重构—单元测试重构

    .NET重构—单元测试重构 阅读目录: 1.开篇介绍 2.单元测试.测试用例代码重复问题(大量使用重复的Mock对象及测试数据) 2.1.单元测试的继承体系(利用超类来减少Mock对象的使用) 2.1 ...

  4. 框架重构:测试中的DateTime.Now

    存在的问题 DateTime.Now是C#语言中获取计算机的当前时间的代码: 但是,在对使用了DateTime.Now的方法进行测试时,由于计算机时间的实时性,期望值一直在变化.如:计算年龄. pub ...

  5. 重构网页过程中的小tips

    1.display为inline-block的元素可以使用virtical-align:middle来使得元素垂直居中对齐 2.在一些按钮标签或者mark标签中,如果文本内容确定不会改变长度的话,可以 ...

  6. 关于ruby重构的过程中去除不必要的format

    (文章是从我的个人主页上粘贴过来的,大家也可以访问我的主页 www.iwangzheng.com) #这段话可以由下面的话替代56     respond_to do |format|57       ...

  7. 通过反射获取SSM的controller层的注解以及注解中的value值

    package com.reflection.test; import java.lang.annotation.Annotation; import java.lang.reflect.Invoca ...

  8. C++对象模型6--对象模型对数据访问的影响

    如何访问成员? 前面介绍了C++对象模型,下面介绍C++对象模型的对访问成员的影响.其实清楚了C++对象模型,就清楚了成员访问机制.下面分别针对数据成员和函数成员是如何访问到的,给出一个大致介绍. 对 ...

  9. 阶段3 1.Mybatis_06.使用Mybatis完成DAO层的开发_6 Mybatis中使用Dao实现类的执行过程分析-增删改方法

    从测试类入手,断点调试 找到实现类,进入到insert方法里面 这里是SqlSession的接口里面的方法. 我们需要找SqlSession的实现类. DefaultSqlSession 里面有两个i ...

随机推荐

  1. DPDK如何抓包

    原创翻译,转载请注明出处. DPDK的librte_pdump库,提供了在DPDK框架下抓包的功能.这个库通过完全复制Rx和Tx的mbuf到一个新的内存池,因此它降低应用程序的性能,所以只推荐在调试的 ...

  2. hadoop节点之间通信问题

    前天遇到一个hadoop问题,由于之前都是伪分布的情况,没有真正的涉及到集群的环境,最近按照一些资料自己搭建了一个集群环境,三台虚拟机,一个master,两个slave,利用jps查看节点信息,启动了 ...

  3. windows下 eclipse搭建spark java编译环境

    环境: win10 jdk1.8 之前有在虚拟机或者集群上安装spark安装包的,解压到你想要放spark的本地目录下,比如我的目录就是D:\Hadoop\spark-1.6.0-bin-hadoop ...

  4. LTE/EPC中,MME怎么找到UE的HSS的?

    http://bbs.c114.net/forum.php?mod=viewthread&tid=486247 HSS---归属用户服务器,我的理解:一般来说只有一个,或者是一个分布式数据库. ...

  5. 201621123033 《Java程序设计》第11周学习总结

    1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 1. 源代码阅读:多线程程序BounceThread 1.1 BallR ...

  6. vue2.0中vue-router使用总结

    #在vue-cli所创建的项目中使用 进入到项目的目录后使用  npm install vue-router --save  安装vue-router,同时保存在webpack.Json配置文件中,然 ...

  7. OSCache页面缓存的使用

    完成项目时,为了减少对数据库的频繁操作,引出了缓存,缓存分为以下几种: 1.一级缓存 一级缓存的存储域是session,作用于单个的dao 2.二级缓存 二级缓存的存储域是sessionFactory ...

  8. [LOJ#6437][BZOJ5373]「PKUSC2018」PKUSC

    [LOJ#6437][BZOJ5373]「PKUSC2018」PKUSC 试题描述 九条可怜是一个爱玩游戏的女孩子. 最近她在玩一个无双割草类的游戏,平面上有 \(n\) 个敌人,每一个敌人的坐标为 ...

  9. hdu4418 Time travel 【期望dp + 高斯消元】

    题目链接 BZOJ4418 题解 题意:从一个序列上某一点开始沿一个方向走,走到头返回,每次走的步长各有概率,问走到一点的期望步数,或者无解 我们先将序列倍长形成循环序列,\(n = (N - 1) ...

  10. HDU1859 最小长方形 (水

    最小长方形 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submi ...