当前位置: 移动技术网 > IT编程>开发语言>.net > 用扩展方法优化多条件查询(不定条件查询)

用扩展方法优化多条件查询(不定条件查询)

2017年12月12日  | 移动技术网IT编程  | 我要评论
在我们开发过程中,特别是管理系统的开发,经常会遇到多条件查询(或者叫不定条件查询)的案例,就是提供给user输入的查询条件有多个不同的查询栏位,而且,在实际使用中并不能确定

在我们开发过程中,特别是管理系统的开发,经常会遇到多条件查询(或者叫不定条件查询)的案例,就是提供给user输入的查询条件有多个不同的查询栏位,而且,在实际使用中并不能确定user会使用哪些条件来当做搜索条件。

下图就是我们实际项目中一个查询页面的截图,
 
user在实际操作中,有可能会只根据[扣帐编号]查询,那么,只要在[扣帐编号]栏位输入号码,其他栏位留空即可,那么查询语句就只卡[扣帐编号]这条条件也有可能直接根据日前范围查询,只要输入起始日期即可。当然,在实际开发的时候我们是不能预判user的行为的,因此,正常情况下我们都是用sql拼接的方法来应对这个问题:

复制代码 代码如下:

stringbulider sbsql=new stringbulider();
sbsql.append("select * from v_view1 where 1=1 ");
/*"注意,这里为了确保拼接sql语句的语法正确,要加上“1=1”,因为可能后面所有的查询条件都为空,这个语句 要以 "where 1=1" 结尾。 以前也有在园子里看到文章说加上“1=1”对查询效率有一定影响,这个没有深入研究过,对此持保留态度鉴于我们这里只针对一般开发,数据量不是很大,所以对于这个问题暂且不做讨论*/
if(!string.isnullorempty(vargrno))
sbsql.appendformat(" and bolnr = '{0}' ",vargrno);

这样,就在生成sql语句之前对user的输入行为做了判断:对于某个查询条件,如果user有输入,则加入sql的where条件中,有个没有输入,则不予考虑。
对于日期范围的判断,可以这样写:
复制代码 代码如下:

stringbulider sbsql=new stringbulider();
sbsql.append("select * from v_view1 where 1=1 ");
if(!string.isnullorempty(vargrno))
sbsql.appendformat(" and bolnr = '{0}' ",vargrno);

if(!string.isnullorempty(vardtfrom))
{
sbsql.appendformat(" and crdate >= '{0}' ",convert.todatetime(vardtfrom));
if(!string.isnullorempty(vardtto))
{
sbsql.appendformat(" and crdate <= '{0}' ",convert.todatetime(vardtto));
}
}

下面是我们实际开发中的完整代码(省略了一些无关的逻辑):
复制代码 代码如下:

public datatable getgrcollections(string varshipto, string vargrno, string vargrnoto, string varmaterialno, string varpl, string varplto, string varcustomerid, string varcustomerid1, string varcustomerpn, string vardatefrom, string vardateto, string varchecked,string varsupplierpn)
{
try
{
#region code here................

datatable dtresult = new datatable();

stringbuilder sbsql = new stringbuilder();
sbsql.append(" select * ")
.append(" from v_querygr")
.append(" where (grtime>= '" + vardatefrom + " 'and grtime<='" + vardateto + "')");
if (!string.isnullorempty(varshipto))
{
sbsql.append(" and plant='"+varshipto+"'");
}

if (!string.isnullorempty(vargrno))
{
if (!string.isnullorempty(vargrnoto))
sbsql.append(" and (grno>='" + vargrno +"' and grno<='"+vargrnoto+ "')");
else
sbsql.append(" and grno='" + vargrno + "'");
}
if (!string.isnullorempty(varmaterialno))
{
sbsql.append(" and materialno='"+varmaterialno+"'");
}

if (!string.isnullorempty(varpl))
{
if (!string.isnullorempty(varplto))
sbsql.append(" and (packingno>='" + varpl + "' and packingno<='"+varplto+"')");
else
sbsql.append(" and packingno='" + varpl + "'");
}
if (!string.isnullorempty(varcustomerid))
{
sbsql.append(" and customid='" + varcustomerid + "'");
}
if (string.isnullorempty(varcustomerid))
{
clscommon objcommon = new clscommon(userdata);
sbsql.append(" and customid in (" + objcommon.getvendorpermissionstring() + ")");
}
if (!string.isnullorempty(varcustomerid1))
{
sbsql.append(" and customid2='" + varcustomerid1 + "'");
}
if (!string.isnullorempty(varcustomerpn))
{
sbsql.append(" and customerpn='" + varcustomerpn + "'");
}
if (!string.isnullorempty(vardatefrom))
{
if (!string.isnullorempty(vardateto))
sbsql.append(" and (grtime>= '" + vardatefrom + "' and grtime<='" + vardateto + "')");
else
sbsql.append(" and packingno='" + vardatefrom + "'");
}
if (varchecked == "checked")
{
sbsql.append(" and checkprice=1 ");
}
if (varchecked == "unchecked")
{
sbsql.append(" and checkprice=0");
}
if (!string.isnullorempty(varsupplierpn))
{
sbsql.append(" and suplierpn='" + varsupplierpn + "'");
}

try
{
controlhandledb();
dtresult = controlsqlaccess.getdatatable(sbsql.tostring());
}
catch
{
throw;
}
finally
{
controlsqlaccess.closeconnection();
}

return dtresult;

#endregion
}
catch (commonobjectsexception ex)
{

}
catch (exception ex)
{

}
}

