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参数标识符
@
.