设计一个系统(基于SQL)的时候最烦的就是对"搜索"功能的设计,比如有一个单据列表.我需要按审核时间查找出特定内容,或者需要根据关键字来查找内容,或者需要根据申请单位来查找内容,或者两个和更多条件的组合.因此该功能本身是一个不稳定的因素.本质上来说就是SQL语句中WHERE后面的内容的实现.在一些系统中,采用暴露WHERE后面语句给操作者这种方式来实现灵活解决这个问题但也引出了另外的问题,比如说封装问题,以及留下恶意攻击的隐患等等.
在基于分层模型中我们更乐意对DAL封装起来,BLL中已经隐藏数据访问的实现.如果需要追加任何一个搜索条件的实现,将面临一系列问题的困扰.
在微软的PETSHOP项目中,采用了方法重载的方式来实现,就是不同的搜索条件由不同的方法去实现,这样可以起到隔离作用,但这种方式不能很好解决灵活性问题.,比如如果要增加一个搜索条件则必须对BLL,DAL均进行修改
经过一段时间的实践,我总结出一种可以解决灵活性和封装性的方案.
例如有以下一个单据系统,由单号,申请时间,审核时间,完成时间,处理结果,备注几个字段构成
rid    applyDate    verifyDate  completDate  endCode  re首先要构建一个类,仅包含数据内容,即单据的属性public class Request
{
    string rid;
    Datetime applyDate;
    Datetime verifyDate;
    Datetime completDate;
    char endCode;
    string re;
    
    public string Rid
    {
        get{return rid;};set{rid=value};
    }
    //其他省略}
然后设计DAL,MSSQL实现,可以根据需要把DAL做成一个接口public SQLRequest
{
    public IList<Request> SearchRequest(List<ICondition> conds)
    //ICondition是单个条件的接口,这个方法通过传入不同的条件接口来构建WHERE 后面的内容
    {
          string sqlstr = "select * from table where 1=1 and ";
          StringBuilder sb = new StringBuilder(100);
          List<SqlParameter>  parms = new List<SqlParmeter>();
          foreach(ICondition cond in conds)
          {
                 //ICondition的设计必须包含条件参数,条件参数的值两个实现,并且重写ToString方法使其自动转换成SQL语句
                 sb.Appent(cond.ToString());
                 parms.Add(cond.parmnme,cond.parmvalue);
          }
          sqlstr += sb.ToString();
          //sqlstr就是组合出来的查询语句,下面的ADO.NET代码省略
    }
}
现在富于变化的条件独立出来,DAL变得稳定,BLL也保持稳定,客户调用时只需要传入相应的ICondition便可,追加支持的搜索条件时只需要增加一个ICondition便可.
下面来设计ICondition接口
该接口必须实现parmnme属性,parmvalue属性,并重写ToString()方法,为了实现优先级,在这里设计了一个Grp属性,同组内部条件为或,组与组之间条件为与interface ICondition
{
    string parmnme{get;}
    object parmvalue{get;}
    char _operator{get;set;}   //操作夫,最好由一个包含支持操作符的枚举来实现,接口的实现最好提供一个默认操作符
    int grpID{get;set;}   //调用者提供组编号,id组同组之间条件为或,不同组之间条件为与,接口的实现提供一个默认分组,
}
//下面是其中一个条件的实现
public class SearchByKey :ICondition
{
    private const string parmnme = "@keyword";
    private object parmvalue;
    public string Parmnme
    {
         get{return parmnme;}
    }
    public string ParmValue
    {
         get{ return parmvalue;}
    }
    //其他实现略
    public SearchByKey(string keyword)
   {
        this.parmvalue = keyword;
   }
    public override string ToString()
    {
         //代码略
    }
    //其他,重写可比较接口ICompareable,代码略,按grpID的值进行比较
}修改 SearchRequest的方法,使其能够根据id组同组之间条件为或,不同组之间条件为与这个规则进行语句组合,因为实现了ICompareable接口,只需要调用SORT方法重新排序,然后循环时与前面一个元素进行比较便可.
整个类的设计思路如上,这种方式协调了封装性与灵活性的问题,闲着无事,将其贴上来与大家一起分享,希望大家能批评指正