WinForm 动态查询
1. 使用场景
在对数据进行筛选, 包含多个筛选字段时适用.
2. 接口设计
/// <summary>
/// 定义可作为追加到 WHERE 子句的控件接口
/// </summary>
internal interface IWhereSentence
{
/// <summary>
/// 当前控件名称
/// </summary>
string Name { get; }
/// <summary>
/// 对应数据表中的字段名称
/// </summary>
string FieldName { get; set; }
/// <summary>
/// 操作符
/// </summary>
string Operator { get; set; }
/// <summary>
/// 是否作为查询条件
/// </summary>
bool AsQuery { get; set; }
}
/// <summary>
/// 定义可追加到 WHERE 子句的控件泛型接口
/// </summary>
/// <typeparam name="T">值的类型</typeparam>
internal interface IWhereSentence<T> : IWhereSentence
{
/// <summary>
/// 实际值
/// </summary>
T Value { get; set; }
}
3. 接口实现, 以常见的 DateTimePicker
和 Textox
控件举例
自定义控件, 继承框架提供的
DateTimePicker
类和IWhereSentence<DateTime>
接口:/// <summary> /// 可追加到查询条件的日期选择器 /// </summary> public partial class QueryDataTimerPicker : DateTimePicker, IWhereSentence<DateTime> { public QueryDataTimerPicker() : base() { } /// <summary> /// 是否作为查询条件 /// </summary> [DisplayName("是否作为查询条件")] public bool AsQuery { get; set; } /// <summary> /// 字段名 /// </summary> [DisplayName("字段名称")] public string FieldName { get; set; } /// <summary> /// 操作符 /// </summary> [DisplayName("操作符")] public string Operator { get; set; } }
自定义控件,继承框架提供的
TextBox
控件和IWhereSentence<string>
接口.AsQuery
属性可根据文本框是否有值, 返回是否作为查询条件,Value
属性与文本框自身的Text
属性绑定/// <summary> /// 可追加到查询条件的文本框 /// </summary> public class QueryTextBox : TextBox, IWhereSentence<string> { private bool _asQuery; public QueryTextBox() : base() { } /// <summary> /// 是否作为查询条件 /// </summary> [DisplayName("是否作为查询条件")] public bool AsQuery { get { return this._asQuery && !string.IsNullOrWhiteSpace(this.Text); } set { this._asQuery = value; } } /// <summary> /// 字段名 /// </summary> [DisplayName("字段名称")] public string FieldName { get; set; } /// <summary> /// 操作符 /// </summary> [DisplayName("操作符")] public string Operator { get; set; } /// <summary> /// 实际值 /// </summary> public string Value { get { return this.Text; } set { this.Text = value; } } }
4. 运用
将自定义控件放置在目标窗体上, 可在属性
窗口设置相关属性:
定义查询控件集合
private IWhereSentence[] _queryControls; this._queryControls = new IWhereSentence[] { this.queryDataTimerPickerStart, this.queryDataTimerPickerEnd, this.queryTextBoxTemp };
获取
WHERE
子句/// <summary> /// 根据查询条件生成WHERE字符串, 用于SQL查询 /// </summary> /// <returns>包含WHERE的SQL字符串</returns> private string GetWhereString() { var builderInst = PooledStringBuilder.GetInstance(); var builder = builderInst.Builder; builder.Append("WHERE "); using (var enumerator = this._queryControls.Where(p => p.AsQuery).GetEnumerator()) { if (!enumerator.MoveNext()) { builder.Clear(); return builderInst.ToStringAndFree(); } AppendWhere(builder, enumerator.Current); while (enumerator.MoveNext()) { builder.Append(" AND "); AppendWhere(builder, enumerator.Current); } } return builderInst.ToStringAndFree(); } /// <summary> /// 将当前查询控件追加到WHERE字符串中 /// </summary> /// <param name="builder"><see cref="StringBuilder"/>对象</param> /// <param name="item">当前查询控件</param> private void AppendWhere(StringBuilder builder, IWhereSentence item) { builder.Append(item.FieldName); if (_formatRegex.IsMatch(item.Operator)) builder.Append(" ").AppendFormat(item.Operator, $"‘ + @{item.Name} + ‘"); else builder.Append(" ").Append(item.Operator).Append(" ").Append("@").Append(item.Name); } /// <summary> /// 获取查询参数实际值 /// </summary> /// <returns>查询参数组成的匿名对象</returns> private object GetQueryParam() { return new { queryDataTimerPickerStart = this.queryDataTimerPickerStart.Value, queryDataTimerPickerEnd = this.queryDataTimerPickerEnd.Value, queryTextBoxTemp = this.queryTextBoxTemp.Value }; }
查询
var sql = "...";
sql = sql + GetWhereString();
var param = GetQueryParam();
var result = conn.Query(sql, param); //使用Dapper
5. 问题
- 当前接口的定义了操作符属性, 在拼接SQL时, 未实现面对复杂运算符的情况, 目前支持
LIKE(‘{0}‘)
运算符. - 多个条件之间默认使用
AND
连接, 若要扩展到使用OR
或者其他运算符, 可在IWhereSentence
接口中添加属性 - 针对SQL Server数据库, 若要针对MySQL或Oracle等其他数据库, 需更改SQL参数标识符
@
.