CPQuery 解决拼接SQL的新方法(3)
来源:asp之家 发布时间:2012-11-30 20:01:46
CPQuery的已知问题以及解决方法
在开始阅读这一节之前,请务必保证已经阅读过前面的源代码,尤其是AddSqlText,TryGetValueFromString这二个方法。在【揭秘原因】这节中,我说过:CPQuery重载了 + 运算符,会识别拼接过程中的参数值与SQL语句片段。 其实这个所谓的识别过程,主要就是在这二个方法中实现的。
尤其是在TryGetValueFromString方法中,我无奈地写出了下面的注释:
代码如下:
// 20,可以是byte, short, int, long, uint, ulong ...
// 23.45,可以是float, double, decimal
// 其它类型全部放弃尝试。
很显然,当把一个数字变成字符串后,很难再知道数字原来的类型是什么。
因此,在这个方法的实现过程中,我只使用了我认为最常见的数据类型。
我不能保证它们永远能够正确运行。
还有,虽然我们可以通过判断二个 ' 来确定中间是一个字符串参数值,然而,对于前面的示例中的参数值来说:"Fish Li" 这个字符串如果是写成这样呢:"Fish" + " " + "Li" ?因为很有可能实际代码是:s1 + " " + s2,换句话说:字符串参数值也是拼接得到的。
对于这二个问题,我只能说:我也没办法了。
这是一个已知道问题,那么有没有解决方法呢?
答案是:有的。思路也简单:既然猜测可能会出错,那么就不要去猜了,你得显式指出参数值。
如何【显式指出参数值】呢?
其实也不难,大致有以下方法:
1. 非字符串参数值不要转成字符串,例如:数字就让它是数字。
2. 字符串参数需要单独标识出来。
具体方法可参考下面的示例代码(与前面的代码是等价的):
代码如下:
static CPQuery BuildDynamicQuery(Product p)
{
// 下面二行代码是等价的,可根据喜好选择。
var query = "select ProductID, ProductName from Products where (1=1) ".AsCPQuery();
//var query = CPQuery.New() + "select ProductID, ProductName from Products where (1=1) ";
// 注意:下面的拼接代码中不能写成: query += .....
if( p.ProductID > 0 )
query = query + " and ProductID = " + p.ProductID; // 整数参数。
if( string.IsNullOrEmpty(p.ProductName) == false )
// 给查询添加一个字符串参数。
query = query + " and ProductName like " + p.ProductName.AsQueryParameter();
if( p.CategoryID > 0 )
query = query + " and CategoryID = " + p.CategoryID; // 整数参数。
if( string.IsNullOrEmpty(p.Unit) == false )
query = query + " and Unit = " + (QueryParameter)p.Unit; // 字符串参数
if( p.UnitPrice > 0 )
query = query + " and UnitPrice >= " + p.UnitPrice; // decimal参数。
if( p.Quantity > 0 )
query = query + " and Quantity >= " + p.Quantity; // 整数参数。
return query;
}
在这段代码中,数字没有转成字符串,它在运行时,其实是执行QueryParameter类型中定义的隐式类型转换,它们会转换成QueryParameter对象,因此,根本就没有机会搞错,而且执行效率更高。字符串参数值需要调用AsQueryParameter()扩展方法或者显式转换成QueryParameter对象,此时也不需要识别,因此也没机会搞错。
我强烈推荐使用这种方法来拼接。
注意:
1. 字符串参数值在拼接时,不需要由二个 ' 包起来。
2. AsCPQuery()或者CPQuery.New()的调用中,不需要参数,或者传入false 。
说明:
1. 在拼接字符串时,C#本身就允许 "abc" + 123 这样的写法,只是说写成"abc" + 123.ToString()会快点。
2. 在使用CPQuery时,所有的参数值都可以显式转换成QueryParameter,例如:“……” + (QueryParameter)p.Quantity
更多CPQuery示例
CPQuery是为了部分解决拼接SQL的缺点而设计的,它做为ClownFish的增强功能已补充到ClownFish中。
在ClownFish的示例中,也专门为CPQuery准备了一个更强大的示例,那个示例演示了在4种数据库中使用CPQuery:
为了方便的使用CPQuery,ClownFish的DbHelper类为所有的数据库访问方法提供了对应的重载方法:
代码如下:
public static int ExecuteNonQuery(CPQuery query)
public static int ExecuteNonQuery(CPQuery query, DbContext dbContext)
public static object ExecuteScalar(CPQuery query)
public static object ExecuteScalar(CPQuery query, DbContext dbContext)
public static T ExecuteScalar<T>(CPQuery query)
public static T ExecuteScalar<T>(CPQuery query, DbContext dbContext)
public static T GetDataItem<T>(CPQuery query)
public static T GetDataItem<T>(CPQuery query, DbContext dbContext)
public static List<T> FillList<T>(CPQuery query)
public static List<T> FillList<T>(CPQuery query, DbContext dbContext)
public static List<T> FillScalarList<T>(CPQuery query)
public static List<T> FillScalarList<T>(CPQuery query, DbContext dbContext)
public static DataTable FillDataTable(CPQuery query)
public static DataTable FillDataTable(CPQuery query, DbContext dbContext)
所以,使用起来也非常容易:
代码如下:
var query = BuildDynamicQuery(p);
DataTable table = DbHelper.FillDataTable(query);
CPQuery的设计目标及使用建议
CPQuery的设计目标是:将传统的拼接SQL代码转成参数化的SQL,而且将使用和学习成本降到最低。
本文开头的示例我想已经证明了CPQuery已经实现了这个目标。
只需要拼接的第一个字符串上调用AsCPQuery()扩展方法,或者在所有字符串前加上CPQuery.New()就能解决。
注意:
1. 提供AsCPQuery(true)或者CPQuery.New(true)方法,仅仅用于处理现有代码,可认为是兼容性解决方案。
2. 我强烈建议调用AsCPQuery()或者CPQuery.New()来处理拼接,原因前面有解释,这里不再重复。
有些人看到了示例代码会认为CPQuery使用起来好复杂。这种说法完全是不动脑子的说法。
你写拼接SQL的代码会短多少?
我前面已经说过了:CPQuery的设计目标不是一个数据访问层,它只是为解决拼接SQL而设计的。
使用起来方不方便,要看具体的数据访问层来与CPQuery的整体与包装方式。
示例代码为了保证所有人能看懂,我直接使用了ADO.NET,而且中间包含了调试代码,所以看起来长了点,但是,关键代码有多少,这个还看不出来吗?
CPQuery类的代码,你看不懂也没用关系,我们只需要调用一次它的扩展方法(或者静态方法)就可以了。
关于易用性,我最后想说的就是:如果想方便,可以试一下 ClownFish,它集成了CPQuery 。
友情提示
本文一开始,我就明确表达了我的观点:CPQuery仅能解决拼接SQL的前二个缺点。
应该仅当需要实现动态查询时才使用CPQuery,因为拼接会涉及多种语句的代码混合在一起,这种做法会给代码的可维护性产生负面影响。
点击此处下载CPQuery源码和示例代码