这样一来,如果参数多一点的话,一个简单的get方法就要写50行以上的代码,虽然不能以代码的行数来评定开发效率,但这种方法无疑增加了代码量,
也降低的代码的可读性和可维护性。
以前,为了给这种情况找到一种更“优雅”,更简洁的方法,也有在网上找了一些资料,发现其他人的方法也是大同小异,差不多都是这样按条件拼接。
园子里有一位同学(现在忘记是哪位了o(∩_∩)o哈!)提出了一种解决方案就是把判断的逻辑直接写到sql语句或者存储过程中:
复制代码 代码如下:

select * from v_view1 where ((isnull(@vargrno,'')<>'' and bolnr=@vargrno ) or (1=1))

这个方法虽然一定程度上减少了代码量,但是把业务逻辑混杂在sql语句中,个人感觉不是太好的方法,而且大大增加了维护的难度。当然,有兴趣的同学可以
自己去研究。
既然,以上方法都有弊端,那么有没有更好一点的解放方案呢?答案是肯定的,上次用ef的时候突然想到.net中的扩展方法能够对这个问题进行优化。
首先,来看一下什么事扩展方法,一下是来做msdn的解释:
扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种特殊的静态方法,但可以像扩展类型上的实例方法一样进行调用。
我们常用的linq中引用的 using system.linq 其实就是一个扩展方法库,更详细的内容可以参照msdn和c# 扩展方法奇思妙用(鹤冲天)。在这里,我只举一个简单的例子:
比如,正常情况下判断一个字符串是否为空是这样写:
复制代码 代码如下:

string.isnullorempty(str);
如果加上一个我们自己扩展的方法:
/// <summary>
/// 检查字符串是否是空(isnullorempty)
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static bool isnullorempty(this string str)
{
return string.isnullorempty(str);
}

那么以后判断字符串是否为空就可以这样:
复制代码 代码如下:

str.isnullorempty();

是不是简洁、优雅了许多呢?
好,回到正题,看看如何用扩展方法的特性来优化sql语句的拼接问题。既然扩展方法允许我们以实例方法的方式来调用静态方法,那么我们是否可以给sql语句的字符串实例扩展一个方法来对其操作呢?
比如这个sql:
复制代码 代码如下:

stringbulider sbsql=new stringbulider();
sbsql.append("select * from v_view1 where 1=1 ");
if(!string.isnullorempty(vargrno))
sbsql.appendformat(" and bolnr = '{0}' ",vargrno);

实际上就是对一个变量进行判断,然后操作字符串实例。
那么,我们就加行一个这样的扩展:
复制代码 代码如下:

public static string strequals(this string strsql, string strvalue, string colname)
{
if (!string.isnullorempty(strvalue))
return string.format(strsql + " and {0}='{1}' ", colname, strvalue);
else
return strsql;
}

看到没有,在方法内部进行参数的非空判断,那么,上面的代码就可以这样写:
复制代码 代码如下:

string strsql="select * from v_view1 where 1=1"
strsql=strsql.strequals(vargrno,bolnr)

是不是少了很多代码?
如果有更多的参数,我们可以写的想linq一样优雅:
复制代码 代码如下:

string strsql="select * from v_view1 where 1=1"
.strequals(vargrno,bolnr)
.strequals(varplno,vbeln)
.strequals(varpono,ebeln)

对于like语句,进行下面的扩展
复制代码 代码如下:

public static string strlike(this string strsql, string strvalue, string colname)
{
if (!string.isnullorempty(strvalue))
return string.format(strsql + " and {0} like '%{1}%' ", colname, strvalue);
else
return strsql;
}


和范围的扩展:
复制代码 代码如下:

public static string strequalsorbetween(this string strsql, string strstart, string strend, string colname)
{
if (string.isnullorempty(strstart) && string.isnullorempty(strend))
return strsql;
else if (!string.isnullorempty(strstart) && !string.isnullorempty(strend))
{
return strsql.strbigger(strstart, colname).strsmaller(strend, colname);
}
else if (string.isnullorempty(strstart) && !string.isnullorempty(strend))
return strsql.strequals(strend, colname);
else
return strsql.strequals(strstart, colname);
}

这样一来,上面一大段的代码就可以写成这样:
复制代码 代码如下:

public datatable getgrcollections(string varshipto, string vargrno, string vargrnoto, string varmaterialno, string varpl, string varplto, string varcustomerid, string varcustomerid1, string varcustomerpn, string vardatefrom, string vardateto, string varchecked,string varsupplierpn)
{
try
{
#region code here................

datatable dtresult = new datatable();

string strsql="select * from v_querygr where 1=1"
.dtequalsorbetween(vardatefrom,vardateto,grtime)
.strequals(varshipto,plant)
.strequalsorbetween(vargrno,grno)
.strequals(varmaterialno,materialno)
.strequalsorbetween(varpl,packingno)
.strequals(varcustomerid,customid)
.strequals(varcustomerid1,customid2)
.strequals(varcustomerpn,customerpn)
.dtequalsorbetween(vardatefrom,vardateto,grtime)
.strequals(varsupplierpn,suplierpn)
try
{
controlhandledb();
dtresult = controlsqlaccess.getdatatable(sbsql.tostring());
}
catch
{
throw;
}
finally
{
controlsqlaccess.closeconnection();
}

return dtresult;

#endregion
}

catch (commonobjectsexception ex)
{

}
catch (exception ex)
{

}
}


对于其他的一下扩展方法,我写了一个类文件,有兴趣的可以点此下载。
第一次正正经经的写博文,累死我了。由于自己也是个菜鸟,想把一个问题讲清楚让更多的“菜鸟”也能看懂,难免有些啰嗦,有不足的地方还请大家多多指教。

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网