在做查询时,经常遇到一类需求。请看下面的SQL语句查询
SELECT * FROM Company WHERE CompanyCode='Kingston' AND Suspended='N' AND DbServer='James\SQLEXPRESS'
这里有三个条件,在界面中,也就是我们需要增加三个控件来供用户输入值,再拼接成SQL发送到服务器。
再来看一下界面中的情况,根据客户名称,下单日期,是否过帐,是否完成4个选项来读取发票信息
点击Refresh按钮之后,产生的SQL条件,可能像下面这样写的一样
SELECT * FROM Invoce WHERE Customer='A' and IssueDate='2013-06-16' and Posted='Y' and Closed='N'
现在遇到的问题时,读取界面中的控件的用户输入值,转化为SQL条件,需要经历一些复杂的重复的操作。
如果用户在这四个控件不输入任何值,那么生成的SQL语句应该直接是
SELECT * FROM Invoce
如果用户只想通过发票日期来过滤,则SQL语句应该是这样的
SELECT * FROM Invoce WHERE IssueDate='2013-06-16'
或者是其它的情况,因为WHERE部分的条件出现的时机不确定,所以我们产生的SQL通常是这样的,在考虑全部情况
SELECT * FROM Invoce WHERE 1=1
AND Customer='A' and IssueDate='2013-06-16' and Posted='Y' and Closed='N'
为了解决这一类问题,下面解释一下,我所遇到到的最好的办法。
设计object control,如下图中所示。添加EntityName和FieldName
在运行时,以下面的代码获取它产生的SQL语句条件
efcCustomerNo.GetPredicateExpression();
这样设计方法,考虑了以下几种情况
1 当用户不在控件中输入任何值时,上面的原代码不返回任何条件。
2 用户想输入一个范围内的值,输入两个值之后,产生 BETWEEN A AND B条件
3 用户想输入一个唯一的过滤条件值,输入一个值A,产生条件 CustomerNo=’A’
依据查询的可能,还有可能是like,模糊查询。请看下面的枚举值
public enum ReportFieldSelectionCondition { [DisplayText("All")] All = 0, [DisplayText("Equal")] Equal = 1, [DisplayText("Exclude")] Exclude = 6, [DisplayText("Include")] Include = 5, [DisplayText("In Range")] InRange = 3, [DisplayText("Like")] Like = 7, [DisplayText("Not Equal")] NotEqual = 2, [DisplayText("Not Like")] NotLike = 8, [DisplayText("Out Range")] OutRange = 4 }
查询条件的几种情况,它都考虑到。根据这些情况,产生不同的SQL条件部分,供程序调用。
控件的基础代码稍微有些复杂,以Between为例子,代码像这样所示
if (!flag) { expression.Add((IPredicate) (field >= valueFrom)); } if (!flag2) { expression.Add((IPredicate) (field <= valueTo)); }
代码的意图明了,第一个控件中有值时,产生<=的表达式,第二个控件中有值时,产生>=的条件。
有三种类型的object control,字符串类型,日期类型和bool类型,上面的图中有全部显示。
字符串类型经常遇到,这篇文章中解释了这个类型的控件,日期类型与字符串相似,产生的条件是日期的比较操作。
bool类型是为了解决一些值,真/假的情况。比如是否过帐,日记帐是否完成。控件有二个基本的属性
[DefaultValue(""), EditorBrowsable(EditorBrowsableState.Always), Browsable(true)] public string EntityName { get { return this._entityName; } set { this._entityName = value; } } [EditorBrowsable(EditorBrowsableState.Always), Browsable(true), DefaultValue("")] public string FieldName { get { return this._fieldName; } set { this._fieldName = value; } }
如果接触过ORM的查询的写法,可能有很多代码像下面这样
if (!AllowViewAllTransaction) args.PredicateBucket.PredicateExpression.Add(AccountsReceivableInvoiceFields.CreatedBy == Shared.CurrentUser.Userid); if (!Shared.CurrentUserSession.AllowAccessAllCustomers) args.PredicateBucket.PredicateExpression.Add(Shared.GetAllowedCustomerNoPredicateExpression(AccountsReceivableInvoiceFields.CustomerNo)); if (_show) { _peCustomerNo = this.efcCustomerNo.GetPredicateExpression(); _pePostedFilter = this.efcPostedFilter.GetPredicateExpression(); _peIssueDate = this.efcIssueDate.GetPredicateExpression(); _peClosedFilter = this.efcClosedFilter.GetPredicateExpression(); FetchAccountsReceivableInvoice(); }
PredicationExpress类型相当于 CustomerNo=’A’ 这样的条件的面向对象的封装,最终产生的SQL条件,与开头的一样,这个过程由ORM框架来负责产生,根据类型及其属性,产生对应的SQL语句。
如果没有应用ORM工具,缺少entityName和fieldName,同样也可以应用这里介绍的技术,直接用SQL表名和字段名即可,再根据各种条件组合判断,产生不同的查询条件。
应用本文中的方法,在做查询类程序时,可以节省大量的重复的代码。