前言:对于经常忙于服务端开发的小伙伴来说,与DB层打交道是在正常不过的事了,但是每次页面的查询条件新增往往意味着后端代码参数化同比增长,当然你可以不使用sqlhelper自带的参数化条件查询,可以直接传递参数,这样一来,可能你写的代码就变少了,但是存在一个隐藏的问题就是sql注入,对于sql注入我想大家都并不陌生,相关资源和预防措施网上千篇一律,有兴趣可以自己去了解,常用的注入工具是sqlmapper,有兴趣的可以一并去了解。

那么你写代码既要保持代码的优雅,语句条数的少量,有想要保证代码的安全性能,不被轻易注入,有没有一种2种都能兼容的方式呢,在对于一般习惯拘泥于原有代码思维的人来看,可能并没有,但是习惯于从不同角度思考问题的人来说,条条大路通罗马,比如我下面说的这种就是基于.NET 自带的stirngfromatter原理来实现的一种自动化参数化查询~~

由于口头表述不是很好,我直接贴图来说明了,请看

这是原先的参数化查询

 public PayOrderEntity GetPayOrderInfoById(int id)
{
PayOrderEntity parorderModel = new PayOrderEntity();
List<SqlParameter> paramList = new List<SqlParameter>();
string sql = @"select p.Id as pid
,c.Id
,c.UserId
,p.OrderId
,p.TransactionId
,p.PayType
,c.TotalPrice as orderprice
,p.TotalPrice as payprice
,c.[Status] as orderstatus
,p.[Status] as paystatus
,c.CreateTime
,p.PayTime
,c.Remark as orderremark
,p.Remark as payremark
from t_ContentOrder(nolock) c
left join t_Payment(nolock) p
on c.Id=p.OrderId where c.IsDeleted=0 and p.IsDeleted=0 and p.Id= {0}";
paramList.Add(new SqlParameter("@id", id));
try
{
var dt = SqlHelper.ExecuteDataset(write_connstring, CommandType.Text, sql, paramList.ToArray()); //使用重写后的DB驱动查询方法调用 by:xuja 2017-05-27 10:34:19
//var dt = SqlQuery(sql, id); parorderModel = DbTableConvertor<PayOrderEntity>.ConvertToEntity(dt.Tables[]);
}
catch (Exception ex)
{
Core.Log.TraceLogger.Error(ex);
}
return parorderModel;
}

步骤一 先实例化一个sqlparams实体

步骤二 将满足条件的参数传入定义好的参数实体并赋值(可能需要条件验证)

步骤三 将填充完成后的参数对象传入调用的sqlhelper查询接口中

这样四步即可实现整个查询流程

看着流程也并不复杂,但是大家想过一个问题没,如果一个页面有6个以上的查询条件,是不是定义起来会很吃力,这种感觉我相信大家都会有,我也不列外 囧 ,接下来利用formatter原理我们在看看重构后的参数化查询代码

 /// <summary>
/// sql参数自动化拼接方法
/// 创建人:xujiangan
/// 2017-05-27 10:35:40
/// </summary>
/// <param name="sqlCommand">需要查询的sql</param>
/// <param name="param">需要拼接的参数列表</param>
/// <returns></returns>
public Tuple<string, SqlParameter[]> ProcessSqlCommand(string sqlCommand, params object[] param)
{
var tempKVDic = param.Select((item, i) => new KeyValuePair<string, object>("@an" + i, item)).ToDictionary(k => k.Key, v => v.Value); var tempSqlCommand = string.Format(sqlCommand, tempKVDic.Keys.ToArray()); var tempParams = tempKVDic.Select(t => new SqlParameter(t.Key, t.Value)).ToArray(); return Tuple.Create(tempSqlCommand, tempParams);
}

方法的返回值因为不确定到具体类型,所以用了元组来定义了,元组的能力类似于dynamic T类型,这里就不多介绍了,参数的条件只有2个,1.要执行查询的sql语句 2.需要传递的参数化条件

