企业应用软件开发具有两个显著的特点:
( 1 )业务复杂;
( 2 )数据量大。
由这两个特点派生出另一特点——数据存储复杂。
在一般的结构或编程习惯下,我们总是尽量让程序的重用性尽可能高,算法的结构尽可能简单明了。基于这两点考虑,在程序中会尽量采用重载和多步处理的方法。这里所说的“多步处理”是指将一个对象,对象集或数据集进行二次处理得到满足需要的格式的结果。合理的多步处理会让程序的可读性明显增强。
首先,我们来看重载。勿庸置疑,重载使得程序本身的复用性大大提高,但不是每个重载都是最佳的设计。比如有这样一个简单问题:
_ 例 1
_
根据给定开始时间( dtBegin )和结束时间( dtEnd )来查询数据,假设查询语句类似“ select A.somefields from A where A.fdBeginDate>=@dtBegin and A.fdEndDate<=@dtEnd ” ;
现在提供给用户的操作界面是两个时间可以不输入,即查询请求提交可能有三种情形:
不妨设用户查询接口为 UserQuery;
UserQuery()
|
// 没有查询条件 ;
---|---
UserQuery(dtBegin,-)
|
// 只有开始时间 ;
UserQuery(-,dtEnd)
|
// 只有结束时间 ;
UserQuery(dtBegin,dtEnd)
|
// 指定每一时间 ;
这时的实现(重载)可这样表示
UserQuery()
|
// 没有查询条件 ;
---|---
UserQuery(DateTime dtInput,bool bIsBegin)
|
//bIsBegin 表示是否是开始时间,否则代表结束时间 ;
UserQuery(DateTime dtBegin,DateTime dtEnd)
|
// 具体指定了每个查询条件 ;
现在我们给其中一个做具体实现,一般是 UserQuery(DateTime dtBegin,DateTime dtEnd) ;而其他两个则变为
public DataSet UserQuery()
{
DateTime dtBegin = new DateTime(1900,1,1,0,0,0);
DateTime dtEnd = new DateTime(9999,12,31,23,59,59);
return UserQuery(dtBegin,dtEnd);
}
public DataSet UserQuery(DateTime dtInput, bool bIsBegin)
{
DateTime dtBegin;
DateTime dtEnd;
if(bIsBegin)
{
dtBegin = dtInput;
dtEnd = new DateTime(9999,12,31,23,59,59);
}
else
{
dtBegin = new DateTime(1900,1,1,0,0,0);
dtEnd = dtInput;
}
return UserQuery(dtBegin,dtEnd);
}
我们这里不讨论将 (1900,1,1,0,0,0) 设为最小时间和 (9999,12,31,23,59,59) 设为最大时间的合理性,只是举例说明很多情况下的条件查询我们会采用类似做法进行重载。
这种做法在结构和复用性都是不错的。但一旦查询的处理过程很复杂 ( 如需要很多表之间的关联和筛选 ) ,则加一个条件的过滤即会耗费一定的时间,也就是说对于用户没有
指定条件的查询,如果硬套上一个边界条件,则会影响处理速度。实际上,我们可以对上面的写法做一些改进,把关于条件的判断加进实现函数里,即在采用下面的实现方法。
public DataSet UserQuery()
{
DateTime dtTemp = new DateTime(1900,1,1,0,0,0);
return UserQuery(dtTemp,dtTemp,false,false);
}
public DataSet UserQuery(DateTime dtInput, bool bIsBegin)
{
if(bIsBegin)
{
return UserQuery(dtInput,dtInput,true,false);
}
else
{
return UserQuery(dtInput,dtInput,false,true);
}
}
public DataSet UserQuery(DateTime dtBegin,DateTime dtEnd)
{
return UserQuery(dtBegin,dtEnd,true,true);
}
private DataSet UserQuery(DateTime dtBegin,DateTime dtEnd, bool bNeedBegin,bool bNeedEnd)
{
string strSQL = "select A.somefields from A where 1=1";
if(bNeedBegin)
{
strSQL += " and A.fdBeginDate>=@dtBegin";
}
if(bNeedEnd)
{
strSQL += " and A.fdEndDate<=@dtEnd";
}
//do some query
....
return a DataSet;
}
再来看第二点——多步处理。程序设计中如果能用一次数据库访问或对象方法调用就得到所需数据是很理想的情况,然而业务不可能总是那样简洁易操作的。在很多业务处理中存在大量的数据比较、数据转换,这些数据处理有时并不复杂但数据量会很大。比如根据一个 ID 到数据库查询一个字符串,这种查询不同于前面所讲的查询,是一种简单但频繁的查询。
来看这样一个例子:
_ 例 2
_
已知数据集 DataSet1
{OBJ1_ID, OBJ1_NAME, OBJ2_ID, OBJ2_NAME}
现在要求根据 DataSet1 得到数据集 DataSet2
{OBJ1_ID, OBJ1_NAME, OBJ2_ID, OBJ2_NAME, OBJ3_NAME, OBJ4_NAME}
假设这里获取 OBJ3_NAME, OBJ4_NAME 的过程很简单,就是根据 OBJ1_ID, OBJ2_ID 去拼一段 SQL 出来,然后连接数据库查询一下即可。按照这个思路,一般会提供两种方法供调用,形如:
QueryCaption(string strID)
|
// 单参数 ;
---|---
QueryCaption(string[] strIDArr)
|
// 数组参数 ;
数组参数可以用 IN 或 OR 将多次查询转成一次查询,这样当数据量大的时候可以减少数据库连接次数。但对于上面的例子,因为有两个字段( OBJ3_NAME, OBJ4_NAME )要取,所以至少还是要调用两次 , 而且还要后续处理。这时可以用 ** Union ** 来实现进一步的优化
假设取 OBJ3_NAME 、 OBJ4_NAME 的 SQL 分别为
SELECT TB_NAMELIB_3.NAME OBJ3_NAME FROM TB_NAMELIB_3 WHERE TB_NAMELIB_3.NAME_ID= OBJ1_ID;
SELECT TB_NAMELIB_4.NAME OBJ4_NAME FROM TB_NAMELIB_4 WHERE TB_NAMELIB_4.NAME_ID= OBJ2_ID;
下面用 ** Union ** 合成取数据过程
DataRowCollection objRows = DataSet1.Tables[0].Rows;
int iCount = objRows.Count;
string[] strSQLArr = new string[iCount];
string strSQL;
string strFields;
string strObjID1;
string strObjID2;
DataSet objDataSet;
for(int i=0;i<iCount;i++)
{
strObjID1 = objRows[i][ “ OBJ1_ID ” ].ToString();
strObjID2 = objRows[i][ “ OBJ2_ID ” ].ToString();
strFields = “’” +strObjID1 +" ’ OBJ1_ID, ";
strFields+= “’” +objRows[i][ “ OBJ1_NAME ” ].ToString()+" ’ OBJ1_NAME, ";
strFields+= “’” +strObjID2 +" ’ OBJ2_ID, ";
strFields+= “’” +objRows[i][ “ OBJ2_NAME ” ].ToString()+" ’ OBJ2_NAME, ";
strSQLArr[i] = " UNION SELECT "+ strFields +" TEMPU. OBJ3_NAME,TEMPJ.OBJ4_NAME, FROM (SELECT 1 PID, TB_NAMELIB_3.NAME OBJ3_NAME FROM TB_NAMELIB_3 WHERE TB_NAMELIB_3.NAME_ID="'" + strObjID1 +""'" ) TEMPU LEFT JOIN (SELECT 1 PID, TB_NAMELIB_4.NAME OBJ4_NAME FROM TB_NAMELIB_4 WHERE TB_NAMELIB_4.NAME_ID= <font face=