CPQuery 解决拼接SQL的新方法(2)
来源:asp之家 发布时间:2012-11-30 20:01:46
标签:拼接SQL,CPQuery
揭秘原因
是不是很神奇:加了一个AsCPQuery()的调用,就将原来的拼接SQL变成了参数化查询?
这其中的原因有以下几点:
1. AsCPQuery()的调用产生了一个新的对象,它的类型不是string,而是CPQuery
2. 在每次执行 + 运算符时,已经不再是二个string对象的相加。
3. CPQuery重载了 + 运算符,会识别拼接过程中的参数值与SQL语句片段。
4. 查询构造完成后,得到的结果不再是一个字符串,而是一个CPQuery对象,它可以生成参数化的SQL语句,它还包含了所有的参数值。
AsCPQuery()是一个扩展方法,代码:
代码如下:
public static CPQuery AsCPQuery(this string s)
{
return new CPQuery(s, false);
}
public static CPQuery AsCPQuery(this string s, bool autoDiscoverParameters)
{
return new CPQuery(s,autoDiscoverParameters);
}
所以在调用后,会得到一个CPQuery对象。
观察前面的示例代码,你会发现AsCPQuery()只需要调用一次。
要得到一个CPQuery对象,也可以调用CPQuery类型的静态方法:
代码如下:
public static CPQuery New()
{
return new CPQuery(null, false);
}
public static CPQuery New(bool autoDiscoverParameters)
{
return new CPQuery(null, autoDiscoverParameters);
}
这二种方法是等效的,示例代码:
代码如下:
// 下面二行代码是等价的,可根据喜好选择。
var query = "select ProductID, ProductName from Products where (1=1) ".AsCPQuery();
//var query = CPQuery.New() + "select ProductID, ProductName from Products where (1=1) ";
继续看拼接的处理:
代码如下:
public static CPQuery operator +(CPQuery query, string s)
{
query.AddSqlText(s);
return query;
}
CPQuery重载了 + 运算符,所以,结果已经不再是二个string对象的相加的结果,而是CPQuery对象本身(JQuery的链接设计思想,便于继续拼接)。
思考一下: " where id = " + "234" + "…………"
你认为我是不是可以判断出 234 就是一个参数值?
类似的还有:" where name = '" + "Fish Li" + "'"
显然,"Fish Li"就是表示一个字符串的参数值嘛,因为拼接的左右二边都有 ' 包围着。
所以,CPQuery对象会识别拼接过程中的参数值与SQL语句片段。
查询拼接完成了,但是此时的SQL语句保存在CPQuery对象中,而且不可能通过一个字符串的方式返回,因为还可能包含多个查询参数呢。所以,在执行查询时,相关的方法需要能够接收CPQuery对象,例如:
代码如下:
static string ExecuteQuery(CPQuery query)
{
StringBuilder sb = new StringBuilder();
using( SqlConnection connection = new SqlConnection(ConnectionString) ) {
SqlCommand command = connection.CreateCommand();
// 将前面的拼接结果绑定到命令对象。
query.BindToCommand(command);
一旦调用了query.BindToCommand(command); CPQuery对象会把它在内部拼接的参数化SQL,以及收集的所有参数值赋值给command对象。后面的事情,该怎么做就怎么做吧,我想大家都会,就不再多说了。
CPQuery源码
前面只贴出了CPQuery的部分代码,这里给出相关的全部代码:
代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Common;
namespace CPQueryDEMO
{
public sealed class CPQuery
{
private enum SPStep // 字符串参数的处理进度
{
NotSet, // 没开始或者已完成一次字符串参数的拼接。
EndWith, // 拼接时遇到一个单引号结束
Skip // 已跳过一次拼接
}
private int _count;
private StringBuilder _sb = new StringBuilder(1024);
private Dictionary<string, QueryParameter> _parameters = new Dictionary<string, QueryParameter>(10);
private bool _autoDiscoverParameters;
private SPStep _step = SPStep.NotSet;
public CPQuery(string text, bool autoDiscoverParameters)
{
_sb.Append(text); _autoDiscoverParameters = autoDiscoverParameters;
}
public static CPQuery New()
{
return new CPQuery(null, false);
}
public static CPQuery New(bool autoDiscoverParameters)
{
return new CPQuery(null, autoDiscoverParameters);
}
public override string ToString()
{
return _sb.ToString();
}
public void BindToCommand(DbCommand command)
{
if( command == null )
throw new ArgumentNullException("command");
command.CommandText = _sb.ToString();
command.Parameters.Clear();
foreach( KeyValuePair<string, QueryParameter> kvp in _parameters ) {
DbParameter p = command.CreateParameter();
p.ParameterName = kvp.Key;
p.Value = kvp.Value.Value;
command.Parameters.Add(p);
}
}
private void AddSqlText(string s)
{
if( string.IsNullOrEmpty(s) )
return;
if( _autoDiscoverParameters ) {
if( _step == SPStep.NotSet ) {
if( s[s.Length - 1] == '\'' ) { // 遇到一个单引号结束
_sb.Append(s.Substring(0, s.Length - 1));
_step = SPStep.EndWith; } else {
object val = TryGetValueFromString(s);
if( val == null )
_sb.Append(s);
else
this.AddParameter(val.AsQueryParameter());
}
}
else if( _step == SPStep.EndWith ) {
// 此时的s应该是字符串参数,不是SQL语句的一部分
// _step 在AddParameter方法中统一修改,防止中途拼接非字符串数据。
this.AddParameter(s.AsQueryParameter());
}
else {
if( s[0] != '\'' )
throw new ArgumentException("正在等待以单引号开始的字符串,但参数不符合预期格式。");
// 找到单引号的闭合输入。
_sb.Append(s.Substring(1));
_step = SPStep.NotSet;
}
}
else {
// 不检查单引号结尾的情况,此时认为一定是SQL语句的一部分。
_sb.Append(s);
}
}
private void AddParameter(QueryParameter p)
{
if( _autoDiscoverParameters && _step == SPStep.Skip )
throw new InvalidOperationException("正在等待以单引号开始的字符串,此时不允许再拼接其它参数。");
string name = "@p" + (_count++).ToString();
_sb.Append(name);
_parameters.Add(name, p);
if( _autoDiscoverParameters && _step == SPStep.EndWith )
_step = SPStep.Skip;
}
private object TryGetValueFromString(string s)
{
// 20,可以是byte, short, int, long, uint, ulong ...
int number1 = 0;
if( int.TryParse(s, out number1) )
return number1;
DateTime dt = DateTime.MinValue;
if( DateTime.TryParse(s, out dt) )
return dt;
// 23.45,可以是float, double, decimal
decimal number5 = 0m;
if( decimal.TryParse(s, out number5) )
return number5;
// 其它类型全部放弃尝试。
return null;
}
public static CPQuery operator +(CPQuery query, string s)
{
query.AddSqlText(s);
return query;
}
public static CPQuery operator +(CPQuery query, QueryParameter p)
{
query.AddParameter(p);
return query;
}
}
public sealed class QueryParameter
{
private object _val;
public QueryParameter(object val)
{
_val = val;
}
public object Value
{
get { return _val; }
}
public static explicit operator QueryParameter(string a)
{
return new QueryParameter(a);
}
public static implicit operator QueryParameter(int a)
{
return new QueryParameter(a);
}
public static implicit operator QueryParameter(decimal a)
{
return new QueryParameter(a);
}
public static implicit operator QueryParameter(DateTime a)
{
return new QueryParameter(a);
}
// 其它需要支持的隐式类型转换操作符重载请自行添加。
}
public static class CPQueryExtensions
{
public static CPQuery AsCPQuery(this string s)
{
return new CPQuery(s, false);
}
public static CPQuery AsCPQuery(this string s, bool autoDiscoverParameters)
{
return new CPQuery(s,autoDiscoverParameters);
}
public static QueryParameter AsQueryParameter(this object b)
{
return new QueryParameter(b);
}
}
}


猜你喜欢
- python实现MD5加密1、简介Message Digest Algorithm MD5(中文名为消息摘要算法第五版)为计算机安全领域广泛
- 创建用户定义函数,它是返回值的已保存的 Transact-SQL 例程。用户定义函数不能用于执行一组修改全局数据库状态的操作。与系统函数一样
- 前序1、蓝图在一个Flask 应用项目中,如果业务视图过多,可否将以某种方式划分出的业务单元单独维护,将每个单元用到的视图、静态文件、模板文
- 背景最近在写一个echarts数据看板,要在一个页面中展示多张图表,所以留给每张图表的尺寸就很小。这也就使得图表x轴的刻度文字全部挤到一起了
- 1 conda介绍conda是一个python的包管理器,用来管理、安装、更新python的包和相关的依赖。另外,conda可以为特定任务创
- 这段时间服务器崩溃2次,一直没有找到原因,今天看到论坛发出的错误信息邮件,想起可能是MySQL的默认连接数引起的问题,一查果然,老天,默认
- 废话不多说了,直接给大家贴代码了,具体代码如下所示:<html> <head> <script> fun
- 从今天开始,我将全面的共享出我所能理解的所有WEB标准方面的知识放在这个“WEB标准能有多难?”的专栏里。当然由于振之的水平有限,所讲并非是
- 对于中小型个人、企业网站来说,MySQL数据库或许是目前数据库的最完美实施解决方案了。在不变更服务器硬件的前提下,一个经过良好架构,优化后的
- 1. 现在的日期时间命令是<%=now%> 即可2.ASP取得表格(from)数据输入的方法,是使用一个内置
- **截止文章发布chinese_calendar版本为1.8.0,大约在每年的11月份更新次年的节假日新版本import datetimef
- 在JavaScript中,数组本质上是一种特殊的对象,它的类型值会返回 object。如果我们需要比较两个数组是否相等,不能像比较基本类型(
- PDOStatement::fetchPDOStatement::fetch — 从结果集中获取下一行(PHP 5 >= 5.1.0,
- 1.文件结构MySQLdb和pymysql的使用差不多阅读的小伙伴可以自己尝试实现2.实验效果3.主文件:main.pyimport MyS
- tempfile 模块专门用于创建临时文件和临时目录,它既可以在 UNIX 平台上运行良好,也可以在 Windows 平台上运行良好。tem
- 本文实例为大家分享了python实现简易动态时钟的具体代码,供大家参考,具体内容如下from turtle import *from dat
- iterator循环器(iterator)是对象的容器,包含有多个对象。通过调用循环器的next()方法 (next()方法,在Python
- 说明本文根据https://github.com/liuchengxu/blockchain-tutorial的内容,用python实现的,
- 映射类型操作符(1)标准类型操作符 字典可以和所有的标准
- 第七步: 在自定义分页的Repeater 里添加排序功能现在已经完成了自定义分页,我们再来添加排序功能。ProductsBLL类的GetPr