代码逻辑:1.将参数列表利用键值对的方法一一对应组合,参数可以自动增长,组成参数列表字典;2.使用format方法将sql语句和参数字典拼接;3.将拼接好的查询字符串返回给调用方

原先接口并没有这次参数条件的重载 接下来,我们还学要重写一下sqlhelper执行查询的接口,如下:

  /// <summary>
/// 重写ExcuteQuery方法,便于自动话添加参数
/// 创建人:xujiangan
/// 2017-05-27 10:35:07
/// </summary>
/// <param name="sqlCommand">执行sql语句</param>
/// <param name="param">需要新增的查询参数</param>
/// <returns></returns>
public DataSet SqlQuery(string sqlCommand, params object[] param)
{
var dt = new DataSet();
if (param == null || param.Length == )
{
dt = SqlHelper.ExecuteDataset(write_connstring, sqlCommand, CommandType.Text);
} var temp = ProcessSqlCommand(sqlCommand, param); dt = SqlHelper.ExecuteDataset(write_connstring, CommandType.Text, temp.Item1, temp.Item2); //object entity = DbTableConvertor<object>.ConvertToEntity(dt.Tables[0]); return dt;
}

代码大家都能看懂,无非就是有参和无参两种情况做了区分,我就不解释了...

俗话说,欲善其事,必先利其器,有了上面的准备之后,我们就可以调用我们刚才重写的接口啦

调用请看下面:

   public PayOrderEntity GetPayOrderInfoById(int id)
{
PayOrderEntity parorderModel = new PayOrderEntity();
string sql = @"select p.Id as pid
,c.Id
,c.UserId
,p.OrderId
,p.TransactionId
,p.PayType
,c.TotalPrice as orderprice
,p.TotalPrice as payprice
,c.[Status] as orderstatus
,p.[Status] as paystatus
,c.CreateTime
,p.PayTime
,c.Remark as orderremark
,p.Remark as payremark
from t_ContentOrder(nolock) c
left join t_Payment(nolock) p
on c.Id=p.OrderId where c.IsDeleted=0 and p.IsDeleted=0 and p.Id= {0}";
try
{
//使用重写后的DB驱动查询方法调用 by:xuja 2017-05-27 10:34:19
var dt = SqlQuery(sql, id);
parorderModel = DbTableConvertor<PayOrderEntity>.ConvertToEntity(dt.Tables[]);
}
catch (Exception ex)
{
Core.Log.TraceLogger.Error(ex);
}
return parorderModel;
}

是不是发现少了什么?没错,我们此时不再需要定义繁杂的sqlparams对象拉,直接传递参数即可,有多少传多少,如果太多可以定义数组或者参数实体传递哦~


PS:注意sql代码语句中参数位置改为占位符!!!

到此为止,整个自动化参数化查询基本上就结束了,有问题可以给我留言指出,共勉

项目目前已经应用到 admin.kk.net 支付订单详情页面查询,经多番测试,并没有发现什么问题,看来是可以用的。


建议sql代码大小写一致,保持只解析一次。


高山仰止,景行行止


by:Jsonxu