猜你喜欢
- 目录distinctgroup byrow_number在使用SQL提数的时候,常会遇到表内有重复值的时候,比如我们想得到 uv (独立访客
- 最近在看《Effective Python》,里面提到判断字符串或者集合是否为空的原则,原文如下:Don't check for e
- 一个日期联动选择器javascript源码,年月日联动显示,准确显示日期(包括闰年日期),可自定义日期范围。 【select】 先说清空一个
- <?php 02 if(!function_exists('get_headers')){ 03&
- Python的3.0版本,常被称为Python 3000,或简称Py3k。相对于Python的早期版本,这是一个较大的升级。为了不带入过多的
- 如下所示:#!/usr/bin/env python#-*- coding: utf-8 -*-"""[0,
- 网上一直说的是先安装SQL Server 2014,再安装VS2015,软件就不会出现问题。我这次在什么都没准备的情况下安装了VS2015,
- 一、概述机器学习算法在近几年大数据点燃的热火熏陶下已经变得被人所“熟知”,就算不懂得其中各算法理论,叫你喊上一两个著名算法的名字,你也能昂首
- 写了个JavaScript版的DateAdd、DateDiff、IsDate函数,大家评评!需要说明的是,JavaScript中IsDate
- tbody 标签表格主体(正文)。该标签用于组合 HTML 表格的主体内容。tbody 元素应该与&
- 原理介绍keras是一种基于模块的高级深度学习开发框架,它并没有仅依赖于某一种高速底层张量库,而是对各种底层张量库进行高层模块封装,让底层库
- 目录一、对比数据类型二、可变集合构造方法三、不可变集合的构造方法四、集合构造注意事项 Python集合又是一种新的数据类型,集合有
- 本文实例讲述了Python实现字符串格式化输出的方法。分享给大家供大家参考,具体如下:python属于强类型的语言,如果像java一样操作字
- Security vulnerability in MySQL/MariaDB 在知道用户名的情况下(如root),直接反复重试(平均大约2
- 本文实例为大家分享了python3实现证件照背景替换的具体代码,供大家参考,具体内容如下import cv2import numpy as
- max_connect_errors是一个MySQL中与安全有关的计数器值,它负责阻止过多尝试失败的客户端以防止暴力破解密码的情况。max_
- 本文实例讲述了python抓取并保存html页面时乱码问题的解决方法。分享给大家供大家参考,具体如下:在用Python抓取html页面并保存
- PyTorch中数据读取的一个重要接口是torch.utils.data.DataLoader,该接口定义在dataloader.py脚本中
- 概要:要实现点赞功能,需要实现的有:谁进行的点赞、什么时候进行点赞、点赞的对象是谁、每一个对象的点赞数量是多少、点赞过后还需要能够取消点赞,
- 目前python 提供了几种多线程实现方式 thread,threading,multithreading ,其中thread模块比较底层,