利用formatter原理自动化参数化查询的更多相关文章

  1. 防止sql注入的参数化查询

    参数化查询为什么能够防止SQL注入 http://netsecurity.51cto.com/art/201301/377209.htm OleDbDataAdapter Class http://m ...

  2. Sql Server参数化查询之where in和like实现详解

    where in 的参数化查询实现 首先说一下我们常用的办法,直接拼SQL实现,一般情况下都能满足需要 string userIds = "1,2,3,4"; using (Sql ...

  3. 【转】Sql Server参数化查询之where in和like实现详解

    转载至:http://www.cnblogs.com/lzrabbit/archive/2012/04/22/2465313.html 文章导读 拼SQL实现where in查询 使用CHARINDE ...

  4. SQL Server 2008 R2——VC++ ADO 操作 参数化查询

    ==================================声明================================== 本文原创,转载在正文中显要的注明作者和出处,并保证文章的完 ...

  5. ADO.NET 参数化查询

    参数化查询 使用参数化查询的情景有很多,但最常用的情景是需要用户在查询中进行输入的情况. 有两种方法可供使用.第一,可以讲用户输入嵌入到查询字符串中,例如可能使用.NET Framework中的Str ...

  6. 【转载】Sql Server参数化查询之where in和like实现详解

    文章导读 拼SQL实现where in查询 使用CHARINDEX或like实现where in 参数化 使用exec动态执行SQl实现where in 参数化 为每一个参数生成一个参数实现where ...

  7. Sql Server参数化查询之where in和like实现详解 [转]

    文章导读 拼SQL实现where in查询 使用CHARINDEX或like实现where in 参数化 使用exec动态执行SQl实现where in 参数化 为每一个参数生成一个参数实现where ...

  8. 使用参数化查询防止SQL注入漏洞(转)

    SQL注入的原理 以往在Web应用程序访问数据库时一般是采取拼接字符串的形式,比如登录的时候就是根据用户名和密码去查询: string sql * FROM [User] WHERE UserName ...

  9. python mysql参数化查询防sql注入

    一.写法 cursor.execute('insert into user (name,password) value (?,?)',(name,password)) 或者 cursor.execut ...

随机推荐

  1. Ubuntu 重装 mysql

    我另篇blog有提到修改完my.cnf文件后mysql server重新启动失败,就是说mysql server启动不起来了,于是我就想到重装再试试,没想到就好了. 重装mysql之前需要卸载干净,删 ...

  2. File类遍历目录及文件

    1. 构造函数 File(String args0)//使用一个表示文件或目录的路径的字符串创建一个File对象 File(URL args0)//使用一个URL对象创建File对象 File(Fil ...

  3. 2 安装redis.md

    cnblogs-DOC 1.服务器环境 2.安装Redis3.安装Zookeeper4.安装MPush5.安装Alloc服务6.完整测试7.常见问题 一.Linux系统安装Redis 官网下载Redi ...

  4. IOS语音录取

    在IOS中,在做语音识别中,需要对语音进行抓取. #import "GetAudioViewController.h" #import <AVFoundation/AVFou ...

  5. JS前端数据格式化

    当我们从后台取了数据,但是我们希望在前台统一显示格式时,我们可能需要格式化数据. 今天正好总结一下前端JS格式化数据的几个方法: 1. toFixed() 方法   可把 Number 四舍五入为指定 ...

  6. 函数, lambda表达式

    函数 函数:简单的理解,就是一次执行很多行代码 函数的返回值 函数的参数,和变量没区别 例: def hello(): print "hello world" hello() he ...

  7. C++STL中map容器的说明和使用技巧(杂谈)

    1.map简介 map是一类关联式容器.它的特点是增加和删除节点对迭代器的影响很小,除了那个操作节点,对其他的节点都没有什么影响.对于迭代器来说,可以修改实值,而不能修改key. 2.map的功能 自 ...

  8. centos GUI界面与命令行的切换

    Linux 系统任何时候都运行在一个指定的运行级上,并且不同的运行级的程序和服务都不同,所要完成的工作和所要达到的目的都不同.Centos设置了如下表所示的运行级,并且系统可以在这些运行级别之间进行切 ...

  9. 【算法系列学习】[kuangbin带你飞]专题十二 基础DP1 G - 免费馅饼

    https://vjudge.net/contest/68966#problem/G 正解一: http://www.clanfei.com/2012/04/646.html #include< ...

  10. #include<> 和#include“”的区别

    1.< >引用的是编译器的类库路径里面的头文件2."    "引用的是程序目录的相对路径中的头文件,在程序目录的相对路径中找不到该头文件时会继续在类库路径里搜寻该头文件 ...